diff --git a/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java index 1d2835bc3..b103e92df 100644 --- a/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java +++ b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java @@ -1,586 +1,580 @@ -package com.jme3.app; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.content.pm.ActivityInfo; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.NinePatchDrawable; -import android.opengl.GLSurfaceView; -import android.os.Bundle; -import android.util.Log; -import android.view.*; -import android.view.ViewGroup.LayoutParams; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; -import com.jme3.audio.AudioRenderer; -import com.jme3.input.JoyInput; -import com.jme3.input.TouchInput; -import com.jme3.input.android.AndroidSensorJoyInput; -import com.jme3.input.controls.TouchListener; -import com.jme3.input.controls.TouchTrigger; -import com.jme3.input.event.TouchEvent; -import com.jme3.system.AppSettings; -import com.jme3.system.SystemListener; -import com.jme3.system.android.JmeAndroidSystem; -import com.jme3.system.android.OGLESContext; -import com.jme3.util.AndroidLogHandler; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.Logger; - -/** - * AndroidHarness wraps a jme application object and runs it on - * Android - * - * @author Kirill - * @author larynx - */ -public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener, SystemListener { - - protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName()); - /** - * The application class to start - */ - protected String appClass = "jme3test.android.Test"; - /** - * The jme3 application object - */ - protected Application app = null; - - /** - * Sets the desired RGB size for the surfaceview. 16 = RGB565, 24 = RGB888. - * (default = 24) - */ - protected int eglBitsPerPixel = 24; - - /** - * Sets the desired number of Alpha bits for the surfaceview. This affects - * how the surfaceview is able to display Android views that are located - * under the surfaceview jME uses to render the scenegraph. - * 0 = Opaque surfaceview background (fastest) - * 1->7 = Transparent surfaceview background - * 8 or higher = Translucent surfaceview background - * (default = 0) - */ - protected int eglAlphaBits = 0; - - /** - * The number of depth bits specifies the precision of the depth buffer. - * (default = 16) - */ - protected int eglDepthBits = 16; - - /** - * Sets the number of samples to use for multisampling.
- * Leave 0 (default) to disable multisampling.
- * Set to 2 or 4 to enable multisampling. - */ - protected int eglSamples = 0; - - /** - * Set the number of stencil bits. - * (default = 0) - */ - protected int eglStencilBits = 0; - - /** - * Set the desired frame rate. If frameRate > 0, the application - * will be capped at the desired frame rate. - * (default = -1, no frame rate cap) - */ - protected int frameRate = -1; - - /** - * Sets the type of Audio Renderer to be used. - *

- * Android MediaPlayer / SoundPool can be used on all - * supported Android platform versions (2.2+)
- * OpenAL Soft uses an OpenSL backend and is only supported on Android - * versions 2.3+. - *

- * Only use ANDROID_ static strings found in AppSettings - * - */ - protected String audioRendererType = AppSettings.ANDROID_OPENAL_SOFT; - - /** - * If true Android Sensors are used as simulated Joysticks. Users can use the - * Android sensor feedback through the RawInputListener or by registering - * JoyAxisTriggers. - */ - protected boolean joystickEventsEnabled = false; - /** - * If true KeyEvents are generated from TouchEvents - */ - protected boolean keyEventsEnabled = true; - /** - * If true MouseEvents are generated from TouchEvents - */ - protected boolean mouseEventsEnabled = true; - /** - * Flip X axis - */ - protected boolean mouseEventsInvertX = false; - /** - * Flip Y axis - */ - protected boolean mouseEventsInvertY = false; - /** - * if true finish this activity when the jme app is stopped - */ - protected boolean finishOnAppStop = true; - /** - * set to false if you don't want the harness to handle the exit hook - */ - protected boolean handleExitHook = true; - /** - * Title of the exit dialog, default is "Do you want to exit?" - */ - protected String exitDialogTitle = "Do you want to exit?"; - /** - * Message of the exit dialog, default is "Use your home key to bring this - * app into the background or exit to terminate it." - */ - protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it."; - /** - * Set the screen window mode. If screenFullSize is true, then the - * notification bar and title bar are removed and the screen covers the - * entire display. If screenFullSize is false, then the notification bar - * remains visible if screenShowTitle is true while screenFullScreen is - * false, then the title bar is also displayed under the notification bar. - */ - protected boolean screenFullScreen = true; - /** - * if screenShowTitle is true while screenFullScreen is false, then the - * title bar is also displayed under the notification bar - */ - protected boolean screenShowTitle = true; - /** - * Splash Screen picture Resource ID. If a Splash Screen is desired, set - * splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If - * splashPicID = 0, then no splash screen will be displayed. - */ - protected int splashPicID = 0; - - /** - * No longer used - Use the android:screenOrientation declaration in - * the AndroidManifest.xml file. - */ - @Deprecated - protected int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR; - - protected OGLESContext ctx; - protected GLSurfaceView view = null; - protected boolean isGLThreadPaused = true; - protected ImageView splashImageView = null; - protected FrameLayout frameLayout = null; - final private String ESCAPE_EVENT = "TouchEscape"; - private boolean firstDrawFrame = true; - private boolean inConfigChange = false; - - private class DataObject { - protected Application app = null; - } - - @Override - public Object onRetainNonConfigurationInstance() { - logger.log(Level.FINE, "onRetainNonConfigurationInstance"); - final DataObject data = new DataObject(); - data.app = this.app; - inConfigChange = true; - return data; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - initializeLogHandler(); - - logger.fine("onCreate"); - super.onCreate(savedInstanceState); - - if (screenFullScreen) { - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - } else { - if (!screenShowTitle) { - requestWindowFeature(Window.FEATURE_NO_TITLE); - } - } - - final DataObject data = (DataObject) getLastNonConfigurationInstance(); - if (data != null) { - logger.log(Level.FINE, "Using Retained App"); - this.app = data.app; - } else { - // Discover the screen reolution - //TODO try to find a better way to get a hand on the resolution - WindowManager wind = this.getWindowManager(); - Display disp = wind.getDefaultDisplay(); - Log.d("AndroidHarness", "Resolution from Window, width:" + disp.getWidth() + ", height: " + disp.getHeight()); - - // Create Settings - logger.log(Level.FINE, "Creating settings"); - AppSettings settings = new AppSettings(true); - settings.setEmulateMouse(mouseEventsEnabled); - settings.setEmulateMouseFlipAxis(mouseEventsInvertX, mouseEventsInvertY); - settings.setUseJoysticks(joystickEventsEnabled); - settings.setEmulateKeyboard(keyEventsEnabled); - - settings.setBitsPerPixel(eglBitsPerPixel); - settings.setAlphaBits(eglAlphaBits); - settings.setDepthBits(eglDepthBits); - settings.setSamples(eglSamples); - settings.setStencilBits(eglStencilBits); - - settings.setResolution(disp.getWidth(), disp.getHeight()); - settings.setAudioRenderer(audioRendererType); - - settings.setFrameRate(frameRate); - - // Create application instance - try { - if (app == null) { - @SuppressWarnings("unchecked") - Class clazz = (Class) Class.forName(appClass); - app = clazz.newInstance(); - } - - app.setSettings(settings); - app.start(); - } catch (Exception ex) { - handleError("Class " + appClass + " init failed", ex); - setContentView(new TextView(this)); - } - } - - ctx = (OGLESContext) app.getContext(); - view = ctx.createView(this); - // store the glSurfaceView in JmeAndroidSystem for future use - JmeAndroidSystem.setView(view); - // AndroidHarness wraps the app as a SystemListener. - ctx.setSystemListener(this); - layoutDisplay(); - } - - @Override - protected void onRestart() { - logger.fine("onRestart"); - super.onRestart(); - if (app != null) { - app.restart(); - } - } - - @Override - protected void onStart() { - logger.fine("onStart"); - super.onStart(); - } - - @Override - protected void onResume() { - logger.fine("onResume"); - super.onResume(); - - gainFocus(); - } - - @Override - protected void onPause() { - logger.fine("onPause"); - loseFocus(); - - super.onPause(); - } - - @Override - protected void onStop() { - logger.fine("onStop"); - super.onStop(); - } - - @Override - protected void onDestroy() { - logger.fine("onDestroy"); - final DataObject data = (DataObject) getLastNonConfigurationInstance(); - if (data != null || inConfigChange) { - logger.fine("In Config Change, not stopping app."); - } else { - if (app != null) { - app.stop(!isGLThreadPaused); - } - } - setContentView(new TextView(this)); - ctx = null; - app = null; - view = null; - JmeAndroidSystem.setView(null); - - super.onDestroy(); - } - - public Application getJmeApplication() { - return app; - } - - /** - * Called when an error has occurred. By default, will show an error message - * to the user and print the exception/error to the log. - */ - @Override - public void handleError(final String errorMsg, final Throwable t) { - String stackTrace = ""; - String title = "Error"; - - if (t != null) { - // Convert exception to string - StringWriter sw = new StringWriter(100); - t.printStackTrace(new PrintWriter(sw)); - stackTrace = sw.toString(); - title = t.toString(); - } - - final String finalTitle = title; - final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception") - + "\n" + stackTrace; - - logger.log(Level.SEVERE, finalMsg); - - runOnUiThread(new Runnable() { - @Override - public void run() { - AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon) - .setTitle(finalTitle).setPositiveButton("Kill", AndroidHarness.this).setMessage(finalMsg).create(); - dialog.show(); - } - }); - } - - /** - * Called by the android alert dialog, terminate the activity and OpenGL - * rendering - * - * @param dialog - * @param whichButton - */ - public void onClick(DialogInterface dialog, int whichButton) { - if (whichButton != -2) { - if (app != null) { - app.stop(true); - } - app = null; - this.finish(); - } - } - - /** - * Gets called by the InputManager on all touch/drag/scale events - */ - @Override - public void onTouch(String name, TouchEvent evt, float tpf) { - if (name.equals(ESCAPE_EVENT)) { - switch (evt.getType()) { - case KEY_UP: - runOnUiThread(new Runnable() { - @Override - public void run() { - AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon) - .setTitle(exitDialogTitle).setPositiveButton("Yes", AndroidHarness.this).setNegativeButton("No", AndroidHarness.this).setMessage(exitDialogMessage).create(); - dialog.show(); - } - }); - break; - default: - break; - } - } - } - - public void layoutDisplay() { - logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID); - if (view == null) { - logger.log(Level.FINE, "view is null!"); - } - if (splashPicID != 0) { - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT, - Gravity.CENTER); - - frameLayout = new FrameLayout(this); - splashImageView = new ImageView(this); - - Drawable drawable = this.getResources().getDrawable(splashPicID); - if (drawable instanceof NinePatchDrawable) { - splashImageView.setBackgroundDrawable(drawable); - } else { - splashImageView.setImageResource(splashPicID); - } - - if (view.getParent() != null) { - ((ViewGroup) view.getParent()).removeView(view); - } - frameLayout.addView(view); - - if (splashImageView.getParent() != null) { - ((ViewGroup) splashImageView.getParent()).removeView(splashImageView); - } - frameLayout.addView(splashImageView, lp); - - setContentView(frameLayout); - logger.log(Level.FINE, "Splash Screen Created"); - } else { - logger.log(Level.FINE, "Splash Screen Skipped."); - setContentView(view); - } - } - - public void removeSplashScreen() { - logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID); - if (splashPicID != 0) { - if (frameLayout != null) { - if (splashImageView != null) { - this.runOnUiThread(new Runnable() { - @Override - public void run() { - splashImageView.setVisibility(View.INVISIBLE); - frameLayout.removeView(splashImageView); - } - }); - } else { - logger.log(Level.FINE, "splashImageView is null"); - } - } else { - logger.log(Level.FINE, "frameLayout is null"); - } - } - } - - /** - * Removes the standard Android log handler due to an issue with not logging - * entries lower than INFO level and adds a handler that produces - * JME formatted log messages. - */ - protected void initializeLogHandler() { - Logger log = LogManager.getLogManager().getLogger(""); - for (Handler handler : log.getHandlers()) { - if (log.getLevel() != null && log.getLevel().intValue() <= Level.FINE.intValue()) { - Log.v("AndroidHarness", "Removing Handler class: " + handler.getClass().getName()); - } - log.removeHandler(handler); - } - Handler handler = new AndroidLogHandler(); - log.addHandler(handler); - handler.setLevel(Level.ALL); - } - - public void initialize() { - app.initialize(); - if (handleExitHook) { - // remove existing mapping from SimpleApplication that stops the app - // when the esc key is pressed (esc key = android back key) so that - // AndroidHarness can produce the exit app dialog box. - if (app.getInputManager().hasMapping(SimpleApplication.INPUT_MAPPING_EXIT)) { - app.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT); - } - - app.getInputManager().addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK)); - app.getInputManager().addListener(this, new String[]{ESCAPE_EVENT}); - } - } - - public void reshape(int width, int height) { - app.reshape(width, height); - } - - public void update() { - app.update(); - // call to remove the splash screen, if present. - // call after app.update() to make sure no gap between - // splash screen going away and app display being shown. - if (firstDrawFrame) { - removeSplashScreen(); - firstDrawFrame = false; - } - } - - public void requestClose(boolean esc) { - app.requestClose(esc); - } - - public void destroy() { - if (app != null) { - app.destroy(); - } - if (finishOnAppStop) { - finish(); - } - } - - public void gainFocus() { - logger.fine("gainFocus"); - if (view != null) { - view.onResume(); - } - - if (app != null) { - //resume the audio - AudioRenderer audioRenderer = app.getAudioRenderer(); - if (audioRenderer != null) { - audioRenderer.resumeAll(); - } - //resume the sensors (aka joysticks) - if (app.getContext() != null) { - JoyInput joyInput = app.getContext().getJoyInput(); - if (joyInput != null) { - if (joyInput instanceof AndroidSensorJoyInput) { - AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput; - androidJoyInput.resumeSensors(); - } - } - } - } - - isGLThreadPaused = false; - - if (app != null) { - app.gainFocus(); - } - } - - public void loseFocus() { - logger.fine("loseFocus"); - if (app != null) { - app.loseFocus(); - } - - if (view != null) { - view.onPause(); - } - - if (app != null) { - //pause the audio - AudioRenderer audioRenderer = app.getAudioRenderer(); - if (audioRenderer != null) { - audioRenderer.pauseAll(); - } - //pause the sensors (aka joysticks) - if (app.getContext() != null) { - JoyInput joyInput = app.getContext().getJoyInput(); - if (joyInput != null) { - if (joyInput instanceof AndroidSensorJoyInput) { - AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput; - androidJoyInput.pauseSensors(); - } - } - } - } - isGLThreadPaused = true; - } -} +package com.jme3.app; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.pm.ActivityInfo; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.NinePatchDrawable; +import android.opengl.GLSurfaceView; +import android.os.Bundle; +import android.util.Log; +import android.view.*; +import android.view.ViewGroup.LayoutParams; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; +import com.jme3.audio.AudioRenderer; +import com.jme3.input.JoyInput; +import com.jme3.input.TouchInput; +import com.jme3.input.android.AndroidSensorJoyInput; +import com.jme3.input.controls.TouchListener; +import com.jme3.input.controls.TouchTrigger; +import com.jme3.input.event.TouchEvent; +import com.jme3.system.AppSettings; +import com.jme3.system.SystemListener; +import com.jme3.system.android.JmeAndroidSystem; +import com.jme3.system.android.OGLESContext; +import com.jme3.util.AndroidLogHandler; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +/** + * AndroidHarness wraps a jme application object and runs it on + * Android + * + * @author Kirill + * @author larynx + */ +public class AndroidHarness extends Activity implements TouchListener, DialogInterface.OnClickListener, SystemListener { + + protected final static Logger logger = Logger.getLogger(AndroidHarness.class.getName()); + /** + * The application class to start + */ + protected String appClass = "jme3test.android.Test"; + /** + * The jme3 application object + */ + protected Application app = null; + + /** + * Sets the desired RGB size for the surfaceview. 16 = RGB565, 24 = RGB888. + * (default = 24) + */ + protected int eglBitsPerPixel = 24; + + /** + * Sets the desired number of Alpha bits for the surfaceview. This affects + * how the surfaceview is able to display Android views that are located + * under the surfaceview jME uses to render the scenegraph. + * 0 = Opaque surfaceview background (fastest) + * 1->7 = Transparent surfaceview background + * 8 or higher = Translucent surfaceview background + * (default = 0) + */ + protected int eglAlphaBits = 0; + + /** + * The number of depth bits specifies the precision of the depth buffer. + * (default = 16) + */ + protected int eglDepthBits = 16; + + /** + * Sets the number of samples to use for multisampling.
+ * Leave 0 (default) to disable multisampling.
+ * Set to 2 or 4 to enable multisampling. + */ + protected int eglSamples = 0; + + /** + * Set the number of stencil bits. + * (default = 0) + */ + protected int eglStencilBits = 0; + + /** + * Set the desired frame rate. If frameRate > 0, the application + * will be capped at the desired frame rate. + * (default = -1, no frame rate cap) + */ + protected int frameRate = -1; + + /** + * Sets the type of Audio Renderer to be used. + *

+ * Android MediaPlayer / SoundPool can be used on all + * supported Android platform versions (2.2+)
+ * OpenAL Soft uses an OpenSL backend and is only supported on Android + * versions 2.3+. + *

+ * Only use ANDROID_ static strings found in AppSettings + * + */ + protected String audioRendererType = AppSettings.ANDROID_OPENAL_SOFT; + + /** + * If true Android Sensors are used as simulated Joysticks. Users can use the + * Android sensor feedback through the RawInputListener or by registering + * JoyAxisTriggers. + */ + protected boolean joystickEventsEnabled = false; + /** + * If true KeyEvents are generated from TouchEvents + */ + protected boolean keyEventsEnabled = true; + /** + * If true MouseEvents are generated from TouchEvents + */ + protected boolean mouseEventsEnabled = true; + /** + * Flip X axis + */ + protected boolean mouseEventsInvertX = false; + /** + * Flip Y axis + */ + protected boolean mouseEventsInvertY = false; + /** + * if true finish this activity when the jme app is stopped + */ + protected boolean finishOnAppStop = true; + /** + * set to false if you don't want the harness to handle the exit hook + */ + protected boolean handleExitHook = true; + /** + * Title of the exit dialog, default is "Do you want to exit?" + */ + protected String exitDialogTitle = "Do you want to exit?"; + /** + * Message of the exit dialog, default is "Use your home key to bring this + * app into the background or exit to terminate it." + */ + protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it."; + /** + * Set the screen window mode. If screenFullSize is true, then the + * notification bar and title bar are removed and the screen covers the + * entire display. If screenFullSize is false, then the notification bar + * remains visible if screenShowTitle is true while screenFullScreen is + * false, then the title bar is also displayed under the notification bar. + */ + protected boolean screenFullScreen = true; + /** + * if screenShowTitle is true while screenFullScreen is false, then the + * title bar is also displayed under the notification bar + */ + protected boolean screenShowTitle = true; + /** + * Splash Screen picture Resource ID. If a Splash Screen is desired, set + * splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If + * splashPicID = 0, then no splash screen will be displayed. + */ + protected int splashPicID = 0; + + + protected OGLESContext ctx; + protected GLSurfaceView view = null; + protected boolean isGLThreadPaused = true; + protected ImageView splashImageView = null; + protected FrameLayout frameLayout = null; + final private String ESCAPE_EVENT = "TouchEscape"; + private boolean firstDrawFrame = true; + private boolean inConfigChange = false; + + private class DataObject { + protected Application app = null; + } + + @Override + public Object onRetainNonConfigurationInstance() { + logger.log(Level.FINE, "onRetainNonConfigurationInstance"); + final DataObject data = new DataObject(); + data.app = this.app; + inConfigChange = true; + return data; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + initializeLogHandler(); + + logger.fine("onCreate"); + super.onCreate(savedInstanceState); + + if (screenFullScreen) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } else { + if (!screenShowTitle) { + requestWindowFeature(Window.FEATURE_NO_TITLE); + } + } + + final DataObject data = (DataObject) getLastNonConfigurationInstance(); + if (data != null) { + logger.log(Level.FINE, "Using Retained App"); + this.app = data.app; + } else { + // Discover the screen reolution + //TODO try to find a better way to get a hand on the resolution + WindowManager wind = this.getWindowManager(); + Display disp = wind.getDefaultDisplay(); + Log.d("AndroidHarness", "Resolution from Window, width:" + disp.getWidth() + ", height: " + disp.getHeight()); + + // Create Settings + logger.log(Level.FINE, "Creating settings"); + AppSettings settings = new AppSettings(true); + settings.setEmulateMouse(mouseEventsEnabled); + settings.setEmulateMouseFlipAxis(mouseEventsInvertX, mouseEventsInvertY); + settings.setUseJoysticks(joystickEventsEnabled); + settings.setEmulateKeyboard(keyEventsEnabled); + + settings.setBitsPerPixel(eglBitsPerPixel); + settings.setAlphaBits(eglAlphaBits); + settings.setDepthBits(eglDepthBits); + settings.setSamples(eglSamples); + settings.setStencilBits(eglStencilBits); + + settings.setResolution(disp.getWidth(), disp.getHeight()); + settings.setAudioRenderer(audioRendererType); + + settings.setFrameRate(frameRate); + + // Create application instance + try { + if (app == null) { + @SuppressWarnings("unchecked") + Class clazz = (Class) Class.forName(appClass); + app = clazz.newInstance(); + } + + app.setSettings(settings); + app.start(); + } catch (Exception ex) { + handleError("Class " + appClass + " init failed", ex); + setContentView(new TextView(this)); + } + } + + ctx = (OGLESContext) app.getContext(); + view = ctx.createView(this); + // store the glSurfaceView in JmeAndroidSystem for future use + JmeAndroidSystem.setView(view); + // AndroidHarness wraps the app as a SystemListener. + ctx.setSystemListener(this); + layoutDisplay(); + } + + @Override + protected void onRestart() { + logger.fine("onRestart"); + super.onRestart(); + if (app != null) { + app.restart(); + } + } + + @Override + protected void onStart() { + logger.fine("onStart"); + super.onStart(); + } + + @Override + protected void onResume() { + logger.fine("onResume"); + super.onResume(); + + gainFocus(); + } + + @Override + protected void onPause() { + logger.fine("onPause"); + loseFocus(); + + super.onPause(); + } + + @Override + protected void onStop() { + logger.fine("onStop"); + super.onStop(); + } + + @Override + protected void onDestroy() { + logger.fine("onDestroy"); + final DataObject data = (DataObject) getLastNonConfigurationInstance(); + if (data != null || inConfigChange) { + logger.fine("In Config Change, not stopping app."); + } else { + if (app != null) { + app.stop(!isGLThreadPaused); + } + } + setContentView(new TextView(this)); + ctx = null; + app = null; + view = null; + JmeAndroidSystem.setView(null); + + super.onDestroy(); + } + + public Application getJmeApplication() { + return app; + } + + /** + * Called when an error has occurred. By default, will show an error message + * to the user and print the exception/error to the log. + */ + @Override + public void handleError(final String errorMsg, final Throwable t) { + String stackTrace = ""; + String title = "Error"; + + if (t != null) { + // Convert exception to string + StringWriter sw = new StringWriter(100); + t.printStackTrace(new PrintWriter(sw)); + stackTrace = sw.toString(); + title = t.toString(); + } + + final String finalTitle = title; + final String finalMsg = (errorMsg != null ? errorMsg : "Uncaught Exception") + + "\n" + stackTrace; + + logger.log(Level.SEVERE, finalMsg); + + runOnUiThread(new Runnable() { + @Override + public void run() { + AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon) + .setTitle(finalTitle).setPositiveButton("Kill", AndroidHarness.this).setMessage(finalMsg).create(); + dialog.show(); + } + }); + } + + /** + * Called by the android alert dialog, terminate the activity and OpenGL + * rendering + * + * @param dialog + * @param whichButton + */ + public void onClick(DialogInterface dialog, int whichButton) { + if (whichButton != -2) { + if (app != null) { + app.stop(true); + } + app = null; + this.finish(); + } + } + + /** + * Gets called by the InputManager on all touch/drag/scale events + */ + @Override + public void onTouch(String name, TouchEvent evt, float tpf) { + if (name.equals(ESCAPE_EVENT)) { + switch (evt.getType()) { + case KEY_UP: + runOnUiThread(new Runnable() { + @Override + public void run() { + AlertDialog dialog = new AlertDialog.Builder(AndroidHarness.this) // .setIcon(R.drawable.alert_dialog_icon) + .setTitle(exitDialogTitle).setPositiveButton("Yes", AndroidHarness.this).setNegativeButton("No", AndroidHarness.this).setMessage(exitDialogMessage).create(); + dialog.show(); + } + }); + break; + default: + break; + } + } + } + + public void layoutDisplay() { + logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID); + if (view == null) { + logger.log(Level.FINE, "view is null!"); + } + if (splashPicID != 0) { + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT, + Gravity.CENTER); + + frameLayout = new FrameLayout(this); + splashImageView = new ImageView(this); + + Drawable drawable = this.getResources().getDrawable(splashPicID); + if (drawable instanceof NinePatchDrawable) { + splashImageView.setBackgroundDrawable(drawable); + } else { + splashImageView.setImageResource(splashPicID); + } + + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + frameLayout.addView(view); + + if (splashImageView.getParent() != null) { + ((ViewGroup) splashImageView.getParent()).removeView(splashImageView); + } + frameLayout.addView(splashImageView, lp); + + setContentView(frameLayout); + logger.log(Level.FINE, "Splash Screen Created"); + } else { + logger.log(Level.FINE, "Splash Screen Skipped."); + setContentView(view); + } + } + + public void removeSplashScreen() { + logger.log(Level.FINE, "Splash Screen Picture Resource ID: {0}", splashPicID); + if (splashPicID != 0) { + if (frameLayout != null) { + if (splashImageView != null) { + this.runOnUiThread(new Runnable() { + @Override + public void run() { + splashImageView.setVisibility(View.INVISIBLE); + frameLayout.removeView(splashImageView); + } + }); + } else { + logger.log(Level.FINE, "splashImageView is null"); + } + } else { + logger.log(Level.FINE, "frameLayout is null"); + } + } + } + + /** + * Removes the standard Android log handler due to an issue with not logging + * entries lower than INFO level and adds a handler that produces + * JME formatted log messages. + */ + protected void initializeLogHandler() { + Logger log = LogManager.getLogManager().getLogger(""); + for (Handler handler : log.getHandlers()) { + if (log.getLevel() != null && log.getLevel().intValue() <= Level.FINE.intValue()) { + Log.v("AndroidHarness", "Removing Handler class: " + handler.getClass().getName()); + } + log.removeHandler(handler); + } + Handler handler = new AndroidLogHandler(); + log.addHandler(handler); + handler.setLevel(Level.ALL); + } + + public void initialize() { + app.initialize(); + if (handleExitHook) { + // remove existing mapping from SimpleApplication that stops the app + // when the esc key is pressed (esc key = android back key) so that + // AndroidHarness can produce the exit app dialog box. + if (app.getInputManager().hasMapping(SimpleApplication.INPUT_MAPPING_EXIT)) { + app.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT); + } + + app.getInputManager().addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK)); + app.getInputManager().addListener(this, new String[]{ESCAPE_EVENT}); + } + } + + public void reshape(int width, int height) { + app.reshape(width, height); + } + + public void update() { + app.update(); + // call to remove the splash screen, if present. + // call after app.update() to make sure no gap between + // splash screen going away and app display being shown. + if (firstDrawFrame) { + removeSplashScreen(); + firstDrawFrame = false; + } + } + + public void requestClose(boolean esc) { + app.requestClose(esc); + } + + public void destroy() { + if (app != null) { + app.destroy(); + } + if (finishOnAppStop) { + finish(); + } + } + + public void gainFocus() { + logger.fine("gainFocus"); + if (view != null) { + view.onResume(); + } + + if (app != null) { + //resume the audio + AudioRenderer audioRenderer = app.getAudioRenderer(); + if (audioRenderer != null) { + audioRenderer.resumeAll(); + } + //resume the sensors (aka joysticks) + if (app.getContext() != null) { + JoyInput joyInput = app.getContext().getJoyInput(); + if (joyInput != null) { + if (joyInput instanceof AndroidSensorJoyInput) { + AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput; + androidJoyInput.resumeSensors(); + } + } + } + } + + isGLThreadPaused = false; + + if (app != null) { + app.gainFocus(); + } + } + + public void loseFocus() { + logger.fine("loseFocus"); + if (app != null) { + app.loseFocus(); + } + + if (view != null) { + view.onPause(); + } + + if (app != null) { + //pause the audio + AudioRenderer audioRenderer = app.getAudioRenderer(); + if (audioRenderer != null) { + audioRenderer.pauseAll(); + } + //pause the sensors (aka joysticks) + if (app.getContext() != null) { + JoyInput joyInput = app.getContext().getJoyInput(); + if (joyInput != null) { + if (joyInput instanceof AndroidSensorJoyInput) { + AndroidSensorJoyInput androidJoyInput = (AndroidSensorJoyInput) joyInput; + androidJoyInput.pauseSensors(); + } + } + } + } + isGLThreadPaused = true; + } +} diff --git a/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java b/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java index 1b205d6dd..5673c8609 100644 --- a/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java +++ b/jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java @@ -195,19 +195,6 @@ public class AndroidHarnessFragment extends Fragment implements */ protected String exitDialogMessage = "Use your home key to bring this app into the background or exit to terminate it."; - /** - * Set the screen window mode. If screenFullSize is true, then the - * notification bar and title bar are removed and the screen covers the - * entire display. If screenFullSize is false, then the notification bar - * remains visible if screenShowTitle is true while screenFullScreen is - * false, then the title bar is also displayed under the notification bar. - */ - protected boolean screenFullScreen = true; - /** - * if screenShowTitle is true while screenFullScreen is false, then the - * title bar is also displayed under the notification bar - */ - protected boolean screenShowTitle = true; /** * Splash Screen picture Resource ID. If a Splash Screen is desired, set * splashPicID to the value of the Resource ID (i.e. R.drawable.picname). If diff --git a/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java index c915164b3..a388ad612 100644 --- a/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java +++ b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java @@ -74,7 +74,7 @@ public class VideoRecorderAppState extends AbstractAppState { public Thread newThread(Runnable r) { Thread th = new Thread(r); - th.setName("jME Video Processing Thread"); + th.setName("jME3 Video Processor"); th.setDaemon(true); return th; } diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java index 592baecaf..202907316 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java @@ -1,265 +1,263 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.jme3.input.android; - -import android.opengl.GLSurfaceView; -import android.os.Build; -import android.view.View; -import com.jme3.input.RawInputListener; -import com.jme3.input.TouchInput; -import com.jme3.input.event.InputEvent; -import com.jme3.input.event.KeyInputEvent; -import com.jme3.input.event.MouseButtonEvent; -import com.jme3.input.event.MouseMotionEvent; -import com.jme3.input.event.TouchEvent; -import com.jme3.system.AppSettings; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * AndroidInput is the main class that connects the Android system - * inputs to jME. It serves as the manager that gathers inputs from the various - * Android input methods and provides them to jME's InputManager. - * - * @author iwgeric - */ -public class AndroidInputHandler implements TouchInput { - private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName()); - - // Custom settings - private boolean mouseEventsEnabled = true; - private boolean mouseEventsInvertX = false; - private boolean mouseEventsInvertY = false; - private boolean keyboardEventsEnabled = false; - private boolean joystickEventsEnabled = false; - private boolean dontSendHistory = false; - - - // Internal - private GLSurfaceView view; - private AndroidTouchHandler touchHandler; - private AndroidKeyHandler keyHandler; - private AndroidGestureHandler gestureHandler; - private boolean initialized = false; - private RawInputListener listener = null; - private ConcurrentLinkedQueue inputEventQueue = new ConcurrentLinkedQueue(); - private final static int MAX_TOUCH_EVENTS = 1024; - private final TouchEventPool touchEventPool = new TouchEventPool(MAX_TOUCH_EVENTS); - private float scaleX = 1f; - private float scaleY = 1f; - - - public AndroidInputHandler() { - int buildVersion = Build.VERSION.SDK_INT; - logger.log(Level.INFO, "Android Build Version: {0}", buildVersion); - if (buildVersion >= 14) { - // add support for onHover and GenericMotionEvent (ie. gamepads) - gestureHandler = new AndroidGestureHandler(this); - touchHandler = new AndroidTouchHandler14(this, gestureHandler); - keyHandler = new AndroidKeyHandler(this); - } else if (buildVersion >= 8){ - gestureHandler = new AndroidGestureHandler(this); - touchHandler = new AndroidTouchHandler(this, gestureHandler); - keyHandler = new AndroidKeyHandler(this); - } - } - - public AndroidInputHandler(AndroidTouchHandler touchInput, - AndroidKeyHandler keyInput, AndroidGestureHandler gestureHandler) { - this.touchHandler = touchInput; - this.keyHandler = keyInput; - this.gestureHandler = gestureHandler; - } - - public void setView(View view) { - if (touchHandler != null) { - touchHandler.setView(view); - } - if (keyHandler != null) { - keyHandler.setView(view); - } - if (gestureHandler != null) { - gestureHandler.setView(view); - } - this.view = (GLSurfaceView)view; - } - - public View getView() { - return view; - } - - public float invertX(float origX) { - return getJmeX(view.getWidth()) - origX; - } - - public float invertY(float origY) { - return getJmeY(view.getHeight()) - origY; - } - - public float getJmeX(float origX) { - return origX * scaleX; - } - - public float getJmeY(float origY) { - return origY * scaleY; - } - - public void loadSettings(AppSettings settings) { - keyboardEventsEnabled = settings.isEmulateKeyboard(); - mouseEventsEnabled = settings.isEmulateMouse(); - mouseEventsInvertX = settings.isEmulateMouseFlipX(); - mouseEventsInvertY = settings.isEmulateMouseFlipY(); - joystickEventsEnabled = settings.useJoysticks(); - - // view width and height are 0 until the view is displayed on the screen - if (view.getWidth() != 0 && view.getHeight() != 0) { - scaleX = (float)settings.getWidth() / (float)view.getWidth(); - scaleY = (float)settings.getHeight() / (float)view.getHeight(); - } - logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}", - new Object[]{scaleX, scaleY}); - - } - - // ----------------------------------------- - // JME3 Input interface - @Override - public void initialize() { - touchEventPool.initialize(); - if (touchHandler != null) { - touchHandler.initialize(); - } - if (keyHandler != null) { - keyHandler.initialize(); - } - if (gestureHandler != null) { - gestureHandler.initialize(); - } - - initialized = true; - } - - @Override - public void destroy() { - initialized = false; - - touchEventPool.destroy(); - if (touchHandler != null) { - touchHandler.destroy(); - } - if (keyHandler != null) { - keyHandler.destroy(); - } - if (gestureHandler != null) { - gestureHandler.destroy(); - } - - setView(null); - } - - @Override - public boolean isInitialized() { - return initialized; - } - - @Override - public void setInputListener(RawInputListener listener) { - this.listener = listener; - } - - @Override - public long getInputTimeNanos() { - return System.nanoTime(); - } - - public void update() { - if (listener != null) { - InputEvent inputEvent; - - while ((inputEvent = inputEventQueue.poll()) != null) { - if (inputEvent instanceof TouchEvent) { - listener.onTouchEvent((TouchEvent)inputEvent); - } else if (inputEvent instanceof MouseButtonEvent) { - listener.onMouseButtonEvent((MouseButtonEvent)inputEvent); - } else if (inputEvent instanceof MouseMotionEvent) { - listener.onMouseMotionEvent((MouseMotionEvent)inputEvent); - } else if (inputEvent instanceof KeyInputEvent) { - listener.onKeyEvent((KeyInputEvent)inputEvent); - } - } - } - } - - // ----------------------------------------- - - public TouchEvent getFreeTouchEvent() { - return touchEventPool.getNextFreeEvent(); - } - - public void addEvent(InputEvent event) { - inputEventQueue.add(event); - if (event instanceof TouchEvent) { - touchEventPool.storeEvent((TouchEvent)event); - } - } - - public void setSimulateMouse(boolean simulate) { - this.mouseEventsEnabled = simulate; - } - - public boolean isSimulateMouse() { - return mouseEventsEnabled; - } - - public boolean isMouseEventsInvertX() { - return mouseEventsInvertX; - } - - public boolean isMouseEventsInvertY() { - return mouseEventsInvertY; - } - - public void setSimulateKeyboard(boolean simulate) { - this.keyboardEventsEnabled = simulate; - } - - public boolean isSimulateKeyboard() { - return keyboardEventsEnabled; - } - - public void setOmitHistoricEvents(boolean dontSendHistory) { - this.dontSendHistory = dontSendHistory; - } - -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.input.android; + +import android.opengl.GLSurfaceView; +import android.os.Build; +import android.view.View; +import com.jme3.input.RawInputListener; +import com.jme3.input.TouchInput; +import com.jme3.input.event.InputEvent; +import com.jme3.input.event.KeyInputEvent; +import com.jme3.input.event.MouseButtonEvent; +import com.jme3.input.event.MouseMotionEvent; +import com.jme3.input.event.TouchEvent; +import com.jme3.system.AppSettings; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * AndroidInput is the main class that connects the Android system + * inputs to jME. It serves as the manager that gathers inputs from the various + * Android input methods and provides them to jME's InputManager. + * + * @author iwgeric + */ +public class AndroidInputHandler implements TouchInput { + private static final Logger logger = Logger.getLogger(AndroidInputHandler.class.getName()); + + // Custom settings + private boolean mouseEventsEnabled = true; + private boolean mouseEventsInvertX = false; + private boolean mouseEventsInvertY = false; + private boolean keyboardEventsEnabled = false; + private boolean dontSendHistory = false; + + + // Internal + private GLSurfaceView view; + private AndroidTouchHandler touchHandler; + private AndroidKeyHandler keyHandler; + private AndroidGestureHandler gestureHandler; + private boolean initialized = false; + private RawInputListener listener = null; + private ConcurrentLinkedQueue inputEventQueue = new ConcurrentLinkedQueue(); + private final static int MAX_TOUCH_EVENTS = 1024; + private final TouchEventPool touchEventPool = new TouchEventPool(MAX_TOUCH_EVENTS); + private float scaleX = 1f; + private float scaleY = 1f; + + + public AndroidInputHandler() { + int buildVersion = Build.VERSION.SDK_INT; + logger.log(Level.INFO, "Android Build Version: {0}", buildVersion); + if (buildVersion >= 14) { + // add support for onHover and GenericMotionEvent (ie. gamepads) + gestureHandler = new AndroidGestureHandler(this); + touchHandler = new AndroidTouchHandler14(this, gestureHandler); + keyHandler = new AndroidKeyHandler(this); + } else if (buildVersion >= 8){ + gestureHandler = new AndroidGestureHandler(this); + touchHandler = new AndroidTouchHandler(this, gestureHandler); + keyHandler = new AndroidKeyHandler(this); + } + } + + public AndroidInputHandler(AndroidTouchHandler touchInput, + AndroidKeyHandler keyInput, AndroidGestureHandler gestureHandler) { + this.touchHandler = touchInput; + this.keyHandler = keyInput; + this.gestureHandler = gestureHandler; + } + + public void setView(View view) { + if (touchHandler != null) { + touchHandler.setView(view); + } + if (keyHandler != null) { + keyHandler.setView(view); + } + if (gestureHandler != null) { + gestureHandler.setView(view); + } + this.view = (GLSurfaceView)view; + } + + public View getView() { + return view; + } + + public float invertX(float origX) { + return getJmeX(view.getWidth()) - origX; + } + + public float invertY(float origY) { + return getJmeY(view.getHeight()) - origY; + } + + public float getJmeX(float origX) { + return origX * scaleX; + } + + public float getJmeY(float origY) { + return origY * scaleY; + } + + public void loadSettings(AppSettings settings) { + keyboardEventsEnabled = settings.isEmulateKeyboard(); + mouseEventsEnabled = settings.isEmulateMouse(); + mouseEventsInvertX = settings.isEmulateMouseFlipX(); + mouseEventsInvertY = settings.isEmulateMouseFlipY(); + + // view width and height are 0 until the view is displayed on the screen + if (view.getWidth() != 0 && view.getHeight() != 0) { + scaleX = (float)settings.getWidth() / (float)view.getWidth(); + scaleY = (float)settings.getHeight() / (float)view.getHeight(); + } + logger.log(Level.FINE, "Setting input scaling, scaleX: {0}, scaleY: {1}", + new Object[]{scaleX, scaleY}); + + } + + // ----------------------------------------- + // JME3 Input interface + @Override + public void initialize() { + touchEventPool.initialize(); + if (touchHandler != null) { + touchHandler.initialize(); + } + if (keyHandler != null) { + keyHandler.initialize(); + } + if (gestureHandler != null) { + gestureHandler.initialize(); + } + + initialized = true; + } + + @Override + public void destroy() { + initialized = false; + + touchEventPool.destroy(); + if (touchHandler != null) { + touchHandler.destroy(); + } + if (keyHandler != null) { + keyHandler.destroy(); + } + if (gestureHandler != null) { + gestureHandler.destroy(); + } + + setView(null); + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public void setInputListener(RawInputListener listener) { + this.listener = listener; + } + + @Override + public long getInputTimeNanos() { + return System.nanoTime(); + } + + public void update() { + if (listener != null) { + InputEvent inputEvent; + + while ((inputEvent = inputEventQueue.poll()) != null) { + if (inputEvent instanceof TouchEvent) { + listener.onTouchEvent((TouchEvent)inputEvent); + } else if (inputEvent instanceof MouseButtonEvent) { + listener.onMouseButtonEvent((MouseButtonEvent)inputEvent); + } else if (inputEvent instanceof MouseMotionEvent) { + listener.onMouseMotionEvent((MouseMotionEvent)inputEvent); + } else if (inputEvent instanceof KeyInputEvent) { + listener.onKeyEvent((KeyInputEvent)inputEvent); + } + } + } + } + + // ----------------------------------------- + + public TouchEvent getFreeTouchEvent() { + return touchEventPool.getNextFreeEvent(); + } + + public void addEvent(InputEvent event) { + inputEventQueue.add(event); + if (event instanceof TouchEvent) { + touchEventPool.storeEvent((TouchEvent)event); + } + } + + public void setSimulateMouse(boolean simulate) { + this.mouseEventsEnabled = simulate; + } + + public boolean isSimulateMouse() { + return mouseEventsEnabled; + } + + public boolean isMouseEventsInvertX() { + return mouseEventsInvertX; + } + + public boolean isMouseEventsInvertY() { + return mouseEventsInvertY; + } + + public void setSimulateKeyboard(boolean simulate) { + this.keyboardEventsEnabled = simulate; + } + + public boolean isSimulateKeyboard() { + return keyboardEventsEnabled; + } + + public void setOmitHistoricEvents(boolean dontSendHistory) { + this.dontSendHistory = dontSendHistory; + } + +} diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInputHandler.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInputHandler.java new file mode 100644 index 000000000..2c3a1d74e --- /dev/null +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInputHandler.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2009-2015 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.input.android; + +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.os.Build; +import android.os.Vibrator; +import android.view.View; +import com.jme3.input.InputManager; +import com.jme3.input.JoyInput; +import com.jme3.input.Joystick; +import com.jme3.input.RawInputListener; +import com.jme3.input.event.InputEvent; +import com.jme3.input.event.JoyAxisEvent; +import com.jme3.input.event.JoyButtonEvent; +import com.jme3.system.AppSettings; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Main class that manages various joystick devices. Joysticks can be many forms + * including a simulated joystick to communicate the device orientation as well + * as physical gamepads.
+ * This class manages all the joysticks and feeds the inputs from each back + * to jME's InputManager. + * + * This handler also supports the joystick.rumble(rumbleAmount) method. In this + * case, when joystick.rumble(rumbleAmount) is called, the Android device will vibrate + * if the device has a built in vibrate motor. + * + * Because Andorid does not allow for the user to define the intensity of the + * vibration, the rumble amount (ie strength) is converted into vibration pulses + * The stronger the strength amount, the shorter the delay between pulses. If + * amount is 1, then the vibration stays on the whole time. If amount is 0.5, + * the vibration will a pulse of equal parts vibration and delay. + * To turn off vibration, set rumble amount to 0. + * + * MainActivity needs the following line to enable Joysticks on Android platforms + * joystickEventsEnabled = true; + * This is done to allow for battery conservation when sensor data or gamepads + * are not required by the application. + * + * To use the joystick rumble feature, the following line needs to be + * added to the Android Manifest File + * + * + * @author iwgeric + */ +public class AndroidJoyInputHandler implements JoyInput { + private static final Logger logger = Logger.getLogger(AndroidJoyInputHandler.class.getName()); + + private List joystickList = new ArrayList(); +// private boolean dontSendHistory = false; + + + // Internal + private GLSurfaceView view; + private boolean initialized = false; + private RawInputListener listener = null; + private ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue(); + private AndroidSensorJoyInput sensorJoyInput; + private Vibrator vibrator = null; + private boolean vibratorActive = false; + private long maxRumbleTime = 250; // 250ms + + public AndroidJoyInputHandler() { + int buildVersion = Build.VERSION.SDK_INT; + 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); + } + + public void setView(GLSurfaceView view) { +// if (touchHandler != null) { +// touchHandler.setView(view); +// } + if (sensorJoyInput != null) { + sensorJoyInput.setView(view); + } + this.view = (GLSurfaceView)view; + + } + + public View getView() { + return view; + } + + public void loadSettings(AppSettings settings) { +// sensorEventsEnabled = settings.useSensors(); + } + + public void addEvent(InputEvent event) { + eventQueue.add(event); + } + + /** + * Pauses the joystick device listeners to save battery life if they are not needed. + * Used to pause when the activity pauses + */ + public void pauseJoysticks() { + if (sensorJoyInput != null) { + sensorJoyInput.pauseSensors(); + } + if (vibrator != null && vibratorActive) { + vibrator.cancel(); + } + + } + + /** + * Resumes the joystick device listeners. + * Used to resume when the activity comes to the top of the stack + */ + public void resumeJoysticks() { + if (sensorJoyInput != null) { + sensorJoyInput.resumeSensors(); + } + + } + + + + + + @Override + public void initialize() { +// 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; + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public void destroy() { + initialized = false; + + if (sensorJoyInput != null) { + sensorJoyInput.destroy(); + } + + setView(null); + } + + @Override + public void setInputListener(RawInputListener listener) { + this.listener = listener; + } + + @Override + public long getInputTimeNanos() { + return System.nanoTime(); + } + + @Override + public void setJoyRumble(int joyId, float amount) { + // convert amount to pulses since Android doesn't allow intensity + if (vibrator != null) { + final long rumbleOnDur = (long)(amount * maxRumbleTime); // ms to pulse vibration on + final long rumbleOffDur = maxRumbleTime - rumbleOnDur; // ms to delay between pulses + final long[] rumblePattern = { + 0, // start immediately + rumbleOnDur, // time to leave vibration on + rumbleOffDur // time to delay between vibrations + }; + final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from + + logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}", + new Object[]{amount, rumbleOnDur, rumbleOffDur}); + + if (rumbleOnDur > 0) { + vibrator.vibrate(rumblePattern, rumbleRepeatFrom); + vibratorActive = true; + } else { + vibrator.cancel(); + vibratorActive = false; + } + } + } + + @Override + public Joystick[] loadJoysticks(InputManager inputManager) { + joystickList.add(sensorJoyInput.loadJoystick(joystickList.size(), inputManager)); + + + return joystickList.toArray( new Joystick[joystickList.size()] ); + } + + @Override + public void update() { + if (sensorJoyInput != null) { + sensorJoyInput.update(); + } + + if (listener != null) { + InputEvent inputEvent; + + while ((inputEvent = eventQueue.poll()) != null) { + if (inputEvent instanceof JoyAxisEvent) { + listener.onJoyAxisEvent((JoyAxisEvent)inputEvent); + } else if (inputEvent instanceof JoyButtonEvent) { + listener.onJoyButtonEvent((JoyButtonEvent)inputEvent); + } + } + } + + } + + + +} diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java index 034b068d8..823aa3d9d 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java @@ -37,7 +37,7 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.os.Vibrator; +import android.opengl.GLSurfaceView; import android.view.Surface; import android.view.WindowManager; import com.jme3.input.AbstractJoystick; @@ -47,10 +47,8 @@ import com.jme3.input.JoyInput; import com.jme3.input.Joystick; import com.jme3.input.JoystickAxis; import com.jme3.input.SensorJoystickAxis; -import com.jme3.input.RawInputListener; import com.jme3.input.event.JoyAxisEvent; import com.jme3.math.FastMath; -import com.jme3.system.android.JmeAndroidSystem; import com.jme3.util.IntMap; import com.jme3.util.IntMap.Entry; import java.util.ArrayList; @@ -63,7 +61,7 @@ import java.util.logging.Logger; * A single joystick is configured and includes data for all configured sensors * as seperate axes of the joystick. * - * Each axis is named accounting to the static strings in SensorJoystickAxis. + * Each axis is named according to the static strings in SensorJoystickAxis. * Refer to the strings defined in SensorJoystickAxis for a list of supported * sensors and their axis data. Each sensor type defined in SensorJoystickAxis * will be attempted to be configured. If the device does not support a particular @@ -72,46 +70,21 @@ import java.util.logging.Logger; * The joystick.getXAxis and getYAxis methods of the joystick are configured to * return the device orientation values in the device's X and Y directions. * - * This joystick also supports the joystick.rumble(rumbleAmount) method. In this - * case, when joystick.rumble(rumbleAmount) is called, the Android device will vibrate - * if the device has a built in vibrate motor. - * - * Because Andorid does not allow for the user to define the intensity of the - * vibration, the rumble amount (ie strength) is converted into vibration pulses - * The stronger the strength amount, the shorter the delay between pulses. If - * amount is 1, then the vibration stays on the whole time. If amount is 0.5, - * the vibration will a pulse of equal parts vibration and delay. - * To turn off vibration, set rumble amount to 0. - * - * MainActivity needs the following line to enable Joysticks on Android platforms - * joystickEventsEnabled = true; - * This is done to allow for battery conservation when sensor data is not required - * by the application. - * - * To use the joystick rumble feature, the following line needs to be - * added to the Android Manifest File - * - * * @author iwgeric */ -public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { +public class AndroidSensorJoyInput implements SensorEventListener { private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName()); - private Context context = null; - private InputManager inputManager = null; + private AndroidJoyInputHandler joyHandler; private SensorManager sensorManager = null; private WindowManager windowManager = null; - private Vibrator vibrator = null; - private boolean vibratorActive = false; - private long maxRumbleTime = 250; // 250ms - private RawInputListener listener = null; private IntMap sensors = new IntMap(); - private AndroidJoystick[] joysticks; private int lastRotation = 0; - private boolean initialized = false; private boolean loaded = false; - private final ArrayList eventQueue = new ArrayList(); + public AndroidSensorJoyInput(AndroidJoyInputHandler joyHandler) { + this.joyHandler = joyHandler; + } /** * Internal class to enclose data for each sensor. @@ -120,7 +93,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { int androidSensorType = -1; int androidSensorSpeed = SensorManager.SENSOR_DELAY_GAME; Sensor sensor = null; - int sensorAccuracy = 0; + int sensorAccuracy = -1; float[] lastValues; final Object valuesLock = new Object(); ArrayList axes = new ArrayList(); @@ -134,16 +107,19 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { } - private void initSensorManager() { - this.context = JmeAndroidSystem.getView().getContext(); - // Get instance of the WindowManager from the current Context - windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - // Get instance of the SensorManager from the current Context - sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - // Get instance of Vibrator from current Context - vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - if (vibrator == null) { - logger.log(Level.FINE, "Vibrator Service not found."); + public void setView(GLSurfaceView view) { + pauseSensors(); + if (sensorManager != null) { + sensorManager.unregisterListener(this); + } + if (view == null) { + windowManager = null; + sensorManager = null; + } else { + // Get instance of the WindowManager from the current Context + windowManager = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE); + // Get instance of the SensorManager from the current Context + sensorManager = (SensorManager) view.getContext().getSystemService(Context.SENSOR_SERVICE); } } @@ -222,9 +198,6 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { unRegisterListener(entry.getKey()); } } - if (vibrator != null && vibratorActive) { - vibrator.cancel(); - } } /** @@ -400,10 +373,8 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { if (!sensorData.haveData) { sensorData.haveData = true; } else { - synchronized (eventQueue){ - if (axis.isChanged()) { - eventQueue.add(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); - } + if (axis.isChanged()) { + joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); } } } @@ -428,47 +399,14 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { // Start of JoyInput methods - public void setJoyRumble(int joyId, float amount) { - // convert amount to pulses since Android doesn't allow intensity - if (vibrator != null) { - final long rumbleOnDur = (long)(amount * maxRumbleTime); // ms to pulse vibration on - final long rumbleOffDur = maxRumbleTime - rumbleOnDur; // ms to delay between pulses - final long[] rumblePattern = { - 0, // start immediately - rumbleOnDur, // time to leave vibration on - rumbleOffDur // time to delay between vibrations - }; - final int rumbleRepeatFrom = 0; // index into rumble pattern to repeat from - - logger.log(Level.FINE, "Rumble amount: {0}, rumbleOnDur: {1}, rumbleOffDur: {2}", - new Object[]{amount, rumbleOnDur, rumbleOffDur}); - - if (rumbleOnDur > 0) { - vibrator.vibrate(rumblePattern, rumbleRepeatFrom); - vibratorActive = true; - } else { - vibrator.cancel(); - vibratorActive = false; - } - } - - } - - public Joystick[] loadJoysticks(InputManager inputManager) { - this.inputManager = inputManager; - - initSensorManager(); - + public Joystick loadJoystick(int joyId, InputManager inputManager) { SensorData sensorData; - List list = new ArrayList(); - AndroidJoystick joystick; AndroidJoystickAxis axis; - joystick = new AndroidJoystick(inputManager, - this, - list.size(), + AndroidJoystick joystick = new AndroidJoystick(inputManager, + joyHandler, + joyId, "AndroidSensorsJoystick"); - list.add(joystick); List availSensors = sensorManager.getSensorList(Sensor.TYPE_ALL); for (Sensor sensor: availSensors) { @@ -555,14 +493,8 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { // } - joysticks = list.toArray( new AndroidJoystick[list.size()] ); loaded = true; - return joysticks; - } - - public void initialize() { - initialized = true; - loaded = false; + return joystick; } public void update() { @@ -570,15 +502,6 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { return; } updateOrientation(); - synchronized (eventQueue){ - // flush events to listener - if (listener != null && eventQueue.size() > 0) { - for (int i = 0; i < eventQueue.size(); i++){ - listener.onJoyAxisEvent(eventQueue.get(i)); - } - eventQueue.clear(); - } - } } public void destroy() { @@ -588,39 +511,27 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { sensorManager.unregisterListener(this); } sensors.clear(); - eventQueue.clear(); - initialized = false; loaded = false; - joysticks = null; sensorManager = null; - vibrator = null; - context = null; - } - - public boolean isInitialized() { - return initialized; - } - - public void setInputListener(RawInputListener listener) { - this.listener = listener; - } - - public long getInputTimeNanos() { - return System.nanoTime(); } - // End of JoyInput methods - // Start of Android SensorEventListener methods + @Override public void onSensorChanged(SensorEvent se) { - if (!initialized || !loaded) { + if (!loaded) { return; } + logger.log(Level.FINE, "onSensorChanged for {0}: accuracy: {1}, values: {2}", + new Object[]{se.sensor.getName(), se.accuracy, se.values}); int sensorType = se.sensor.getType(); SensorData sensorData = sensors.get(sensorType); + if (sensorData != null) { + logger.log(Level.FINE, "sensorData name: {0}, enabled: {1}, unreliable: {2}", + new Object[]{sensorData.sensor.getName(), sensorData.enabled, sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE}); + } if (sensorData != null && sensorData.sensor.equals(se.sensor) && sensorData.enabled) { if (sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) { @@ -641,10 +552,11 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { if (!sensorData.haveData) { sensorData.haveData = true; } else { - synchronized (eventQueue){ - if (axis.isChanged()) { - eventQueue.add(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); - } + if (axis.isChanged()) { + JoyAxisEvent event = new JoyAxisEvent(axis, axis.getJoystickAxisValue()); + logger.log(Level.INFO, "adding JoyAxisEvent: {0}", event); + joyHandler.addEvent(event); +// joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); } } } @@ -658,6 +570,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { } } + @Override public void onAccuracyChanged(Sensor sensor, int i) { int sensorType = sensor.getType(); SensorData sensorData = sensors.get(sensorType); @@ -697,7 +610,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { AndroidJoystickAxis axis; axis = new AndroidJoystickAxis( - inputManager, // InputManager (InputManager) + getInputManager(), // InputManager (InputManager) this, // parent Joystick (Joystick) axisNum, // Axis Index (int) axisName, // Axis Name (String) @@ -758,10 +671,12 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { this.maxRawValue = maxRawValue; } + @Override public float getMaxRawValue() { return maxRawValue; } + @Override public void setMaxRawValue(float maxRawValue) { this.maxRawValue = maxRawValue; } @@ -787,6 +702,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { return hasChanged; } + @Override public void calibrateCenter() { zeroRawValue = lastRawValue; logger.log(Level.FINE, "Calibrating axis {0} to {1}", diff --git a/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java b/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java index cef3e9f66..462715f37 100644 --- a/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java +++ b/jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java @@ -1850,21 +1850,6 @@ public class OGLESShaderRenderer implements Renderer { TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y); } - public void clearTextureUnits() { - IDList textureList = context.textureIndexList; - Image[] textures = context.boundTextures; - for (int i = 0; i < textureList.oldLen; i++) { - int idx = textureList.oldList[i]; -// if (context.boundTextureUnit != idx){ -// glActiveTexture(GL_TEXTURE0 + idx); -// context.boundTextureUnit = idx; -// } -// glDisable(convertTextureType(textures[idx].getType())); - textures[idx] = null; - } - context.textureIndexList.copyNewToOld(); - } - public void deleteImage(Image image) { int texId = image.getId(); if (texId != -1) { @@ -2339,7 +2324,6 @@ public class OGLESShaderRenderer implements Renderer { RendererUtil.checkGLError(); } clearVertexAttribs(); - clearTextureUnits(); } private void renderMeshDefault(Mesh mesh, int lod, int count) { @@ -2378,7 +2362,6 @@ public class OGLESShaderRenderer implements Renderer { RendererUtil.checkGLError(); } clearVertexAttribs(); - clearTextureUnits(); } public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { diff --git a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java index cd849b1dd..5ef866188 100644 --- a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java +++ b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java @@ -46,17 +46,15 @@ import android.view.ViewGroup.LayoutParams; import android.widget.EditText; import android.widget.FrameLayout; import com.jme3.input.*; -import com.jme3.input.android.AndroidSensorJoyInput; import com.jme3.input.android.AndroidInputHandler; +import com.jme3.input.android.AndroidJoyInputHandler; import com.jme3.input.controls.SoftTextDialogInputListener; import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyMouseInput; import com.jme3.renderer.android.AndroidGL; import com.jme3.renderer.opengl.GL; -import com.jme3.renderer.opengl.GLDebugES; import com.jme3.renderer.opengl.GLExt; import com.jme3.renderer.opengl.GLRenderer; -import com.jme3.renderer.opengl.GLTracer; import com.jme3.system.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; @@ -77,9 +75,9 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex protected SystemListener listener; protected boolean autoFlush = true; protected AndroidInputHandler androidInput; + protected AndroidJoyInputHandler androidJoyInput = null; protected long minFrameDuration = 0; // No FPS cap protected long lastUpdateTime = 0; - protected JoyInput androidSensorJoyInput = null; public OGLESContext() { } @@ -119,6 +117,12 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex androidInput.setView(view); androidInput.loadSettings(settings); + if (androidJoyInput == null) { + androidJoyInput = new AndroidJoyInputHandler(); + } + androidJoyInput.setView(view); + androidJoyInput.loadSettings(settings); + // setEGLContextClientVersion must be set before calling setRenderer // this means it cannot be set in AndroidConfigChooser (too late) view.setEGLContextClientVersion(2); @@ -231,6 +235,9 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex if (androidInput != null) { androidInput.loadSettings(settings); } + if (androidJoyInput != null) { + androidJoyInput.loadSettings(settings); + } if (settings.getFrameRate() > 0) { minFrameDuration = (long)(1000d / (double)settings.getFrameRate()); // ms @@ -267,10 +274,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex @Override public JoyInput getJoyInput() { - if (androidSensorJoyInput == null) { - androidSensorJoyInput = new AndroidSensorJoyInput(); - } - return androidSensorJoyInput; + return androidJoyInput; } @Override diff --git a/jme3-android/src/main/java/jme3test/android/DemoAndroidHarness.java b/jme3-android/src/main/java/jme3test/android/DemoAndroidHarness.java index 42c621a4f..61d0d6868 100644 --- a/jme3-android/src/main/java/jme3test/android/DemoAndroidHarness.java +++ b/jme3-android/src/main/java/jme3test/android/DemoAndroidHarness.java @@ -7,21 +7,19 @@ import com.jme3.app.AndroidHarness; public class DemoAndroidHarness extends AndroidHarness { @Override - public void onCreate(Bundle savedInstanceState) - { + public void onCreate(Bundle savedInstanceState) + { // Set the application class to run // First Extract the bundle from intent Bundle bundle = getIntent().getExtras(); //Next extract the values using the key as - appClass = bundle.getString("APPCLASSNAME"); - + appClass = bundle.getString("APPCLASSNAME"); + exitDialogTitle = "Close Demo?"; exitDialogMessage = "Press Yes"; - screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); } } diff --git a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java index c215d0677..c412c07b3 100644 --- a/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java +++ b/jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java @@ -32,36 +32,19 @@ package com.jme3.asset; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; -import com.jme3.animation.Animation; -import com.jme3.bounding.BoundingVolume; -import com.jme3.collision.Collidable; -import com.jme3.collision.CollisionResults; -import com.jme3.collision.UnsupportedCollisionException; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.material.Material; import com.jme3.material.RenderState.FaceCullMode; -import com.jme3.math.ColorRGBA; -import com.jme3.post.Filter; -import com.jme3.scene.CameraNode; -import com.jme3.scene.LightNode; -import com.jme3.scene.Node; -import com.jme3.scene.SceneGraphVisitor; -import com.jme3.scene.Spatial; -import com.jme3.texture.Texture; /** * Blender key. Contains path of the blender file and its loading properties. * @author Marcin Roguski (Kaelthas) */ public class BlenderKey extends ModelKey { - protected static final int DEFAULT_FPS = 25; /** * FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time @@ -72,7 +55,7 @@ public class BlenderKey extends ModelKey { * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded. */ protected int featuresToLoad = FeaturesToLoad.ALL; - /** This variable determines if assets that are not linked to the objects should be loaded. */ + /** The variable that tells if content of the file (along with data unlinked to any feature on the scene) should be stored as 'user data' in the result spatial. */ protected boolean loadUnlinkedAssets; /** The root path for all the assets. */ protected String assetRootPath; @@ -268,6 +251,7 @@ public class BlenderKey extends ModelKey { * @param featuresToLoad * bitwise flag of FeaturesToLoad interface values */ + @Deprecated public void includeInLoading(int featuresToLoad) { this.featuresToLoad |= featuresToLoad; } @@ -277,10 +261,12 @@ public class BlenderKey extends ModelKey { * @param featuresNotToLoad * bitwise flag of FeaturesToLoad interface values */ + @Deprecated public void excludeFromLoading(int featuresNotToLoad) { featuresToLoad &= ~featuresNotToLoad; } + @Deprecated public boolean shouldLoad(int featureToLoad) { return (featuresToLoad & featureToLoad) != 0; } @@ -290,6 +276,7 @@ public class BlenderKey extends ModelKey { * the blender file loader. * @return features that will be loaded by the blender file loader */ + @Deprecated public int getFeaturesToLoad() { return featuresToLoad; } @@ -317,15 +304,6 @@ public class BlenderKey extends ModelKey { this.loadUnlinkedAssets = loadUnlinkedAssets; } - /** - * This method creates an object where loading results will be stores. Only those features will be allowed to store - * that were specified by features-to-load flag. - * @return an object to store loading results - */ - public LoadingResults prepareLoadingResults() { - return new LoadingResults(featuresToLoad); - } - /** * This method sets the fix up axis state. If set to true then Y is up axis. Otherwise the up i Z axis. By default Y * is up axis. @@ -699,8 +677,11 @@ public class BlenderKey extends ModelKey { /** * This interface describes the features of the scene that are to be loaded. + * @deprecated this interface is deprecated and is not used anymore; to ensure the loading models consistency + * everything must be loaded because in blender one feature might depend on another * @author Marcin Roguski (Kaelthas) */ + @Deprecated public static interface FeaturesToLoad { int SCENES = 0x0000FFFF; @@ -745,281 +726,4 @@ public class BlenderKey extends ModelKey { */ ALL_NAMES_MATCH; } - - /** - * This class holds the loading results according to the given loading flag. - * @author Marcin Roguski (Kaelthas) - */ - public static class LoadingResults extends Spatial { - - /** Bitwise mask of features that are to be loaded. */ - private final int featuresToLoad; - /** The scenes from the file. */ - private List scenes; - /** Objects from all scenes. */ - private List objects; - /** Materials from all objects. */ - private List materials; - /** Textures from all objects. */ - private List textures; - /** Animations of all objects. */ - private List animations; - /** All cameras from the file. */ - private List cameras; - /** All lights from the file. */ - private List lights; - /** Loaded sky. */ - private Spatial sky; - /** Scene filters (ie. FOG). */ - private List filters; - /** - * The background color of the render loaded from the horizon color of the world. If no world is used than the gray color - * is set to default (as in blender editor. - */ - private ColorRGBA backgroundColor = ColorRGBA.Gray; - - /** - * Private constructor prevents users to create an instance of this class from outside the - * @param featuresToLoad - * bitwise mask of features that are to be loaded - * @see FeaturesToLoad FeaturesToLoad - */ - private LoadingResults(int featuresToLoad) { - this.featuresToLoad = featuresToLoad; - if ((featuresToLoad & FeaturesToLoad.SCENES) != 0) { - scenes = new ArrayList(); - } - if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) { - objects = new ArrayList(); - if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) { - materials = new ArrayList(); - if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) { - textures = new ArrayList(); - } - } - if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) { - animations = new ArrayList(); - } - } - if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) { - cameras = new ArrayList(); - } - if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) { - lights = new ArrayList(); - } - } - - /** - * This method returns a bitwise flag describing what features of the blend file will be included in the result. - * @return bitwise mask of features that are to be loaded - * @see FeaturesToLoad FeaturesToLoad - */ - public int getLoadedFeatures() { - return featuresToLoad; - } - - /** - * This method adds a scene to the result set. - * @param scene - * scene to be added to the result set - */ - public void addScene(Node scene) { - if (scenes != null) { - scenes.add(scene); - } - } - - /** - * This method adds an object to the result set. - * @param object - * object to be added to the result set - */ - public void addObject(Node object) { - if (objects != null) { - objects.add(object); - } - } - - /** - * This method adds a material to the result set. - * @param material - * material to be added to the result set - */ - public void addMaterial(Material material) { - if (materials != null) { - materials.add(material); - } - } - - /** - * This method adds a texture to the result set. - * @param texture - * texture to be added to the result set - */ - public void addTexture(Texture texture) { - if (textures != null) { - textures.add(texture); - } - } - - /** - * This method adds a camera to the result set. - * @param camera - * camera to be added to the result set - */ - public void addCamera(CameraNode camera) { - if (cameras != null) { - cameras.add(camera); - } - } - - /** - * This method adds a light to the result set. - * @param light - * light to be added to the result set - */ - public void addLight(LightNode light) { - if (lights != null) { - lights.add(light); - } - } - - /** - * This method sets the sky of the scene. Only one sky can be set. - * @param sky - * the sky to be set - */ - public void setSky(Spatial sky) { - this.sky = sky; - } - - /** - * This method adds a scene filter. Filters are used to load FOG or other - * scene effects that blender can define. - * @param filter - * the filter to be added - */ - public void addFilter(Filter filter) { - if (filter != null) { - if (filters == null) { - filters = new ArrayList(5); - } - filters.add(filter); - } - } - - /** - * @param backgroundColor - * the background color - */ - public void setBackgroundColor(ColorRGBA backgroundColor) { - this.backgroundColor = backgroundColor; - } - - /** - * @return all loaded scenes - */ - public List getScenes() { - return scenes; - } - - /** - * @return all loaded objects - */ - public List getObjects() { - return objects; - } - - /** - * @return all loaded materials - */ - public List getMaterials() { - return materials; - } - - /** - * @return all loaded textures - */ - public List getTextures() { - return textures; - } - - /** - * @return all loaded animations - */ - public List getAnimations() { - return animations; - } - - /** - * @return all loaded cameras - */ - public List getCameras() { - return cameras; - } - - /** - * @return all loaded lights - */ - public List getLights() { - return lights; - } - - /** - * @return the scene's sky - */ - public Spatial getSky() { - return sky; - } - - /** - * @return scene filters - */ - public List getFilters() { - return filters; - } - - /** - * @return the background color - */ - public ColorRGBA getBackgroundColor() { - return backgroundColor; - } - - @Override - public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException { - return 0; - } - - @Override - public void updateModelBound() { - } - - @Override - public void setModelBound(BoundingVolume modelBound) { - } - - @Override - public int getVertexCount() { - return 0; - } - - @Override - public int getTriangleCount() { - return 0; - } - - @Override - public Spatial deepClone() { - return null; - } - - @Override - public void depthFirstTraversal(SceneGraphVisitor visitor) { - } - - @Override - protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue) { - } - } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java index eff993f76..e11101c14 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java @@ -31,17 +31,34 @@ */ package com.jme3.scene.plugins.blender; +import java.io.File; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; +import com.jme3.animation.Animation; +import com.jme3.asset.AssetNotFoundException; +import com.jme3.asset.BlenderKey; import com.jme3.export.Savable; +import com.jme3.light.Light; import com.jme3.math.FastMath; import com.jme3.math.Quaternion; +import com.jme3.post.Filter; +import com.jme3.renderer.Camera; +import com.jme3.scene.Node; import com.jme3.scene.Spatial; +import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Structure; +import com.jme3.scene.plugins.blender.materials.MaterialContext; +import com.jme3.scene.plugins.blender.meshes.TemporalMesh; import com.jme3.scene.plugins.blender.objects.Properties; +import com.jme3.texture.Texture; /** * A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can @@ -49,14 +66,16 @@ import com.jme3.scene.plugins.blender.objects.Properties; * @author Marcin Roguski */ public abstract class AbstractBlenderHelper { + private static final Logger LOGGER = Logger.getLogger(AbstractBlenderHelper.class.getName()); + /** The blender context. */ - protected BlenderContext blenderContext; + protected BlenderContext blenderContext; /** The version of the blend file. */ - protected final int blenderVersion; + protected final int blenderVersion; /** This variable indicates if the Y asxis is the UP axis or not. */ - protected boolean fixUpAxis; + protected boolean fixUpAxis; /** Quaternion used to rotate data when Y is up axis. */ - protected Quaternion upAxisRotationQuaternion; + protected Quaternion upAxisRotationQuaternion; /** * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender @@ -129,4 +148,115 @@ public abstract class AbstractBlenderHelper { } } } + + /** + * The method loads library of a given ID from linked blender file. + * @param id + * the ID of the linked feature (it contains its name and blender path) + * @return loaded feature or null if none was found + * @throws BlenderFileException + * and exception is throw when problems with reading a blend file occur + */ + @SuppressWarnings("unchecked") + protected Object loadLibrary(Structure id) throws BlenderFileException { + Pointer pLib = (Pointer) id.getFieldValue("lib"); + if (pLib.isNotNull()) { + String fullName = id.getFieldValue("name").toString();// we need full name with the prefix + String nameOfFeatureToLoad = id.getName(); + Structure library = pLib.fetchData().get(0); + String path = library.getFieldValue("filepath").toString(); + + if (!blenderContext.getLinkedFeatures().keySet().contains(path)) { + File file = new File(path); + List pathsToCheck = new ArrayList(); + String currentPath = file.getName(); + do { + pathsToCheck.add(currentPath); + file = file.getParentFile(); + if (file != null) { + currentPath = file.getName() + '/' + currentPath; + } + } while (file != null); + + Spatial loadedAsset = null; + BlenderKey blenderKey = null; + for (String p : pathsToCheck) { + blenderKey = new BlenderKey(p); + blenderKey.setLoadUnlinkedAssets(true); + try { + loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey); + break;// break if no exception was thrown + } catch (AssetNotFoundException e) { + LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", p); + } + } + + if (loadedAsset != null) { + Map> linkedData = loadedAsset.getUserData("linkedData"); + for (Entry> entry : linkedData.entrySet()) { + String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey(); + + List scenes = (List) entry.getValue().get("scenes"); + for (Node scene : scenes) { + blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene); + } + List objects = (List) entry.getValue().get("objects"); + for (Node object : objects) { + blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object); + } + List meshes = (List) entry.getValue().get("meshes"); + for (TemporalMesh mesh : meshes) { + blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh); + } + List materials = (List) entry.getValue().get("materials"); + for (MaterialContext materialContext : materials) { + blenderContext.addLinkedFeature(linkedDataFilePath, "MA" + materialContext.getName(), materialContext); + } + List textures = (List) entry.getValue().get("textures"); + for (Texture texture : textures) { + blenderContext.addLinkedFeature(linkedDataFilePath, "TE" + texture.getName(), texture); + } + List images = (List) entry.getValue().get("images"); + for (Texture image : images) { + blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image); + } + List animations = (List) entry.getValue().get("animations"); + for (Animation animation : animations) { + blenderContext.addLinkedFeature(linkedDataFilePath, "AC" + animation.getName(), animation); + } + List cameras = (List) entry.getValue().get("cameras"); + for (Camera camera : cameras) { + blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera); + } + List lights = (List) entry.getValue().get("lights"); + for (Light light : lights) { + blenderContext.addLinkedFeature(linkedDataFilePath, "LA" + light.getName(), light); + } + Spatial sky = (Spatial) entry.getValue().get("sky"); + if (sky != null) { + blenderContext.addLinkedFeature(linkedDataFilePath, sky.getName(), sky); + } + List filters = (List) entry.getValue().get("filters"); + for (Filter filter : filters) { + blenderContext.addLinkedFeature(linkedDataFilePath, filter.getName(), filter); + } + } + } else { + LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path); + } + } + + Object result = blenderContext.getLinkedFeature(path, fullName); + if (result == null) { + LOGGER.log(Level.WARNING, "Could NOT find asset named {0} in the library of path: {1}.", new Object[] { nameOfFeatureToLoad, path }); + } else { + blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.STRUCTURE, id); + blenderContext.addLoadedFeatures(id.getOldMemoryAddress(), LoadedDataType.FEATURE, result); + } + return result; + } else { + LOGGER.warning("Library link points to nothing!"); + } + return null; + } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java index cde38e327..6e8042b09 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java @@ -53,6 +53,7 @@ import com.jme3.scene.plugins.blender.constraints.Constraint; import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.DnaBlockData; import com.jme3.scene.plugins.blender.file.FileBlockHeader; +import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode; import com.jme3.scene.plugins.blender.file.Structure; /** @@ -64,49 +65,51 @@ import com.jme3.scene.plugins.blender.file.Structure; */ public class BlenderContext { /** The blender file version. */ - private int blenderVersion; + private int blenderVersion; /** The blender key. */ - private BlenderKey blenderKey; + private BlenderKey blenderKey; /** The header of the file block. */ - private DnaBlockData dnaBlockData; + private DnaBlockData dnaBlockData; /** The scene structure. */ - private Structure sceneStructure; + private Structure sceneStructure; /** The input stream of the blend file. */ - private BlenderInputStream inputStream; + private BlenderInputStream inputStream; /** The asset manager. */ - private AssetManager assetManager; + private AssetManager assetManager; /** The blocks read from the file. */ - protected List blocks; + protected List blocks; /** * A map containing the file block headers. The key is the old memory address. */ - private Map fileBlockHeadersByOma = new HashMap(); + private Map fileBlockHeadersByOma = new HashMap(); /** A map containing the file block headers. The key is the block code. */ - private Map> fileBlockHeadersByCode = new HashMap>(); + private Map> fileBlockHeadersByCode = new HashMap>(); /** * This map stores the loaded features by their old memory address. The * first object in the value table is the loaded structure and the second - * the structure already converted into proper data. */ - private Map> loadedFeatures = new HashMap>(); + private Map> loadedFeatures = new HashMap>(); + /** Features loaded from external blender files. The key is the file path and the value is a map between feature name and loaded feature. */ + private Map> linkedFeatures = new HashMap>(); /** A stack that hold the parent structure of currently loaded feature. */ - private Stack parentStack = new Stack(); + private Stack parentStack = new Stack(); /** A list of constraints for the specified object. */ - protected Map> constraints = new HashMap>(); + protected Map> constraints = new HashMap>(); /** Animations loaded for features. */ - private Map> animations = new HashMap>(); + private Map> animations = new HashMap>(); /** Loaded skeletons. */ - private Map skeletons = new HashMap(); + private Map skeletons = new HashMap(); /** A map between skeleton and node it modifies. */ - private Map nodesWithSkeletons = new HashMap(); + private Map nodesWithSkeletons = new HashMap(); /** A map of bone contexts. */ - protected Map boneContexts = new HashMap(); + protected Map boneContexts = new HashMap(); /** A map og helpers that perform loading. */ - private Map helpers = new HashMap(); + private Map helpers = new HashMap(); /** Markers used by loading classes to store some custom data. This is made to avoid putting this data into user properties. */ - private Map> markers = new HashMap>(); + private Map> markers = new HashMap>(); /** A map of blender actions. The key is the action name and the value is the action itself. */ - private Map actions = new HashMap(); + private Map actions = new HashMap(); /** * This method sets the blender file version. @@ -231,10 +234,10 @@ public class BlenderContext { */ public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); - List headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode())); + List headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode()); if (headers == null) { headers = new ArrayList(); - fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers); + fileBlockHeadersByCode.put(fileBlockHeader.getCode(), headers); } headers.add(fileBlockHeader); } @@ -258,7 +261,7 @@ public class BlenderContext { * the code of file blocks * @return a list of file blocks' headers of a specified code */ - public List getFileBlocks(Integer code) { + public List getFileBlocks(BlockCode code) { return fileBlockHeadersByCode.get(code); } @@ -299,7 +302,7 @@ public class BlenderContext { throw new IllegalArgumentException("One of the given arguments is null!"); } Map map = loadedFeatures.get(oldMemoryAddress); - if(map == null) { + if (map == null) { map = new HashMap(); loadedFeatures.put(oldMemoryAddress, map); } @@ -325,6 +328,48 @@ public class BlenderContext { return null; } + /** + * The method adds linked content to the blender context. + * @param blenderFilePath + * the path of linked blender file + * @param featureName + * the linked feature name + * @param feature + * the linked feature + */ + public void addLinkedFeature(String blenderFilePath, String featureName, Object feature) { + if (feature != null) { + Map linkedFeatures = this.linkedFeatures.get(blenderFilePath); + if (linkedFeatures == null) { + linkedFeatures = new HashMap(); + this.linkedFeatures.put(blenderFilePath, linkedFeatures); + } + if (!linkedFeatures.containsKey(featureName)) { + linkedFeatures.put(featureName, feature); + } + } + } + + /** + * The method returns linked feature of a given name from the specified blender path. + * @param blenderFilePath + * the blender file path + * @param featureName + * the feature name we want to get + * @return linked feature or null if none was found + */ + public Object getLinkedFeature(String blenderFilePath, String featureName) { + Map linkedFeatures = this.linkedFeatures.get(blenderFilePath); + return linkedFeatures != null ? linkedFeatures.get(featureName) : null; + } + + /** + * @return all linked features for the current blend file + */ + public Map> getLinkedFeatures() { + return linkedFeatures; + } + /** * This method adds the structure to the parent stack. * diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java index 200f83b39..dd0160063 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java @@ -33,17 +33,21 @@ package com.jme3.scene.plugins.blender; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import com.jme3.animation.Animation; import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetLoader; import com.jme3.asset.BlenderKey; -import com.jme3.asset.BlenderKey.FeaturesToLoad; -import com.jme3.asset.BlenderKey.LoadingResults; import com.jme3.asset.ModelKey; import com.jme3.light.Light; +import com.jme3.math.ColorRGBA; +import com.jme3.post.Filter; +import com.jme3.renderer.Camera; import com.jme3.scene.CameraNode; import com.jme3.scene.LightNode; import com.jme3.scene.Node; @@ -55,16 +59,20 @@ import com.jme3.scene.plugins.blender.curves.CurvesHelper; import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.FileBlockHeader; +import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode; import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.landscape.LandscapeHelper; import com.jme3.scene.plugins.blender.lights.LightHelper; +import com.jme3.scene.plugins.blender.materials.MaterialContext; import com.jme3.scene.plugins.blender.materials.MaterialHelper; import com.jme3.scene.plugins.blender.meshes.MeshHelper; +import com.jme3.scene.plugins.blender.meshes.TemporalMesh; import com.jme3.scene.plugins.blender.modifiers.ModifierHelper; import com.jme3.scene.plugins.blender.objects.ObjectHelper; import com.jme3.scene.plugins.blender.particles.ParticlesHelper; import com.jme3.scene.plugins.blender.textures.TextureHelper; +import com.jme3.texture.Texture; /** * This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures. @@ -83,72 +91,130 @@ public class BlenderLoader implements AssetLoader { try { this.setup(assetInfo); - List sceneBlocks = new ArrayList(); - BlenderKey blenderKey = blenderContext.getBlenderKey(); - LoadingResults loadingResults = blenderKey.prepareLoadingResults(); - AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); animationHelper.loadAnimations(); - + + BlenderKey blenderKey = blenderContext.getBlenderKey(); + LoadedFeatures loadedFeatures = new LoadedFeatures(); for (FileBlockHeader block : blocks) { switch (block.getCode()) { - case FileBlockHeader.BLOCK_OB00:// Object + case BLOCK_OB00: ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - Object object = objectHelper.toObject(block.getStructure(blenderContext), blenderContext); - if (object instanceof LightNode) { - loadingResults.addLight((LightNode) object); - } else if (object instanceof CameraNode) { - loadingResults.addCamera((CameraNode) object); - } else if (object instanceof Node) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() }); - } - if (this.isRootObject(loadingResults, (Node) object)) { - loadingResults.addObject((Node) object); - } + Node object = (Node) objectHelper.toObject(block.getStructure(blenderContext), blenderContext); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { object.getName(), object.getLocalTranslation().toString(), object.getParent() == null ? "null" : object.getParent().getName() }); } + if (object.getParent() == null) { + loadedFeatures.objects.add(object); + } + if (object instanceof LightNode && ((LightNode) object).getLight() != null) { + loadedFeatures.lights.add(((LightNode) object).getLight()); + } else if (object instanceof CameraNode && ((CameraNode) object).getCamera() != null) { + loadedFeatures.cameras.add(((CameraNode) object).getCamera()); + } + break; + case BLOCK_SC00:// Scene + loadedFeatures.sceneBlocks.add(block); break; -// case FileBlockHeader.BLOCK_MA00:// Material -// MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); -// MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext); -// if (blenderKey.isLoadUnlinkedAssets() && blenderKey.shouldLoad(FeaturesToLoad.MATERIALS)) { -// loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext))); -// } -// break; - case FileBlockHeader.BLOCK_SC00:// Scene - if (blenderKey.shouldLoad(FeaturesToLoad.SCENES)) { - sceneBlocks.add(block); + case BLOCK_MA00:// Material + MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); + MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext); + loadedFeatures.materials.add(materialContext); + break; + case BLOCK_ME00:// Mesh + MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class); + TemporalMesh temporalMesh = meshHelper.toTemporalMesh(block.getStructure(blenderContext), blenderContext); + loadedFeatures.meshes.add(temporalMesh); + break; + case BLOCK_IM00:// Image + TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class); + Texture image = textureHelper.loadImageAsTexture(block.getStructure(blenderContext), 0, blenderContext); + if (image != null && image.getImage() != null) {// render results are stored as images but are not being loaded + loadedFeatures.images.add(image); } break; - case FileBlockHeader.BLOCK_WO00:// World - if (blenderKey.shouldLoad(FeaturesToLoad.WORLD)) { - Structure worldStructure = block.getStructure(blenderContext); - String worldName = worldStructure.getName(); - if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) { - LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class); - Light ambientLight = landscapeHelper.toAmbientLight(worldStructure); - if(ambientLight != null) { - loadingResults.addLight(new LightNode(null, ambientLight)); - } - loadingResults.setSky(landscapeHelper.toSky(worldStructure)); - loadingResults.addFilter(landscapeHelper.toFog(worldStructure)); - loadingResults.setBackgroundColor(landscapeHelper.toBackgroundColor(worldStructure)); + case BLOCK_TE00: + Structure textureStructure = block.getStructure(blenderContext); + int type = ((Number) textureStructure.getFieldValue("type")).intValue(); + if (type == TextureHelper.TEX_IMAGE) { + TextureHelper texHelper = blenderContext.getHelper(TextureHelper.class); + Texture texture = texHelper.getTexture(textureStructure, null, blenderContext); + if (texture != null) {// null is returned when texture has no image + loadedFeatures.textures.add(texture); } + } else { + LOGGER.fine("Only image textures can be loaded as unlinked assets. Generated textures will be applied to an existing object."); } break; + case BLOCK_WO00:// World + LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class); + Structure worldStructure = block.getStructure(blenderContext); + + String worldName = worldStructure.getName(); + if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) { + + Light ambientLight = landscapeHelper.toAmbientLight(worldStructure); + if (ambientLight != null) { + loadedFeatures.objects.add(new LightNode(null, ambientLight)); + loadedFeatures.lights.add(ambientLight); + } + loadedFeatures.sky = landscapeHelper.toSky(worldStructure); + loadedFeatures.backgroundColor = landscapeHelper.toBackgroundColor(worldStructure); + + Filter fogFilter = landscapeHelper.toFog(worldStructure); + if (fogFilter != null) { + loadedFeatures.filters.add(landscapeHelper.toFog(worldStructure)); + } + } + break; + case BLOCK_AC00: + LOGGER.fine("Loading unlinked animations is not yet supported!"); + break; + default: + LOGGER.log(Level.FINEST, "Ommiting the block: {0}.", block.getCode()); } } - // bake constraints after everything is loaded + LOGGER.fine("Baking constraints after every feature is loaded."); ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); constraintHelper.bakeConstraints(blenderContext); - // load the scene at the very end so that the root nodes have no parent during loading or constraints applying - for (FileBlockHeader sceneBlock : sceneBlocks) { - loadingResults.addScene(this.toScene(sceneBlock.getStructure(blenderContext))); + LOGGER.fine("Loading scenes and attaching them to the root object."); + for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) { + loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext))); + } + + LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it."); + Node modelRoot = new Node(blenderKey.getName()); + for (Node scene : loadedFeatures.scenes) { + modelRoot.attachChild(scene); } - return loadingResults; + if (blenderKey.isLoadUnlinkedAssets()) { + LOGGER.fine("Setting loaded content as user data in resulting sptaial."); + Map> linkedData = new HashMap>(); + + Map thisFileData = new HashMap(); + thisFileData.put("scenes", loadedFeatures.scenes == null ? new ArrayList() : loadedFeatures.scenes); + thisFileData.put("objects", loadedFeatures.objects == null ? new ArrayList() : loadedFeatures.objects); + thisFileData.put("meshes", loadedFeatures.meshes == null ? new ArrayList() : loadedFeatures.meshes); + thisFileData.put("materials", loadedFeatures.materials == null ? new ArrayList() : loadedFeatures.materials); + thisFileData.put("textures", loadedFeatures.textures == null ? new ArrayList() : loadedFeatures.textures); + thisFileData.put("images", loadedFeatures.images == null ? new ArrayList() : loadedFeatures.images); + thisFileData.put("animations", loadedFeatures.animations == null ? new ArrayList() : loadedFeatures.animations); + thisFileData.put("cameras", loadedFeatures.cameras == null ? new ArrayList() : loadedFeatures.cameras); + thisFileData.put("lights", loadedFeatures.lights == null ? new ArrayList() : loadedFeatures.lights); + thisFileData.put("filters", loadedFeatures.filters == null ? new ArrayList() : loadedFeatures.filters); + thisFileData.put("backgroundColor", loadedFeatures.backgroundColor); + thisFileData.put("sky", loadedFeatures.sky); + + linkedData.put("this", thisFileData); + linkedData.putAll(blenderContext.getLinkedFeatures()); + + modelRoot.setUserData("linkedData", linkedData); + } + + return modelRoot; } catch (BlenderFileException e) { throw new IOException(e.getLocalizedMessage(), e); } catch (Exception e) { @@ -158,62 +224,36 @@ public class BlenderLoader implements AssetLoader { } } - /** - * This method indicates if the given spatial is a root object. It means it - * has no parent or is directly attached to one of the already loaded scene - * nodes. - * - * @param loadingResults - * loading results containing the scene nodes - * @param spatial - * spatial object - * @return true if the given spatial is a root object and - * false otherwise - */ - protected boolean isRootObject(LoadingResults loadingResults, Spatial spatial) { - if (spatial.getParent() == null) { - return true; - } - for (Node scene : loadingResults.getScenes()) { - if (spatial.getParent().equals(scene)) { - return true; - } - } - return false; - } - /** * This method converts the given structure to a scene node. * @param structure * structure of a scene * @return scene's node + * @throws BlenderFileException + * an exception throw when problems with blender file occur */ - private Node toScene(Structure structure) { + private Node toScene(Structure structure) throws BlenderFileException { ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); Node result = new Node(structure.getName()); - try { - List base = ((Structure) structure.getFieldValue("base")).evaluateListBase(); - for (Structure b : base) { - Pointer pObject = (Pointer) b.getFieldValue("object"); - if (pObject.isNotNull()) { - Structure objectStructure = pObject.fetchData().get(0); + List base = ((Structure) structure.getFieldValue("base")).evaluateListBase(); + for (Structure b : base) { + Pointer pObject = (Pointer) b.getFieldValue("object"); + if (pObject.isNotNull()) { + Structure objectStructure = pObject.fetchData().get(0); - Object object = objectHelper.toObject(objectStructure, blenderContext); - if (object instanceof LightNode) { - result.addLight(((LightNode) object).getLight()); - result.attachChild((LightNode) object); - } else if (object instanceof Node) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() }); - } - if (((Node) object).getParent() == null) { - result.attachChild((Spatial) object); - } + Object object = objectHelper.toObject(objectStructure, blenderContext); + if (object instanceof LightNode) { + result.addLight(((LightNode) object).getLight());// FIXME: check if this is needed !!! + result.attachChild((LightNode) object); + } else if (object instanceof Node) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() }); + } + if (((Node) object).getParent() == null) { + result.attachChild((Spatial) object); } } } - } catch (BlenderFileException e) { - LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); } return result; } @@ -261,7 +301,7 @@ public class BlenderLoader implements AssetLoader { blenderContext.putHelper(ConstraintHelper.class, new ConstraintHelper(inputStream.getVersionNumber(), blenderContext)); blenderContext.putHelper(ParticlesHelper.class, new ParticlesHelper(inputStream.getVersionNumber(), blenderContext)); blenderContext.putHelper(LandscapeHelper.class, new LandscapeHelper(inputStream.getVersionNumber(), blenderContext)); - + // reading the blocks (dna block is automatically saved in the blender context when found) FileBlockHeader sceneFileBlock = null; do { @@ -269,7 +309,7 @@ public class BlenderLoader implements AssetLoader { if (!fileBlock.isDnaBlock()) { blocks.add(fileBlock); // save the scene's file block - if (fileBlock.getCode() == FileBlockHeader.BLOCK_SC00) { + if (fileBlock.getCode() == BlockCode.BLOCK_SC00) { sceneFileBlock = fileBlock; } } @@ -287,4 +327,39 @@ public class BlenderLoader implements AssetLoader { blenderContext = null; blocks = null; } + + /** + * This class holds the loading results according to the given loading flag. + * @author Marcin Roguski (Kaelthas) + */ + private static class LoadedFeatures { + private List sceneBlocks = new ArrayList(); + /** The scenes from the file. */ + private List scenes = new ArrayList(); + /** Objects from all scenes. */ + private List objects = new ArrayList(); + /** All meshes. */ + private List meshes = new ArrayList(); + /** Materials from all objects. */ + private List materials = new ArrayList(); + /** Textures from all objects. */ + private List textures = new ArrayList(); + /** The images stored in the blender file. */ + private List images = new ArrayList(); + /** Animations of all objects. */ + private List animations = new ArrayList(); + /** All cameras from the file. */ + private List cameras = new ArrayList(); + /** All lights from the file. */ + private List lights = new ArrayList(); + /** Loaded sky. */ + private Spatial sky; + /** Scene filters (ie. FOG). */ + private List filters = new ArrayList(); + /** + * The background color of the render loaded from the horizon color of the world. If no world is used than the gray color + * is set to default (as in blender editor. + */ + private ColorRGBA backgroundColor = ColorRGBA.Gray; + } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java index 021a6082f..eae047421 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java @@ -31,79 +31,10 @@ */ package com.jme3.scene.plugins.blender; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.asset.AssetInfo; -import com.jme3.asset.BlenderKey; -import com.jme3.asset.BlenderKey.FeaturesToLoad; -import com.jme3.scene.LightNode; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.plugins.blender.animations.AnimationHelper; -import com.jme3.scene.plugins.blender.constraints.ConstraintHelper; -import com.jme3.scene.plugins.blender.file.BlenderFileException; -import com.jme3.scene.plugins.blender.file.FileBlockHeader; -import com.jme3.scene.plugins.blender.objects.ObjectHelper; - /** * This is the main loading class. Have in notice that asset manager needs to have loaders for resources like textures. - * + * @deprecated this class is deprecated; use BlenderLoader instead * @author Marcin Roguski (Kaelthas) */ public class BlenderModelLoader extends BlenderLoader { - - private static final Logger LOGGER = Logger.getLogger(BlenderModelLoader.class.getName()); - - @Override - public Spatial load(AssetInfo assetInfo) throws IOException { - try { - this.setup(assetInfo); - - AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); - animationHelper.loadAnimations(); - - BlenderKey blenderKey = blenderContext.getBlenderKey(); - List rootObjects = new ArrayList(); - for (FileBlockHeader block : blocks) { - if (block.getCode() == FileBlockHeader.BLOCK_OB00) { - ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); - Object object = objectHelper.toObject(block.getStructure(blenderContext), blenderContext); - if (object instanceof LightNode && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.LIGHTS) != 0) { - rootObjects.add((LightNode) object); - } else if (object instanceof Node && (blenderKey.getFeaturesToLoad() & FeaturesToLoad.OBJECTS) != 0) { - LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { ((Node) object).getName(), ((Node) object).getLocalTranslation().toString(), ((Node) object).getParent() == null ? "null" : ((Node) object).getParent().getName() }); - if (((Node) object).getParent() == null) { - rootObjects.add((Node) object); - } - } - } - } - - // bake constraints after everything is loaded - ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class); - constraintHelper.bakeConstraints(blenderContext); - - // attach the nodes to the root node at the very end so that the root objects have no parents during constraint applying - LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene to it."); - Node modelRoot = new Node(blenderKey.getName()); - for (Node node : rootObjects) { - if (node instanceof LightNode) { - modelRoot.addLight(((LightNode) node).getLight()); - } - modelRoot.attachChild(node); - } - - return modelRoot; - } catch (BlenderFileException e) { - throw new IOException(e.getLocalizedMessage(), e); - } catch (Exception e) { - throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e); - } finally { - this.clear(); - } - } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java index 1b5a40e79..1d889f992 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java @@ -25,6 +25,7 @@ import com.jme3.scene.plugins.blender.curves.BezierCurve; import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.FileBlockHeader; +import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode; import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.objects.ObjectHelper; @@ -48,7 +49,7 @@ public class AnimationHelper extends AbstractBlenderHelper { */ public void loadAnimations() throws BlenderFileException { LOGGER.info("Loading animations that will be later applied to scene features."); - List actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); + List actionHeaders = blenderContext.getFileBlocks(BlockCode.BLOCK_AC00); if (actionHeaders != null) { for (FileBlockHeader header : actionHeaders) { Structure actionStructure = header.getStructure(blenderContext); @@ -70,7 +71,7 @@ public class AnimationHelper extends AbstractBlenderHelper { if (actions.size() > 0) { List animations = new ArrayList(); for (BlenderAction action : actions) { - SpatialTrack[] tracks = action.toTracks(node); + SpatialTrack[] tracks = action.toTracks(node, blenderContext); if (tracks != null && tracks.length > 0) { Animation spatialAnimation = new Animation(action.getName(), action.getAnimationTime()); spatialAnimation.setTracks(tracks); @@ -109,7 +110,7 @@ public class AnimationHelper extends AbstractBlenderHelper { if (actions.size() > 0) { List animations = new ArrayList(); for (BlenderAction action : actions) { - BoneTrack[] tracks = action.toTracks(skeleton); + BoneTrack[] tracks = action.toTracks(skeleton, blenderContext); if (tracks != null && tracks.length > 0) { Animation boneAnimation = new Animation(action.getName(), action.getAnimationTime()); boneAnimation.setTracks(tracks); diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BlenderAction.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BlenderAction.java index aa36c918e..38a437ba0 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BlenderAction.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BlenderAction.java @@ -10,9 +10,8 @@ import java.util.Map.Entry; import com.jme3.animation.BoneTrack; import com.jme3.animation.Skeleton; import com.jme3.animation.SpatialTrack; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; import com.jme3.scene.Node; +import com.jme3.scene.plugins.blender.BlenderContext; /** * An abstract representation of animation. The data stored here is mainly a @@ -69,10 +68,10 @@ public class BlenderAction implements Cloneable { * the node that will be animated * @return the spatial tracks for the node */ - public SpatialTrack[] toTracks(Node node) { + public SpatialTrack[] toTracks(Node node, BlenderContext blenderContext) { List tracks = new ArrayList(featuresTracks.size()); for (Entry entry : featuresTracks.entrySet()) { - tracks.add((SpatialTrack) entry.getValue().calculateTrack(0, node.getLocalTranslation(), node.getLocalRotation(), node.getLocalScale(), 1, stopFrame, fps, true)); + tracks.add((SpatialTrack) entry.getValue().calculateTrack(0, null, node.getLocalTranslation(), node.getLocalRotation(), node.getLocalScale(), 1, stopFrame, fps, true)); } return tracks.toArray(new SpatialTrack[tracks.size()]); } @@ -84,11 +83,12 @@ public class BlenderAction implements Cloneable { * the skeleton that will be animated * @return the bone tracks for the node */ - public BoneTrack[] toTracks(Skeleton skeleton) { + public BoneTrack[] toTracks(Skeleton skeleton, BlenderContext blenderContext) { List tracks = new ArrayList(featuresTracks.size()); for (Entry entry : featuresTracks.entrySet()) { int boneIndex = skeleton.getBoneIndex(entry.getKey()); - tracks.add((BoneTrack) entry.getValue().calculateTrack(boneIndex, Vector3f.ZERO, Quaternion.IDENTITY, Vector3f.UNIT_XYZ, 1, stopFrame, fps, false)); + BoneContext boneContext = blenderContext.getBoneContext(skeleton.getBone(boneIndex)); + tracks.add((BoneTrack) entry.getValue().calculateTrack(boneIndex, boneContext, boneContext.getBone().getBindPosition(), boneContext.getBone().getBindRotation(), boneContext.getBone().getBindScale(), 1, stopFrame, fps, false)); } return tracks.toArray(new BoneTrack[tracks.size()]); } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java index 0eb50fb65..adc0a5c4c 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java @@ -25,10 +25,13 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper; */ public class BoneContext { // the flags of the bone - public static final int SELECTED = 0x0001; - public static final int CONNECTED_TO_PARENT = 0x0010; - public static final int DEFORM = 0x1000; - + public static final int SELECTED = 0x000001; + public static final int CONNECTED_TO_PARENT = 0x000010; + public static final int DEFORM = 0x001000; + public static final int NO_LOCAL_LOCATION = 0x400000; + public static final int NO_INHERIT_SCALE = 0x008000; + public static final int NO_INHERIT_ROTATION = 0x000200; + /** * The bones' matrices have, unlike objects', the coordinate system identical to JME's (Y axis is UP, X to the right and Z toward us). * So in order to have them loaded properly we need to transform their armature matrix (which blender sees as rotated) to make sure we get identical results. diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java index f388b703f..f5113129f 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java @@ -137,7 +137,7 @@ public class Ipo { * as jme while other features have different one (Z is UP) * @return bone track for the specified bone */ - public Track calculateTrack(int targetIndex, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean spatialTrack) { + public Track calculateTrack(int targetIndex, BoneContext boneContext, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean spatialTrack) { if (calculatedTrack == null) { // preparing data for track int framesAmount = stopFrame - startFrame; @@ -236,6 +236,15 @@ public class Ipo { } } translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2])); + + if(boneContext != null) { + if(boneContext.getBone().getParent() == null && boneContext.is(BoneContext.NO_LOCAL_LOCATION)) { + float temp = translations[index].z; + translations[index].z = -translations[index].y; + translations[index].y = temp; + } + } + if (queternionRotationUsed) { rotations[index] = new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); } else { @@ -292,7 +301,7 @@ public class Ipo { } @Override - public BoneTrack calculateTrack(int boneIndex, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean boneTrack) { + public BoneTrack calculateTrack(int boneIndex, BoneContext boneContext, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean boneTrack) { throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!"); } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java index c5e1f0192..70cb09b1f 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java @@ -5,7 +5,6 @@ import java.util.logging.Logger; import com.jme3.math.FastMath; import com.jme3.renderer.Camera; -import com.jme3.scene.CameraNode; import com.jme3.scene.plugins.blender.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.file.BlenderFileException; @@ -43,7 +42,7 @@ public class CameraHelper extends AbstractBlenderHelper { * an exception is thrown when there are problems with the * blender file */ - public CameraNode toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException { + public Camera toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException { if (blenderVersion >= 250) { return this.toCamera250(structure, blenderContext.getSceneStructure()); } else { @@ -63,7 +62,7 @@ public class CameraHelper extends AbstractBlenderHelper { * an exception is thrown when there are problems with the * blender file */ - private CameraNode toCamera250(Structure structure, Structure sceneStructure) throws BlenderFileException { + private Camera toCamera250(Structure structure, Structure sceneStructure) throws BlenderFileException { int width = DEFAULT_CAM_WIDTH; int height = DEFAULT_CAM_HEIGHT; if (sceneStructure != null) { @@ -99,7 +98,7 @@ public class CameraHelper extends AbstractBlenderHelper { sensor = ((Number) structure.getFieldValue(sensorName)).floatValue(); } float focalLength = ((Number) structure.getFieldValue("lens")).floatValue(); - float fov = 2.0f * FastMath.atan((sensor / 2.0f) / focalLength); + float fov = 2.0f * FastMath.atan(sensor / 2.0f / focalLength); if (sensorVertical) { fovY = fov * FastMath.RAD_TO_DEG; } else { @@ -111,7 +110,8 @@ public class CameraHelper extends AbstractBlenderHelper { fovY = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); } camera.setFrustumPerspective(fovY, aspect, clipsta, clipend); - return new CameraNode(null, camera); + camera.setName(structure.getName()); + return camera; } /** @@ -124,7 +124,7 @@ public class CameraHelper extends AbstractBlenderHelper { * an exception is thrown when there are problems with the * blender file */ - private CameraNode toCamera249(Structure structure) throws BlenderFileException { + private Camera toCamera249(Structure structure) throws BlenderFileException { Camera camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); int type = ((Number) structure.getFieldValue("type")).intValue(); if (type != 0 && type != 1) { @@ -142,6 +142,7 @@ public class CameraHelper extends AbstractBlenderHelper { aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); } camera.setFrustumPerspective(aspect, camera.getWidth() / camera.getHeight(), clipsta, clipend); - return new CameraNode(null, camera); + camera.setName(structure.getName()); + return camera; } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/BezierCurve.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/BezierCurve.java index 9876702c8..96a91335d 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/BezierCurve.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/BezierCurve.java @@ -1,10 +1,11 @@ package com.jme3.scene.plugins.blender.curves; +import java.util.ArrayList; +import java.util.List; + import com.jme3.math.Vector3f; import com.jme3.scene.plugins.blender.file.DynamicArray; import com.jme3.scene.plugins.blender.file.Structure; -import java.util.ArrayList; -import java.util.List; /** * A class that helps to calculate the bezier curves calues. It uses doubles for performing calculations to minimize @@ -12,21 +13,26 @@ import java.util.List; * @author Marcin Roguski (Kaelthas) */ public class BezierCurve { + private static final int IPO_CONSTANT = 0; + private static final int IPO_LINEAR = 1; + private static final int IPO_BEZIER = 2; - public static final int X_VALUE = 0; - public static final int Y_VALUE = 1; - public static final int Z_VALUE = 2; + public static final int X_VALUE = 0; + public static final int Y_VALUE = 1; + public static final int Z_VALUE = 2; /** * The type of the curve. Describes the data it modifies. * Used in ipos calculations. */ - private int type; + private int type; /** The dimension of the curve. */ - private int dimension; + private int dimension; /** A table of the bezier points. */ - private double[][][] bezierPoints; + private double[][][] bezierPoints; /** Array that stores a radius for each bezier triple. */ - private double[] radiuses; + private double[] radiuses; + /** Interpolation types of the bezier triples. */ + private int[] interpolations; public BezierCurve(final int type, final List bezTriples, final int dimension) { this(type, bezTriples, dimension, false); @@ -44,6 +50,7 @@ public class BezierCurve { // the third index specifies the coordinates of the specific point in a bezier triple bezierPoints = new double[bezTriples.size()][3][dimension]; radiuses = new double[bezTriples.size()]; + interpolations = new int[bezTriples.size()]; int i = 0, j, k; for (Structure bezTriple : bezTriples) { DynamicArray vec = (DynamicArray) bezTriple.getFieldValue("vec"); @@ -57,7 +64,8 @@ public class BezierCurve { bezierPoints[i][j][1] = temp; } } - radiuses[i++] = ((Number) bezTriple.getFieldValue("radius")).floatValue(); + radiuses[i] = ((Number) bezTriple.getFieldValue("radius")).floatValue(); + interpolations[i++] = ((Number) bezTriple.getFieldValue("ipo", IPO_BEZIER)).intValue(); } } @@ -75,10 +83,19 @@ public class BezierCurve { for (int i = 0; i < bezierPoints.length - 1; ++i) { if (frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) { double t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]); - double oneMinusT = 1.0f - t; - double oneMinusT2 = oneMinusT * oneMinusT; - double t2 = t * t; - return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t; + switch (interpolations[i]) { + case IPO_BEZIER: + double oneMinusT = 1.0f - t; + double oneMinusT2 = oneMinusT * oneMinusT; + double t2 = t * t; + return bezierPoints[i][1][valuePart] * oneMinusT2 * oneMinusT + 3.0f * bezierPoints[i][2][valuePart] * t * oneMinusT2 + 3.0f * bezierPoints[i + 1][0][valuePart] * t2 * oneMinusT + bezierPoints[i + 1][1][valuePart] * t2 * t; + case IPO_LINEAR: + return (1f - t) * bezierPoints[i][1][valuePart] + t * bezierPoints[i + 1][1][valuePart]; + case IPO_CONSTANT: + return bezierPoints[i][1][valuePart]; + default: + throw new IllegalStateException("Unknown interpolation type for curve: " + interpolations[i]); + } } } if (frame < bezierPoints[0][1][0]) { diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/FileBlockHeader.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/FileBlockHeader.java index 7bd634254..666da7896 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/FileBlockHeader.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/FileBlockHeader.java @@ -31,6 +31,8 @@ */ package com.jme3.scene.plugins.blender.file; +import java.util.logging.Logger; + import com.jme3.scene.plugins.blender.BlenderContext; /** @@ -39,39 +41,23 @@ import com.jme3.scene.plugins.blender.BlenderContext; * @author Marcin Roguski */ public class FileBlockHeader { + private static final Logger LOGGER = Logger.getLogger(FileBlockHeader.class.getName()); - public static final int BLOCK_TE00 = 'T' << 24 | 'E' << 16; // TE00 - public static final int BLOCK_ME00 = 'M' << 24 | 'E' << 16; // ME00 - public static final int BLOCK_SR00 = 'S' << 24 | 'R' << 16; // SR00 - public static final int BLOCK_CA00 = 'C' << 24 | 'A' << 16; // CA00 - public static final int BLOCK_LA00 = 'L' << 24 | 'A' << 16; // LA00 - public static final int BLOCK_OB00 = 'O' << 24 | 'B' << 16; // OB00 - public static final int BLOCK_MA00 = 'M' << 24 | 'A' << 16; // MA00 - public static final int BLOCK_SC00 = 'S' << 24 | 'C' << 16; // SC00 - public static final int BLOCK_WO00 = 'W' << 24 | 'O' << 16; // WO00 - public static final int BLOCK_TX00 = 'T' << 24 | 'X' << 16; // TX00 - public static final int BLOCK_IP00 = 'I' << 24 | 'P' << 16; // IP00 - public static final int BLOCK_AC00 = 'A' << 24 | 'C' << 16; // AC00 - public static final int BLOCK_GLOB = 'G' << 24 | 'L' << 16 | 'O' << 8 | 'B'; // GLOB - public static final int BLOCK_REND = 'R' << 24 | 'E' << 16 | 'N' << 8 | 'D'; // REND - public static final int BLOCK_DATA = 'D' << 24 | 'A' << 16 | 'T' << 8 | 'A'; // DATA - public static final int BLOCK_DNA1 = 'D' << 24 | 'N' << 16 | 'A' << 8 | '1'; // DNA1 - public static final int BLOCK_ENDB = 'E' << 24 | 'N' << 16 | 'D' << 8 | 'B'; // ENDB /** Identifier of the file-block [4 bytes]. */ - private int code; + private BlockCode code; /** Total length of the data after the file-block-header [4 bytes]. */ - private int size; + private int size; /** * Memory address the structure was located when written to disk [4 or 8 bytes (defined in file header as a pointer * size)]. */ - private long oldMemoryAddress; + private long oldMemoryAddress; /** Index of the SDNA structure [4 bytes]. */ - private int sdnaIndex; + private int sdnaIndex; /** Number of structure located in this file-block [4 bytes]. */ - private int count; + private int count; /** Start position of the block's data in the stream. */ - private int blockPosition; + private int blockPosition; /** * Constructor. Loads the block header from the given stream during instance creation. @@ -84,13 +70,13 @@ public class FileBlockHeader { */ public FileBlockHeader(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException { inputStream.alignPosition(4); - code = inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte(); + code = BlockCode.valueOf(inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte()); size = inputStream.readInt(); oldMemoryAddress = inputStream.readPointer(); sdnaIndex = inputStream.readInt(); count = inputStream.readInt(); blockPosition = inputStream.getPosition(); - if (FileBlockHeader.BLOCK_DNA1 == code) { + if (BlockCode.BLOCK_DNA1 == code) { blenderContext.setBlockData(new DnaBlockData(inputStream, blenderContext)); } else { inputStream.setPosition(blockPosition + size); @@ -116,7 +102,7 @@ public class FileBlockHeader { * This method returns the code of this data block. * @return the code of this data block */ - public int getCode() { + public BlockCode getCode() { return code; } @@ -157,7 +143,7 @@ public class FileBlockHeader { * @return true if this block is the last one in the file nad false otherwise */ public boolean isLastBlock() { - return FileBlockHeader.BLOCK_ENDB == code; + return BlockCode.BLOCK_ENDB == code; } /** @@ -165,25 +151,44 @@ public class FileBlockHeader { * @return true if this block is the SDNA block and false otherwise */ public boolean isDnaBlock() { - return FileBlockHeader.BLOCK_DNA1 == code; + return BlockCode.BLOCK_DNA1 == code; } @Override public String toString() { - return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]"; + return "FILE BLOCK HEADER [" + code.toString() + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]"; } - /** - * This method transforms the coded bloch id into a string value. - * @param code - * the id of the block - * @return the string value of the block id - */ - protected String codeToString(int code) { - char c1 = (char) ((code & 0xFF000000) >> 24); - char c2 = (char) ((code & 0xFF0000) >> 16); - char c3 = (char) ((code & 0xFF00) >> 8); - char c4 = (char) (code & 0xFF); - return String.valueOf(c1) + c2 + c3 + c4; + public static enum BlockCode { + BLOCK_ME00('M' << 24 | 'E' << 16), // mesh + BLOCK_CA00('C' << 24 | 'A' << 16), // camera + BLOCK_LA00('L' << 24 | 'A' << 16), // lamp + BLOCK_OB00('O' << 24 | 'B' << 16), // object + BLOCK_MA00('M' << 24 | 'A' << 16), // material + BLOCK_SC00('S' << 24 | 'C' << 16), // scene + BLOCK_WO00('W' << 24 | 'O' << 16), // world + BLOCK_TX00('T' << 24 | 'X' << 16), // texture + BLOCK_IP00('I' << 24 | 'P' << 16), // ipo + BLOCK_AC00('A' << 24 | 'C' << 16), // action + BLOCK_IM00('I' << 24 | 'M' << 16), // image + BLOCK_TE00('T' << 24 | 'E' << 16), BLOCK_WM00('W' << 24 | 'M' << 16), BLOCK_SR00('S' << 24 | 'R' << 16), BLOCK_SN00('S' << 24 | 'N' << 16), BLOCK_BR00('B' << 24 | 'R' << 16), BLOCK_LS00('L' << 24 | 'S' << 16), BLOCK_GLOB('G' << 24 | 'L' << 16 | 'O' << 8 | 'B'), BLOCK_REND('R' << 24 | 'E' << 16 | 'N' << 8 | 'D'), BLOCK_DATA('D' << 24 | 'A' << 16 | 'T' << 8 | 'A'), BLOCK_DNA1('D' << 24 | 'N' << 16 | 'A' << 8 | '1'), BLOCK_ENDB('E' << 24 | 'N' << 16 | 'D' << 8 | 'B'), BLOCK_TEST('T' << 24 | 'E' << 16 + | 'S' << 8 | 'T'), BLOCK_UNKN(0); + + private int code; + + private BlockCode(int code) { + this.code = code; + } + + public static BlockCode valueOf(int code) { + for (BlockCode blockCode : BlockCode.values()) { + if (blockCode.code == code) { + return blockCode; + } + } + byte[] codeBytes = new byte[] { (byte) (code >> 24 & 0xFF), (byte) (code >> 16 & 0xFF), (byte) (code >> 8 & 0xFF), (byte) (code & 0xFF) }; + LOGGER.warning("Unknown block header: " + new String(codeBytes)); + return BLOCK_UNKN; + } } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java index 4f0de7e04..941c7a8fc 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java @@ -254,7 +254,8 @@ public class Structure implements Cloneable { Structure id = (Structure) fieldValue; return id == null ? null : id.getFieldValue("name").toString().substring(2);// blender adds 2-charactes as a name prefix } - return null; + Object name = this.getFieldValue("name", null); + return name == null ? null : name.toString().substring(2); } @Override diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java index c05eed775..8466d5cce 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java @@ -208,6 +208,6 @@ public class LandscapeHelper extends AbstractBlenderHelper { } LOGGER.fine("Sky texture created. Creating sky."); - return SkyFactory.createSky(blenderContext.getAssetManager(), texture, false); + return SkyFactory.createSky(blenderContext.getAssetManager(), texture, SkyFactory.EnvMapType.CubeMap); } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/lights/LightHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/lights/LightHelper.java index 17e38c92b..3ae866b7c 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/lights/LightHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/lights/LightHelper.java @@ -40,7 +40,6 @@ import com.jme3.light.PointLight; import com.jme3.light.SpotLight; import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; -import com.jme3.scene.LightNode; import com.jme3.scene.plugins.blender.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; @@ -67,8 +66,8 @@ public class LightHelper extends AbstractBlenderHelper { super(blenderVersion, blenderContext); } - public LightNode toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException { - LightNode result = (LightNode) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE); + public Light toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException { + Light result = (Light) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE); if (result != null) { return result; } @@ -111,6 +110,7 @@ public class LightHelper extends AbstractBlenderHelper { float g = ((Number) structure.getFieldValue("g")).floatValue(); float b = ((Number) structure.getFieldValue("b")).floatValue(); light.setColor(new ColorRGBA(r, g, b, 1.0f)); - return new LightNode(structure.getName(), light); + light.setName(structure.getName()); + return light; } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java index 6e67da967..51c7072b1 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java @@ -1,11 +1,15 @@ package com.jme3.scene.plugins.blender.materials; +import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.Savable; import com.jme3.material.Material; import com.jme3.material.RenderState.BlendMode; import com.jme3.material.RenderState.FaceCullMode; @@ -30,7 +34,7 @@ import com.jme3.util.BufferUtils; * This class holds the data about the material. * @author Marcin Roguski (Kaelthas) */ -public final class MaterialContext { +public final class MaterialContext implements Savable { private static final Logger LOGGER = Logger.getLogger(MaterialContext.class.getName()); // texture mapping types @@ -67,7 +71,7 @@ public final class MaterialContext { int diff_shader = ((Number) structure.getFieldValue("diff_shader")).intValue(); diffuseShader = DiffuseShader.values()[diff_shader]; ambientFactor = ((Number) structure.getFieldValue("amb")).floatValue(); - + if (shadeless) { float r = ((Number) structure.getFieldValue("r")).floatValue(); float g = ((Number) structure.getFieldValue("g")).floatValue(); @@ -107,6 +111,13 @@ public final class MaterialContext { this.transparent = transparent; } + /** + * @return the name of the material + */ + public String getName() { + return name; + } + /** * Applies material to a given geometry. * @@ -314,4 +325,14 @@ public final class MaterialContext { float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); return new ColorRGBA(r, g, b, alpha); } + + @Override + public void write(JmeExporter e) throws IOException { + throw new IOException("Material context is not for saving! It implements savable only to be passed to another blend file as a Savable in user data!"); + } + + @Override + public void read(JmeImporter e) throws IOException { + throw new IOException("Material context is not for loading! It implements savable only to be passed to another blend file as a Savable in user data!"); + } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialHelper.java index 5fcccc389..885c5850e 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialHelper.java @@ -51,6 +51,7 @@ import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.shader.VarType; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; +import com.jme3.texture.image.ColorSpace; import com.jme3.texture.Texture; import com.jme3.util.BufferUtils; @@ -161,12 +162,17 @@ public class MaterialHelper extends AbstractBlenderHelper { * an exception is throw when problems with blend file occur */ public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException { - LOGGER.log(Level.FINE, "Loading material."); MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE); if (result != null) { return result; } + if ("ID".equals(structure.getType())) { + LOGGER.fine("Loading material from external blend file."); + return (MaterialContext) this.loadLibrary(structure); + } + + LOGGER.fine("Loading material."); result = new MaterialContext(structure, blenderContext); LOGGER.log(Level.FINE, "Material''s name: {0}", result.name); Long oma = structure.getOldMemoryAddress(); @@ -212,7 +218,7 @@ public class MaterialHelper extends AbstractBlenderHelper { } } - image = new Image(Format.RGBA8, w, h, bb); + image = new Image(Format.RGBA8, w, h, bb, ColorSpace.Linear); texture.setImage(image); result.setTextureParam("Texture", VarType.Texture2D, texture); diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java index 92b236f4c..5284b964a 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java @@ -40,7 +40,6 @@ import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; -import com.jme3.asset.BlenderKey.FeaturesToLoad; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector2f; @@ -52,7 +51,6 @@ import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.DynamicArray; import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Structure; -import com.jme3.scene.plugins.blender.materials.MaterialContext; import com.jme3.scene.plugins.blender.materials.MaterialHelper; import com.jme3.scene.plugins.blender.objects.Properties; @@ -106,17 +104,18 @@ public class MeshHelper extends AbstractBlenderHelper { return temporalMesh.clone(); } + if ("ID".equals(meshStructure.getType())) { + LOGGER.fine("Loading mesh from external blend file."); + return (TemporalMesh) this.loadLibrary(meshStructure); + } + String name = meshStructure.getName(); LOGGER.log(Level.FINE, "Reading mesh: {0}.", name); temporalMesh = new TemporalMesh(meshStructure, blenderContext); LOGGER.fine("Loading materials."); MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); - MaterialContext[] materials = null; - if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) { - materials = materialHelper.getMaterials(meshStructure, blenderContext); - } - temporalMesh.setMaterials(materials); + temporalMesh.setMaterials(materialHelper.getMaterials(meshStructure, blenderContext)); LOGGER.fine("Reading custom properties."); Properties properties = this.loadProperties(meshStructure, blenderContext); diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java index 8e2dc2652..17c0d421e 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java @@ -40,12 +40,15 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import com.jme3.asset.BlenderKey.FeaturesToLoad; +import com.jme3.light.Light; import com.jme3.math.FastMath; import com.jme3.math.Matrix4f; import com.jme3.math.Transform; import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.scene.CameraNode; import com.jme3.scene.Geometry; +import com.jme3.scene.LightNode; import com.jme3.scene.Mesh.Mode; import com.jme3.scene.Node; import com.jme3.scene.Spatial; @@ -106,39 +109,34 @@ public class ObjectHelper extends AbstractBlenderHelper { * an exception is thrown when the given data is inapropriate */ public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException { - LOGGER.fine("Loading blender object."); + Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedDataType.FEATURE); + if (loadedResult != null) { + return loadedResult; + } + LOGGER.fine("Loading blender object."); + if ("ID".equals(objectStructure.getType())) { + Node object = (Node) this.loadLibrary(objectStructure); + if (object.getParent() != null) { + LOGGER.log(Level.FINEST, "Detaching object {0}, loaded from external file, from its parent.", object); + object.getParent().detachChild(object); + } + return object; + } int type = ((Number) objectStructure.getFieldValue("type")).intValue(); ObjectType objectType = ObjectType.valueOf(type); LOGGER.log(Level.FINE, "Type of the object: {0}.", objectType); - if (objectType == ObjectType.LAMP && !blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.LIGHTS)) { - LOGGER.fine("Lamps are not included in loading."); - return null; - } - if (objectType == ObjectType.CAMERA && !blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.CAMERAS)) { - LOGGER.fine("Cameras are not included in loading."); - return null; - } - if (!blenderContext.getBlenderKey().shouldLoad(FeaturesToLoad.OBJECTS)) { - LOGGER.fine("Objects are not included in loading."); - return null; - } + int lay = ((Number) objectStructure.getFieldValue("lay")).intValue(); if ((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) { LOGGER.fine("The layer this object is located in is not included in loading."); return null; } - LOGGER.fine("Checking if the object has not been already loaded."); - Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedDataType.FEATURE); - if (loadedResult != null) { - return loadedResult; - } - blenderContext.pushParent(objectStructure); String name = objectStructure.getName(); LOGGER.log(Level.FINE, "Loading obejct: {0}", name); - + int restrictflag = ((Number) objectStructure.getFieldValue("restrictflag")).intValue(); boolean visible = (restrictflag & 0x01) != 0; @@ -171,7 +169,7 @@ public class ObjectHelper extends AbstractBlenderHelper { Pointer pMesh = (Pointer) objectStructure.getFieldValue("data"); List meshesArray = pMesh.fetchData(); TemporalMesh temporalMesh = meshHelper.toTemporalMesh(meshesArray.get(0), blenderContext); - if(temporalMesh != null) { + if (temporalMesh != null) { result.attachChild(temporalMesh); } break; @@ -183,7 +181,7 @@ public class ObjectHelper extends AbstractBlenderHelper { CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class); Structure curveData = pCurve.fetchData().get(0); TemporalMesh curvesTemporalMesh = curvesHelper.toCurve(curveData, blenderContext); - if(curvesTemporalMesh != null) { + if (curvesTemporalMesh != null) { result.attachChild(curvesTemporalMesh); } } @@ -193,10 +191,12 @@ public class ObjectHelper extends AbstractBlenderHelper { if (pLamp.isNotNull()) { LightHelper lightHelper = blenderContext.getHelper(LightHelper.class); List lampsArray = pLamp.fetchData(); - result = lightHelper.toLight(lampsArray.get(0), blenderContext); - if (result == null) { + Light light = lightHelper.toLight(lampsArray.get(0), blenderContext); + if (light == null) { // probably some light type is not supported, just create a node so that we can maintain child-parent relationship for nodes result = new Node(name); + } else { + result = new LightNode(name, light); } } break; @@ -205,19 +205,25 @@ public class ObjectHelper extends AbstractBlenderHelper { if (pCamera.isNotNull()) { CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class); List camerasArray = pCamera.fetchData(); - result = cameraHelper.toCamera(camerasArray.get(0), blenderContext); + Camera camera = cameraHelper.toCamera(camerasArray.get(0), blenderContext); + if (camera == null) { + // just create a node so that we can maintain child-parent relationship for nodes + result = new Node(name); + } else { + result = new CameraNode(name, camera); + } } break; default: LOGGER.log(Level.WARNING, "Unsupported object type: {0}", type); } - + if (result != null) { LOGGER.fine("Storing loaded feature in blender context and applying markers (those will be removed before the final result is released)."); Long oma = objectStructure.getOldMemoryAddress(); blenderContext.addLoadedFeatures(oma, LoadedDataType.STRUCTURE, objectStructure); blenderContext.addLoadedFeatures(oma, LoadedDataType.FEATURE, result); - + blenderContext.addMarker(OMA_MARKER, result, objectStructure.getOldMemoryAddress()); if (objectType == ObjectType.ARMATURE) { blenderContext.addMarker(ARMATURE_NODE_MARKER, result, Boolean.TRUE); @@ -235,13 +241,13 @@ public class ObjectHelper extends AbstractBlenderHelper { for (Modifier modifier : modifiers) { modifier.apply(result, blenderContext); } - + if (result.getChildren() != null && result.getChildren().size() > 0) { - if(result.getChildren().size() == 1 && result.getChild(0) instanceof TemporalMesh) { + if (result.getChildren().size() == 1 && result.getChild(0) instanceof TemporalMesh) { LOGGER.fine("Converting temporal mesh into jme geometries."); - ((TemporalMesh)result.getChild(0)).toGeometries(); + ((TemporalMesh) result.getChild(0)).toGeometries(); } - + LOGGER.fine("Applying proper scale to the geometries."); for (Spatial child : result.getChildren()) { if (child instanceof Geometry) { diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java index 5bfc9eb78..b7d6958ca 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java @@ -444,13 +444,17 @@ public class CombinedTexture { case RGB8: return true;// these types have no alpha by definition case ABGR8: + case DXT1A: case DXT3: case DXT5: case Luminance16FAlpha16F: case Luminance8Alpha8: case RGBA16F: case RGBA32F: - case RGBA8:// with these types it is better to make sure if the texture is or is not transparent + case RGBA8: + case ARGB8: + case BGRA8: + case RGB5A1:// with these types it is better to make sure if the texture is or is not transparent PixelInputOutput pixelInputOutput = PixelIOFactory.getPixelIO(image.getFormat()); TexturePixel pixel = new TexturePixel(); int depth = image.getDepth() == 0 ? 1 : image.getDepth(); @@ -465,6 +469,8 @@ public class CombinedTexture { } } return true; + default: + throw new IllegalStateException("Unknown image format: " + image.getFormat()); } } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java index 66740769d..99ea9b5d2 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java @@ -41,13 +41,13 @@ public final class ImageUtils { public static Image createEmptyImage(Format format, int width, int height, int depth) { int bufferSize = width * height * (format.getBitsPerPixel() >> 3); if (depth < 2) { - return new Image(format, width, height, BufferUtils.createByteBuffer(bufferSize)); + return new Image(format, width, height, BufferUtils.createByteBuffer(bufferSize), com.jme3.texture.image.ColorSpace.Linear); } ArrayList data = new ArrayList(depth); for (int i = 0; i < depth; ++i) { data.add(BufferUtils.createByteBuffer(bufferSize)); } - return new Image(Format.RGB8, width, height, depth, data); + return new Image(Format.RGB8, width, height, depth, data, com.jme3.texture.image.ColorSpace.Linear); } /** @@ -337,7 +337,7 @@ public final class ImageUtils { alphas[0] = data.get() * 255.0f; alphas[1] = data.get() * 255.0f; //the casts to long must be done here because otherwise 32-bit integers would be shifetd by 32 and 40 bits which would result in improper values - long alphaIndices = (long)data.get() | (long)data.get() << 8 | (long)data.get() << 16 | (long)data.get() << 24 | (long)data.get() << 32 | (long)data.get() << 40; + long alphaIndices = data.get() | (long)data.get() << 8 | (long)data.get() << 16 | (long)data.get() << 24 | (long)data.get() << 32 | (long)data.get() << 40; if (alphas[0] > alphas[1]) {// 6 interpolated alpha values. alphas[2] = (6 * alphas[0] + alphas[1]) / 7; alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7; @@ -404,7 +404,8 @@ public final class ImageUtils { dataArray.add(BufferUtils.createByteBuffer(bytes)); } - Image result = depth > 1 ? new Image(Format.RGBA8, image.getWidth(), image.getHeight(), depth, dataArray) : new Image(Format.RGBA8, image.getWidth(), image.getHeight(), dataArray.get(0)); + Image result = depth > 1 ? new Image(Format.RGBA8, image.getWidth(), image.getHeight(), depth, dataArray, com.jme3.texture.image.ColorSpace.Linear) : + new Image(Format.RGBA8, image.getWidth(), image.getHeight(), dataArray.get(0), com.jme3.texture.image.ColorSpace.Linear); if (newMipmapSizes != null) { result.setMipMapSizes(newMipmapSizes); } @@ -467,6 +468,6 @@ public final class ImageUtils { private static Image toJmeImage(BufferedImage bufferedImage, Format format) { ByteBuffer byteBuffer = BufferUtils.createByteBuffer(bufferedImage.getWidth() * bufferedImage.getHeight() * 3); ImageToAwt.convert(bufferedImage, format, byteBuffer); - return new Image(format, bufferedImage.getWidth(), bufferedImage.getHeight(), byteBuffer); + return new Image(format, bufferedImage.getWidth(), bufferedImage.getHeight(), byteBuffer, com.jme3.texture.image.ColorSpace.Linear); } } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java index ad35a2ebc..f1c1c64fc 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java @@ -121,7 +121,7 @@ public class TextureHelper extends AbstractBlenderHelper { * data. The returned texture has the name set to the value of its blender * type. * - * @param tex + * @param textureStructure * texture structure filled with data * @param blenderContext * the blender context @@ -130,23 +130,29 @@ public class TextureHelper extends AbstractBlenderHelper { * this exception is thrown when the blend file structure is * somehow invalid or corrupted */ - public Texture getTexture(Structure tex, Structure mTex, BlenderContext blenderContext) throws BlenderFileException { - Texture result = (Texture) blenderContext.getLoadedFeature(tex.getOldMemoryAddress(), LoadedDataType.FEATURE); + public Texture getTexture(Structure textureStructure, Structure mTex, BlenderContext blenderContext) throws BlenderFileException { + Texture result = (Texture) blenderContext.getLoadedFeature(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE); if (result != null) { return result; } - int type = ((Number) tex.getFieldValue("type")).intValue(); - int imaflag = ((Number) tex.getFieldValue("imaflag")).intValue(); + + if ("ID".equals(textureStructure.getType())) { + LOGGER.fine("Loading texture from external blend file."); + return (Texture) this.loadLibrary(textureStructure); + } + + int type = ((Number) textureStructure.getFieldValue("type")).intValue(); + int imaflag = ((Number) textureStructure.getFieldValue("imaflag")).intValue(); switch (type) { case TEX_IMAGE:// (it is first because probably this will be most commonly used) - Pointer pImage = (Pointer) tex.getFieldValue("ima"); + Pointer pImage = (Pointer) textureStructure.getFieldValue("ima"); if (pImage.isNotNull()) { Structure image = pImage.fetchData().get(0); - Texture loadedTexture = this.loadTexture(image, imaflag, blenderContext); + Texture loadedTexture = this.loadImageAsTexture(image, imaflag, blenderContext); if (loadedTexture != null) { result = loadedTexture; - this.applyColorbandAndColorFactors(tex, result.getImage(), blenderContext); + this.applyColorbandAndColorFactors(textureStructure, result.getImage(), blenderContext); } } break; @@ -160,7 +166,7 @@ public class TextureHelper extends AbstractBlenderHelper { case TEX_MUSGRAVE: case TEX_VORONOI: case TEX_DISTNOISE: - result = new GeneratedTexture(tex, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext); + result = new GeneratedTexture(textureStructure, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext); break; case TEX_NONE:// No texture, do nothing break; @@ -169,13 +175,13 @@ public class TextureHelper extends AbstractBlenderHelper { case TEX_PLUGIN: case TEX_ENVMAP: case TEX_OCEAN: - LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[] { type, tex.getName() }); + LOGGER.log(Level.WARNING, "Unsupported texture type: {0} for texture: {1}", new Object[] { type, textureStructure.getName() }); break; default: - throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + tex.getName()); + throw new BlenderFileException("Unknown texture type: " + type + " for texture: " + textureStructure.getName()); } if (result != null) { - result.setName(tex.getName()); + result.setName(textureStructure.getName()); result.setWrap(WrapMode.Repeat); // decide if the mipmaps will be generated @@ -195,14 +201,14 @@ public class TextureHelper extends AbstractBlenderHelper { } if (type != TEX_IMAGE) {// only generated textures should have this key - result.setKey(new GeneratedTextureKey(tex.getName())); + result.setKey(new GeneratedTextureKey(textureStructure.getName())); } if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] { result.getName(), tex.getOldMemoryAddress() }); + LOGGER.log(Level.FINE, "Adding texture {0} to the loaded features with OMA = {1}", new Object[] { result.getName(), textureStructure.getOldMemoryAddress() }); } - blenderContext.addLoadedFeatures(tex.getOldMemoryAddress(), LoadedDataType.STRUCTURE, tex); - blenderContext.addLoadedFeatures(tex.getOldMemoryAddress(), LoadedDataType.FEATURE, result); + blenderContext.addLoadedFeatures(textureStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, textureStructure); + blenderContext.addLoadedFeatures(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE, result); } return result; } @@ -222,29 +228,39 @@ public class TextureHelper extends AbstractBlenderHelper { * this exception is thrown when the blend file structure is * somehow invalid or corrupted */ - protected Texture loadTexture(Structure imageStructure, int imaflag, BlenderContext blenderContext) throws BlenderFileException { + public Texture loadImageAsTexture(Structure imageStructure, int imaflag, BlenderContext blenderContext) throws BlenderFileException { LOGGER.log(Level.FINE, "Fetching texture with OMA = {0}", imageStructure.getOldMemoryAddress()); Texture result = null; Image im = (Image) blenderContext.getLoadedFeature(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE); - if (im == null) { - String texturePath = imageStructure.getFieldValue("name").toString(); - Pointer pPackedFile = (Pointer) imageStructure.getFieldValue("packedfile"); - if (pPackedFile.isNull()) { - LOGGER.log(Level.FINE, "Reading texture from file: {0}", texturePath); - result = this.loadImageFromFile(texturePath, imaflag, blenderContext); + // if (im == null) { HACK force reaload always, as constructor in else case is destroying the TextureKeys! + if ("ID".equals(imageStructure.getType())) { + LOGGER.fine("Loading texture from external blend file."); + result = (Texture) this.loadLibrary(imageStructure); } else { - LOGGER.fine("Packed texture. Reading directly from the blend file!"); - Structure packedFile = pPackedFile.fetchData().get(0); - Pointer pData = (Pointer) packedFile.getFieldValue("data"); - FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress()); - blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition()); - ImageLoader imageLoader = new ImageLoader(); - - // Should the texture be flipped? It works for sinbad .. - result = new Texture2D(imageLoader.loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true)); + String texturePath = imageStructure.getFieldValue("name").toString(); + Pointer pPackedFile = (Pointer) imageStructure.getFieldValue("packedfile"); + if (pPackedFile.isNull()) { + LOGGER.log(Level.FINE, "Reading texture from file: {0}", texturePath); + result = this.loadImageFromFile(texturePath, imaflag, blenderContext); + } else { + LOGGER.fine("Packed texture. Reading directly from the blend file!"); + Structure packedFile = pPackedFile.fetchData().get(0); + Pointer pData = (Pointer) packedFile.getFieldValue("data"); + FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress()); + blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition()); + + // Should the texture be flipped? It works for sinbad .. + result = new Texture2D(new ImageLoader().loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true)); + } } - } else { - result = new Texture2D(im); + //} else { + // result = new Texture2D(im); + // } + + if (result != null) {// render result is not being loaded + blenderContext.addLoadedFeatures(imageStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, imageStructure); + blenderContext.addLoadedFeatures(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE, result.getImage()); + result.setName(imageStructure.getName()); } return result; } @@ -524,6 +540,18 @@ public class TextureHelper extends AbstractBlenderHelper { return result; } + /** + * Reads the texture data from the given material or sky structure. + * @param structure + * the structure of material or sky + * @param diffuseColorArray + * array of diffuse colors + * @param skyTexture + * indicates it we're going to read sky texture or not + * @return a list of combined textures + * @throws BlenderFileException + * an exception is thrown when problems with reading the blend file occur + */ @SuppressWarnings("unchecked") public List readTextureData(Structure structure, float[] diffuseColorArray, boolean skyTexture) throws BlenderFileException { DynamicArray mtexsArray = (DynamicArray) structure.getFieldValue("mtex"); diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java index fd4044244..9a4889109 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java @@ -31,6 +31,7 @@ import com.jme3.texture.Image; import com.jme3.texture.Image.Format; import com.jme3.texture.Texture; import com.jme3.texture.Texture2D; +import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; /** @@ -77,7 +78,7 @@ import com.jme3.util.BufferUtils; for (int i = 0; i < facesCount; ++i) { faceTextures.add(new TriangleTextureElement(i, texture2d.getImage(), uvs, true, blenderContext)); } - this.format = texture2d.getImage().getFormat(); + format = texture2d.getImage().getFormat(); } /** @@ -113,7 +114,7 @@ import com.jme3.util.BufferUtils; */ public void blend(TextureBlender textureBlender, TriangulatedTexture baseTexture, BlenderContext blenderContext) { Format newFormat = null; - for (TriangleTextureElement triangleTextureElement : this.faceTextures) { + for (TriangleTextureElement triangleTextureElement : faceTextures) { Image baseImage = baseTexture == null ? null : baseTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image; triangleTextureElement.image = textureBlender.blend(triangleTextureElement.image, baseImage, blenderContext); if (newFormat == null) { @@ -122,7 +123,7 @@ import com.jme3.util.BufferUtils; throw new IllegalArgumentException("Face texture element images MUST have the same image format!"); } } - this.format = newFormat; + format = newFormat; } /** @@ -242,7 +243,7 @@ import com.jme3.util.BufferUtils; resultUVS.set(entry.getKey().faceIndex * 3 + 2, uvs[2]); } - Image resultImage = new Image(format, resultImageWidth, resultImageHeight, BufferUtils.createByteBuffer(resultImageWidth * resultImageHeight * (format.getBitsPerPixel() >> 3))); + Image resultImage = new Image(format, resultImageWidth, resultImageHeight, BufferUtils.createByteBuffer(resultImageWidth * resultImageHeight * (format.getBitsPerPixel() >> 3)), ColorSpace.Linear); resultTexture = new Texture2D(resultImage); for (Entry entry : imageLayoutData.entrySet()) { if (!duplicatedFaceIndexes.contains(entry.getKey().faceIndex)) { @@ -420,7 +421,7 @@ import com.jme3.util.BufferUtils; data.put(pixel.getA8()); } } - image = new Image(Format.RGBA8, width, height, data); + image = new Image(Format.RGBA8, width, height, data, ColorSpace.Linear); // modify the UV values so that they fit the new image float heightUV = maxUVY - minUVY; @@ -481,7 +482,7 @@ import com.jme3.util.BufferUtils; imageHeight = 1; } ByteBuffer data = BufferUtils.createByteBuffer(imageWidth * imageHeight * (imageFormat.getBitsPerPixel() >> 3)); - image = new Image(texture.getImage().getFormat(), imageWidth, imageHeight, data); + image = new Image(texture.getImage().getFormat(), imageWidth, imageHeight, data, ColorSpace.Linear); // computing the pixels PixelInputOutput pixelWriter = PixelIOFactory.getPixelIO(imageFormat); @@ -529,8 +530,8 @@ import com.jme3.util.BufferUtils; public void computeFinalUVCoordinates(int totalImageWidth, int totalImageHeight, int xPos, int yPos, Vector2f[] result) { for (int i = 0; i < 3; ++i) { result[i] = new Vector2f(); - result[i].x = xPos / (float) totalImageWidth + this.uv[i].x * (this.image.getWidth() / (float) totalImageWidth); - result[i].y = yPos / (float) totalImageHeight + this.uv[i].y * (this.image.getHeight() / (float) totalImageHeight); + result[i].x = xPos / (float) totalImageWidth + uv[i].x * (image.getWidth() / (float) totalImageWidth); + result[i].y = yPos / (float) totalImageHeight + uv[i].y * (image.getHeight() / (float) totalImageHeight); } } @@ -623,9 +624,9 @@ import com.jme3.util.BufferUtils; * a position in 3D space */ public RectangleEnvelope(Vector3f pointPosition) { - this.min = pointPosition; - this.h = this.w = Vector3f.ZERO; - this.width = this.height = 1; + min = pointPosition; + h = w = Vector3f.ZERO; + width = height = 1; } /** @@ -642,8 +643,8 @@ import com.jme3.util.BufferUtils; this.min = min; this.h = h; this.w = w; - this.width = w.length(); - this.height = h.length(); + width = w.length(); + height = h.length(); } @Override diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java index 5c3504771..8ff55baac 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java @@ -1,10 +1,12 @@ package com.jme3.scene.plugins.blender.textures.blending; +import java.util.logging.Logger; + +import jme3tools.converters.MipMapGenerator; + import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.materials.MaterialHelper; import com.jme3.texture.Image; -import java.util.logging.Logger; -import jme3tools.converters.MipMapGenerator; /** * An abstract class that contains the basic methods used by the classes that @@ -103,12 +105,12 @@ import jme3tools.converters.MipMapGenerator; public void copyBlendingData(TextureBlender textureBlender) { if (textureBlender instanceof AbstractTextureBlender) { - this.flag = ((AbstractTextureBlender) textureBlender).flag; - this.negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture; - this.blendType = ((AbstractTextureBlender) textureBlender).blendType; - this.materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone(); - this.color = ((AbstractTextureBlender) textureBlender).color.clone(); - this.blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor; + flag = ((AbstractTextureBlender) textureBlender).flag; + negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture; + blendType = ((AbstractTextureBlender) textureBlender).blendType; + materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone(); + color = ((AbstractTextureBlender) textureBlender).color.clone(); + blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor; } else { LOGGER.warning("Cannot copy blending data from other types than " + this.getClass()); } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java index d7f26b1be..1d7bf2146 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java @@ -38,7 +38,9 @@ import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; +import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; + import java.nio.ByteBuffer; import java.util.ArrayList; @@ -141,7 +143,7 @@ public class TextureBlenderAWT extends AbstractTextureBlender { dataArray.add(newData); } - Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray) : new Image(Format.RGBA8, width, height, dataArray.get(0)); + Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray, ColorSpace.Linear) : new Image(Format.RGBA8, width, height, dataArray.get(0), ColorSpace.Linear); if (image.getMipMapSizes() != null) { result.setMipMapSizes(image.getMipMapSizes().clone()); } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java index 264ebc1da..b40e2cc8f 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java @@ -6,9 +6,12 @@ import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; +import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; + import java.nio.ByteBuffer; import java.util.ArrayList; + import jme3tools.converters.RGB565; /** @@ -119,7 +122,7 @@ public class TextureBlenderDDS extends TextureBlenderAWT { dataArray.add(newData); } - Image result = dataArray.size() > 1 ? new Image(format, width, height, depth, dataArray) : new Image(format, width, height, dataArray.get(0)); + Image result = dataArray.size() > 1 ? new Image(format, width, height, depth, dataArray, ColorSpace.Linear) : new Image(format, width, height, dataArray.get(0), ColorSpace.Linear); if (image.getMipMapSizes() != null) { result.setMipMapSizes(image.getMipMapSizes().clone()); } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java index c682ed1e5..f487e240d 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java @@ -31,13 +31,13 @@ */ package com.jme3.scene.plugins.blender.textures.blending; +import java.util.logging.Level; +import java.util.logging.Logger; + import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; -import java.util.logging.Level; -import java.util.logging.Logger; - /** * This class creates the texture blending class depending on the texture type. * @@ -66,7 +66,6 @@ public class TextureBlenderFactory { * the texture format * @return texture blending class */ - @SuppressWarnings("deprecation") public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) { switch (format) { case Luminance8: diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java index cbfc28631..1f2ae01e1 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java @@ -7,7 +7,9 @@ import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory; import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; +import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; + import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.logging.Level; @@ -93,7 +95,7 @@ public class TextureBlenderLuminance extends AbstractTextureBlender { dataArray.add(newData); } - Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray) : new Image(Format.RGBA8, width, height, dataArray.get(0)); + Image result = depth > 1 ? new Image(Format.RGBA8, width, height, depth, dataArray, ColorSpace.Linear) : new Image(Format.RGBA8, width, height, dataArray.get(0), ColorSpace.Linear); if (image.getMipMapSizes() != null) { result.setMipMapSizes(image.getMipMapSizes().clone()); } diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java index efacee639..569c328be 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java @@ -17,12 +17,18 @@ import jme3tools.converters.RGB565; case RGBA8: pixel.fromARGB8(data.get(index + 3), data.get(index), data.get(index + 1), data.get(index + 2)); break; + case ARGB8: + pixel.fromARGB8(data.get(index), data.get(index + 1), data.get(index + 2), data.get(index + 3)); + break; case ABGR8: pixel.fromARGB8(data.get(index), data.get(index + 3), data.get(index + 2), data.get(index + 1)); break; case BGR8: pixel.fromARGB8((byte) 0xFF, data.get(index + 2), data.get(index + 1), data.get(index)); break; + case BGRA8: + pixel.fromARGB8(data.get(index + 3), data.get(index + 2), data.get(index + 1), data.get(index)); + break; case RGB8: pixel.fromARGB8((byte) 0xFF, data.get(index), data.get(index + 1), data.get(index + 2)); break; @@ -72,6 +78,12 @@ import jme3tools.converters.RGB565; data.put(index + 2, pixel.getB8()); data.put(index + 3, pixel.getA8()); break; + case ARGB8: + data.put(index, pixel.getA8()); + data.put(index + 1, pixel.getR8()); + data.put(index + 2, pixel.getG8()); + data.put(index + 3, pixel.getB8()); + break; case ABGR8: data.put(index, pixel.getA8()); data.put(index + 1, pixel.getB8()); @@ -83,6 +95,12 @@ import jme3tools.converters.RGB565; data.put(index + 1, pixel.getG8()); data.put(index + 2, pixel.getR8()); break; + case BGRA8: + data.put(index, pixel.getB8()); + data.put(index + 1, pixel.getG8()); + data.put(index + 2, pixel.getR8()); + data.put(index + 3, pixel.getA8()); + break; case RGB8: data.put(index, pixel.getR8()); data.put(index + 1, pixel.getG8()); diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java index 916090228..47cf7090b 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java @@ -26,7 +26,9 @@ public class PixelIOFactory { case ABGR8: case RGBA8: case BGR8: + case BGRA8: case RGB8: + case ARGB8: case RGB111110F: case RGB16F: case RGB16F_to_RGB111110F: diff --git a/jme3-bullet-native/build.gradle b/jme3-bullet-native/build.gradle index 163485f0e..dbb74981a 100644 --- a/jme3-bullet-native/build.gradle +++ b/jme3-bullet-native/build.gradle @@ -175,7 +175,7 @@ binaries.withType(SharedLibraryBinary) { binary -> // Add depend on build jar.dependsOn builderTask // Add output to libs folder - task "copyBinaryToLibs${targetPlatform}"(type: Copy) { + task "copyBinaryToLibs${targetPlatform}"(type: Copy, dependsOn: builderTask) { from builderTask.outputFile into "libs/native/${targetPlatform.operatingSystem.name}/${targetPlatform.architecture.name}" } diff --git a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.cpp b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.cpp index b51630c55..f9be6a6ad 100644 --- a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.cpp +++ b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.cpp @@ -468,6 +468,67 @@ extern "C" { return; } + + + JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_sweepTest_1native + (JNIEnv * env, jobject object, jlong shapeId, jobject from, jobject to, jlong spaceId, jobject resultlist, jfloat allowedCcdPenetration) { + + jmePhysicsSpace* space = reinterpret_cast (spaceId); + if (space == NULL) { + jclass newExc = env->FindClass("java/lang/NullPointerException"); + env->ThrowNew(newExc, "The physics space does not exist."); + return; + } + + btCollisionShape* shape = reinterpret_cast (shapeId); + if (shape == NULL) { + jclass newExc = env->FindClass("java/lang/NullPointerException"); + env->ThrowNew(newExc, "The shape does not exist."); + return; + } + + struct AllConvexResultCallback : public btCollisionWorld::ConvexResultCallback { + + AllConvexResultCallback(const btTransform& convexFromWorld, const btTransform & convexToWorld) : m_convexFromWorld(convexFromWorld), m_convexToWorld(convexToWorld) { + } + jobject resultlist; + JNIEnv* env; + btTransform m_convexFromWorld; //used to calculate hitPointWorld from hitFraction + btTransform m_convexToWorld; + + btVector3 m_hitNormalWorld; + btVector3 m_hitPointWorld; + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace) { + if (normalInWorldSpace) { + m_hitNormalWorld = convexResult.m_hitNormalLocal; + } + else { + m_hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis() * convexResult.m_hitNormalLocal; + } + m_hitPointWorld.setInterpolate3(m_convexFromWorld.getBasis() * m_convexFromWorld.getOrigin(), m_convexToWorld.getBasis() * m_convexToWorld.getOrigin(), convexResult.m_hitFraction); + + jmeBulletUtil::addSweepResult(env, resultlist, &m_hitNormalWorld, &m_hitPointWorld, convexResult.m_hitFraction, convexResult.m_hitCollisionObject); + + return 1.f; + } + }; + + btTransform native_to = btTransform(); + jmeBulletUtil::convert(env, to, &native_to); + + btTransform native_from = btTransform(); + jmeBulletUtil::convert(env, from, &native_from); + + btScalar native_allowed_ccd_penetration = btScalar(allowedCcdPenetration); + + AllConvexResultCallback resultCallback(native_from, native_to); + resultCallback.env = env; + resultCallback.resultlist = resultlist; + space->getDynamicsWorld()->convexSweepTest((btConvexShape *) shape, native_from, native_to, resultCallback, native_allowed_ccd_penetration); + return; + } + #ifdef __cplusplus } #endif diff --git a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.h b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.h index e040f8da8..b499ff04c 100644 --- a/jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.h +++ b/jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.h @@ -165,6 +165,15 @@ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_initNativePhysics JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_finalizeNative (JNIEnv *, jobject, jlong); + +/* +* Class: com_jme3_bullet_PhysicsSpace +* Method : sweepTest_native +* Signature: (J;L;Lcom/jme3/math/Transform;Lcom/jme3/math/Transform;L;JLjava/util/List;F)V +*/ +JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_sweepTest_1native +(JNIEnv *, jobject, jlong, jobject, jobject, jlong, jobject, jfloat); + #ifdef __cplusplus } #endif diff --git a/jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp b/jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp index fa88ad473..04b56a545 100644 --- a/jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp +++ b/jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp @@ -59,6 +59,38 @@ void jmeBulletUtil::convert(JNIEnv* env, jobject in, btVector3* out) { out->setZ(z); } +void jmeBulletUtil::convert(JNIEnv* env, jobject in, btQuaternion* out) { + if (in == NULL || out == NULL) { + jmeClasses::throwNPE(env); + } + float x = env->GetFloatField(in, jmeClasses::Quaternion_x); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + float y = env->GetFloatField(in, jmeClasses::Quaternion_y); //env->CallFloatMethod(in, jmeClasses::Vector3f_getY); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + float z = env->GetFloatField(in, jmeClasses::Quaternion_z); //env->CallFloatMethod(in, jmeClasses::Vector3f_getZ); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + float w = env->GetFloatField(in, jmeClasses::Quaternion_w); //env->CallFloatMethod(in, jmeClasses::Vector3f_getZ); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + out->setX(x); + out->setY(y); + out->setZ(z); + out->setW(w); +} + + void jmeBulletUtil::convert(JNIEnv* env, const btVector3* in, jobject out) { if (in == NULL || out == NULL) { jmeClasses::throwNPE(env); @@ -325,3 +357,61 @@ void jmeBulletUtil::addResult(JNIEnv* env, jobject resultlist, btVector3* hitnor return; } } + + +void jmeBulletUtil::addSweepResult(JNIEnv* env, jobject resultlist, btVector3* hitnormal, btVector3* m_hitPointWorld, btScalar m_hitFraction, const btCollisionObject* hitobject) { + + jobject singleresult = env->AllocObject(jmeClasses::PhysicsSweep_Class); + jobject hitnormalvec = env->AllocObject(jmeClasses::Vector3f); + + convert(env, hitnormal, hitnormalvec); + jmeUserPointer *up1 = (jmeUserPointer*)hitobject->getUserPointer(); + + env->SetObjectField(singleresult, jmeClasses::PhysicsSweep_normalInWorldSpace, hitnormalvec); + env->SetFloatField(singleresult, jmeClasses::PhysicsSweep_hitfraction, m_hitFraction); + + env->SetObjectField(singleresult, jmeClasses::PhysicsSweep_collisionObject, up1->javaCollisionObject); + env->CallVoidMethod(resultlist, jmeClasses::PhysicsSweep_addmethod, singleresult); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } +} + +void jmeBulletUtil::convert(JNIEnv* env, jobject in, btTransform* out) { + if (in == NULL || out == NULL) { + jmeClasses::throwNPE(env); + } + + jobject translation_vec = env->CallObjectMethod(in, jmeClasses::Transform_translation); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + jobject rot_quat = env->CallObjectMethod(in, jmeClasses::Transform_rotation); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + /* + //Scale currently not supported by bullet + //@TBD: Create an assertion somewhere to avoid scale values + jobject scale_vec = env->GetObjectField(in, jmeClasses::Transform_scale); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + */ + btVector3 native_translation_vec = btVector3(); + //btVector3 native_scale_vec = btVector3(); + btQuaternion native_rot_quat = btQuaternion(); + + convert(env, translation_vec, &native_translation_vec); + //convert(env, scale_vec, native_scale_vec); + convert(env, rot_quat, &native_rot_quat); + + out->setRotation(native_rot_quat); + out->setOrigin(native_translation_vec); +} diff --git a/jme3-bullet-native/src/native/cpp/jmeBulletUtil.h b/jme3-bullet-native/src/native/cpp/jmeBulletUtil.h index 96509ab9e..7d982c80e 100644 --- a/jme3-bullet-native/src/native/cpp/jmeBulletUtil.h +++ b/jme3-bullet-native/src/native/cpp/jmeBulletUtil.h @@ -42,10 +42,13 @@ public: static void convert(JNIEnv* env, jobject in, btVector3* out); static void convert(JNIEnv* env, const btVector3* in, jobject out); static void convert(JNIEnv* env, jobject in, btMatrix3x3* out); + static void convert(JNIEnv* env, jobject in, btQuaternion* out); static void convert(JNIEnv* env, const btMatrix3x3* in, jobject out); static void convertQuat(JNIEnv* env, jobject in, btMatrix3x3* out); static void convertQuat(JNIEnv* env, const btMatrix3x3* in, jobject out); + static void convert(JNIEnv* env, jobject in, btTransform* out); static void addResult(JNIEnv* env, jobject resultlist, btVector3* hitnormal, btVector3* m_hitPointWorld,const btScalar m_hitFraction,const btCollisionObject* hitobject); + static void addSweepResult(JNIEnv* env, jobject resultlist, btVector3* hitnormal, btVector3* m_hitPointWorld, const btScalar m_hitFraction, const btCollisionObject* hitobject); private: jmeBulletUtil(){}; ~jmeBulletUtil(){}; diff --git a/jme3-bullet-native/src/native/cpp/jmeClasses.cpp b/jme3-bullet-native/src/native/cpp/jmeClasses.cpp index e0b5515b4..6b7caa0e9 100644 --- a/jme3-bullet-native/src/native/cpp/jmeClasses.cpp +++ b/jme3-bullet-native/src/native/cpp/jmeClasses.cpp @@ -91,6 +91,21 @@ jfieldID jmeClasses::PhysicsRay_collisionObject; jclass jmeClasses::PhysicsRay_listresult; jmethodID jmeClasses::PhysicsRay_addmethod; +jclass jmeClasses::PhysicsSweep_Class; +jmethodID jmeClasses::PhysicsSweep_newSingleResult; + +jfieldID jmeClasses::PhysicsSweep_normalInWorldSpace; +jfieldID jmeClasses::PhysicsSweep_hitfraction; +jfieldID jmeClasses::PhysicsSweep_collisionObject; + +jclass jmeClasses::PhysicsSweep_listresult; +jmethodID jmeClasses::PhysicsSweep_addmethod; + + +jclass jmeClasses::Transform; +jmethodID jmeClasses::Transform_rotation; +jmethodID jmeClasses::Transform_translation; + //private fields //JNIEnv* jmeClasses::env; JavaVM* jmeClasses::vm; @@ -240,6 +255,70 @@ void jmeClasses::initJavaClasses(JNIEnv* env) { env->Throw(env->ExceptionOccurred()); return; } + + PhysicsSweep_Class = (jclass)env->NewGlobalRef(env->FindClass("com/jme3/bullet/collision/PhysicsSweepTestResult")); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; +} + + PhysicsSweep_newSingleResult = env->GetMethodID(PhysicsSweep_Class, "", "()V"); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + PhysicsSweep_normalInWorldSpace = env->GetFieldID(PhysicsSweep_Class, "hitNormalLocal", "Lcom/jme3/math/Vector3f;"); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + + PhysicsSweep_hitfraction = env->GetFieldID(PhysicsSweep_Class, "hitFraction", "F"); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + + PhysicsSweep_collisionObject = env->GetFieldID(PhysicsSweep_Class, "collisionObject", "Lcom/jme3/bullet/collision/PhysicsCollisionObject;"); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + PhysicsSweep_listresult = env->FindClass("java/util/List"); + PhysicsSweep_listresult = (jclass)env->NewGlobalRef(PhysicsSweep_listresult); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + PhysicsSweep_addmethod = env->GetMethodID(PhysicsSweep_listresult, "add", "(Ljava/lang/Object;)Z"); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + Transform = (jclass)env->NewGlobalRef(env->FindClass("com/jme3/math/Transform")); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + Transform_rotation = env->GetMethodID(Transform, "getRotation", "()Lcom/jme3/math/Quaternion;"); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + + Transform_translation = env->GetMethodID(Transform, "getTranslation", "()Lcom/jme3/math/Vector3f;"); + if (env->ExceptionCheck()) { + env->Throw(env->ExceptionOccurred()); + return; + } + } void jmeClasses::throwNPE(JNIEnv* env) { diff --git a/jme3-bullet-native/src/native/cpp/jmeClasses.h b/jme3-bullet-native/src/native/cpp/jmeClasses.h index 24684dae9..bb1b0e99a 100644 --- a/jme3-bullet-native/src/native/cpp/jmeClasses.h +++ b/jme3-bullet-native/src/native/cpp/jmeClasses.h @@ -89,6 +89,18 @@ public: static jclass PhysicsRay_listresult; static jmethodID PhysicsRay_addmethod; + static jclass PhysicsSweep_Class; + static jmethodID PhysicsSweep_newSingleResult; + static jfieldID PhysicsSweep_normalInWorldSpace; + static jfieldID PhysicsSweep_hitfraction; + static jfieldID PhysicsSweep_collisionObject; + static jclass PhysicsSweep_listresult; + static jmethodID PhysicsSweep_addmethod; + + static jclass Transform; + static jmethodID Transform_rotation; + static jmethodID Transform_translation; + static jclass DebugMeshCallback; static jmethodID DebugMeshCallback_addVector; diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java index 0f011f8d7..380dc3e16 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java @@ -820,6 +820,10 @@ public class PhysicsSpace { // return lrr.hitFraction; // } // } +// +// + + /** * Performs a sweep collision test and returns the results as a list of * PhysicsSweepTestResults
You have to use different Transforms for @@ -828,48 +832,47 @@ public class PhysicsSpace { * center. */ public List sweepTest(CollisionShape shape, Transform start, Transform end) { - List results = new LinkedList(); -// if (!(shape.getCShape() instanceof ConvexShape)) { -// logger.log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!"); -// return results; -// } -// dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results)); - return results; + List results = new LinkedList(); + sweepTest(shape, start, end , results); + return (List) results; + } + public List sweepTest(CollisionShape shape, Transform start, Transform end, List results) { + return sweepTest(shape, start, end, results, 0.0f); } + public native void sweepTest_native(long shape, Transform from, Transform to, long physicsSpaceId, List results, float allowedCcdPenetration); /** * Performs a sweep collision test and returns the results as a list of * PhysicsSweepTestResults
You have to use different Transforms for - * start and end (at least distance > 0.4f). SweepTest will not see a + * start and end (at least distance > allowedCcdPenetration). SweepTest will not see a * collision if it starts INSIDE an object and is moving AWAY from its * center. */ - public List sweepTest(CollisionShape shape, Transform start, Transform end, List results) { + public List sweepTest(CollisionShape shape, Transform start, Transform end, List results, float allowedCcdPenetration ) { results.clear(); -// if (!(shape.getCShape() instanceof ConvexShape)) { -// logger.log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!"); -// return results; -// } -// dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results)); + sweepTest_native(shape.getObjectId(), start, end, physicsSpaceId, results, allowedCcdPenetration); return results; } -// private class InternalSweepListener extends CollisionWorld.ConvexResultCallback { -// -// private List results; -// -// public InternalSweepListener(List results) { -// this.results = results; -// } -// -// @Override -// public float addSingleResult(LocalConvexResult lcr, boolean bln) { -// PhysicsCollisionObject obj = (PhysicsCollisionObject) lcr.hitCollisionObject.getUserPointer(); -// results.add(new PhysicsSweepTestResult(obj, Converter.convert(lcr.hitNormalLocal), lcr.hitFraction, bln)); -// return lcr.hitFraction; -// } -// } +/* private class InternalSweepListener extends CollisionWorld.ConvexResultCallback { + + private List results; + + public InternalSweepListener(List results) { + this.results = results; + } + + @Override + public float addSingleResult(LocalConvexResult lcr, boolean bln) { + PhysicsCollisionObject obj = (PhysicsCollisionObject) lcr.hitCollisionObject.getUserPointer(); + results.add(new PhysicsSweepTestResult(obj, Converter.convert(lcr.hitNormalLocal), lcr.hitFraction, bln)); + return lcr.hitFraction; + } + } + + */ + /** * destroys the current PhysicsSpace so that a new one can be created */ diff --git a/jme3-core/build.gradle b/jme3-core/build.gradle index 78bc24dd1..bd699f52a 100644 --- a/jme3-core/build.gradle +++ b/jme3-core/build.gradle @@ -26,14 +26,29 @@ import org.ajoberstar.grgit.* task updateVersion << { - def grgit = Grgit.open(project.file('.').parent) + def verfile = file('src/main/java/com/jme3/system/JmeVersion.java') + def jmeGitHash + def jmeShortGitHash + def jmeBuildDate + def jmeBranchName - def jmeGitHash = grgit.head().id - def jmeShortGitHash = grgit.head().abbreviatedId - def jmeBuildDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date()) - def jmeBranchName = grgit.branch.current.name + try { + def grgit = Grgit.open(project.file('.').parent) + jmeGitHash = grgit.head().id + jmeShortGitHash = grgit.head().abbreviatedId + jmeBuildDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + jmeBranchName = grgit.branch.current.name + } catch (ex) { + // Failed to get repo info + logger.warn("Failed to get repository info: " + ex.message + ". " + \ + "Only partial build info will be generated.") + + jmeGitHash = "" + jmeShortGitHash = "" + jmeBuildDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + jmeBranchName = "unknown" + } - def verfile = file('src/main/java/com/jme3/system/JmeVersion.java') verfile.text = "\npackage com.jme3.system;\n\n" + "/**\n * THIS IS AN AUTO-GENERATED FILE..\n * DO NOT MODIFY!\n */\n" + "public class JmeVersion {\n" + diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java b/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java index 4c362ff00..44b836115 100644 --- a/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java +++ b/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java @@ -312,7 +312,6 @@ public final class AnimChannel { } } animation = null; - // System.out.println("Setting notified false"); notified = false; } diff --git a/jme3-core/src/main/java/com/jme3/asset/AssetConfig.java b/jme3-core/src/main/java/com/jme3/asset/AssetConfig.java index 50b6e38af..71c4d8bb1 100644 --- a/jme3-core/src/main/java/com/jme3/asset/AssetConfig.java +++ b/jme3-core/src/main/java/com/jme3/asset/AssetConfig.java @@ -69,7 +69,7 @@ public final class AssetConfig { public static void loadText(AssetManager assetManager, URL configUrl) throws IOException{ InputStream in = configUrl.openStream(); try { - Scanner scan = new Scanner(in); + Scanner scan = new Scanner(in, "UTF-8"); scan.useLocale(Locale.US); // Fix commas / periods ?? while (scan.hasNext()){ String cmd = scan.next(); diff --git a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java index 4364f3b60..66a6fbbdf 100644 --- a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java +++ b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java @@ -312,13 +312,12 @@ public class DesktopAssetManager implements AssetManager { protected T registerAndCloneSmartAsset(AssetKey key, T obj, AssetProcessor proc, AssetCache cache) { // object obj is the original asset // create an instance for user - T clone = (T) obj; if (proc == null) { throw new IllegalStateException("Asset implements " + "CloneableSmartAsset but doesn't " + "have processor to handle cloning"); } else { - clone = (T) proc.createClone(obj); + T clone = (T) proc.createClone(obj); if (cache != null && clone != obj) { cache.registerAssetClone(key, clone); } else { @@ -326,8 +325,8 @@ public class DesktopAssetManager implements AssetManager { + "CloneableSmartAsset but doesn't have cache or " + "was not cloned"); } + return clone; } - return clone; } @Override diff --git a/jme3-core/src/main/java/com/jme3/asset/ImplHandler.java b/jme3-core/src/main/java/com/jme3/asset/ImplHandler.java index 3601bc463..73e6d8df3 100644 --- a/jme3-core/src/main/java/com/jme3/asset/ImplHandler.java +++ b/jme3-core/src/main/java/com/jme3/asset/ImplHandler.java @@ -47,7 +47,7 @@ import java.util.logging.Logger; * This is done by keeping an instance of each asset loader and asset * locator object in a thread local. */ -public class ImplHandler { +final class ImplHandler { private static final Logger logger = Logger.getLogger(ImplHandler.class.getName()); @@ -75,7 +75,7 @@ public class ImplHandler { this.assetManager = assetManager; } - protected class ImplThreadLocal extends ThreadLocal { + protected static class ImplThreadLocal extends ThreadLocal { private final Class type; private final String path; @@ -83,7 +83,7 @@ public class ImplHandler { public ImplThreadLocal(Class type, String[] extensions){ this.type = type; - this.extensions = extensions; + this.extensions = extensions.clone(); this.path = null; } diff --git a/jme3-core/src/main/java/com/jme3/asset/ShaderNodeDefinitionKey.java b/jme3-core/src/main/java/com/jme3/asset/ShaderNodeDefinitionKey.java index db25a1217..9713d6ab8 100644 --- a/jme3-core/src/main/java/com/jme3/asset/ShaderNodeDefinitionKey.java +++ b/jme3-core/src/main/java/com/jme3/asset/ShaderNodeDefinitionKey.java @@ -39,8 +39,6 @@ import java.util.List; * Used for loading {@link ShaderNodeDefinition shader nodes definition} * * Tells if the defintion has to be loaded with or without its documentation - * - * @author Kirill Vainer */ public class ShaderNodeDefinitionKey extends AssetKey> { diff --git a/jme3-core/src/main/java/com/jme3/asset/cache/WeakRefCloneAssetCache.java b/jme3-core/src/main/java/com/jme3/asset/cache/WeakRefCloneAssetCache.java index e3566d22c..ab3581013 100644 --- a/jme3-core/src/main/java/com/jme3/asset/cache/WeakRefCloneAssetCache.java +++ b/jme3-core/src/main/java/com/jme3/asset/cache/WeakRefCloneAssetCache.java @@ -155,11 +155,7 @@ public class WeakRefCloneAssetCache implements AssetCache { } public T getFromCache(AssetKey key) { - AssetRef smartInfo; - synchronized (smartCache){ - smartInfo = smartCache.get(key); - } - + AssetRef smartInfo = smartCache.get(key); if (smartInfo == null) { return null; } else { diff --git a/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java b/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java index 346998e2e..c3ccea741 100644 --- a/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java +++ b/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java @@ -51,6 +51,9 @@ import static com.jme3.audio.openal.EFX.*; public class ALAudioRenderer implements AudioRenderer, Runnable { private static final Logger logger = Logger.getLogger(ALAudioRenderer.class.getName()); + + private static final String THREAD_NAME = "jME3 Audio Decoder"; + private final NativeObjectManager objManager = new NativeObjectManager(); // When multiplied by STREAMING_BUFFER_COUNT, will equal 44100 * 2 * 2 // which is exactly 1 second of audio. @@ -75,7 +78,7 @@ public class ALAudioRenderer implements AudioRenderer, Runnable { // Fill streaming sources every 50 ms private static final float UPDATE_RATE = 0.05f; - private final Thread decoderThread = new Thread(this, "jME3 Audio Decoding Thread"); + private final Thread decoderThread = new Thread(this, THREAD_NAME); private final Object threadLock = new Object(); private final AL al; diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java index 30751e078..d5dd56203 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java @@ -640,8 +640,7 @@ public class BoundingSphere extends BoundingVolume { return rVal; } - return new BoundingSphere(radius, - (center != null ? (Vector3f) center.clone() : null)); + return new BoundingSphere(radius, center.clone()); } /** diff --git a/jme3-core/src/main/java/com/jme3/collision/CollisionResult.java b/jme3-core/src/main/java/com/jme3/collision/CollisionResult.java index a777bc9b6..ae271c880 100644 --- a/jme3-core/src/main/java/com/jme3/collision/CollisionResult.java +++ b/jme3-core/src/main/java/com/jme3/collision/CollisionResult.java @@ -109,6 +109,11 @@ public class CollisionResult implements Comparable { return super.equals(obj); } + @Override + public int hashCode() { + return Float.floatToIntBits(distance); + } + public Vector3f getContactPoint() { return contactPoint; } diff --git a/jme3-core/src/main/java/com/jme3/light/SpotLight.java b/jme3-core/src/main/java/com/jme3/light/SpotLight.java index 0499ce254..f02f76fa4 100644 --- a/jme3-core/src/main/java/com/jme3/light/SpotLight.java +++ b/jme3-core/src/main/java/com/jme3/light/SpotLight.java @@ -56,7 +56,7 @@ import java.io.IOException; * the light intensity slowly decrease between the inner cone and the outer cone. * @author Nehon */ -public class SpotLight extends Light implements Savable { +public class SpotLight extends Light { protected Vector3f position = new Vector3f(); protected Vector3f direction = new Vector3f(0,-1,0); diff --git a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java index ad16ae8fb..8ab55fbac 100644 --- a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java +++ b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java @@ -103,11 +103,10 @@ public class TechniqueDef implements Savable { private EnumSet requiredCaps = EnumSet.noneOf(Caps.class); private String name; - private EnumMap shaderLanguage; - private EnumMap shaderName; + private EnumMap shaderLanguages; + private EnumMap shaderNames; private DefineList presetDefines; - private boolean usesShaders; private boolean usesNodes = false; private List shaderNodes; private ShaderGenerationInfo shaderGenerationInfo; @@ -140,8 +139,8 @@ public class TechniqueDef implements Savable { * Serialization only. Do not use. */ public TechniqueDef(){ - shaderLanguage=new EnumMap(Shader.ShaderType.class); - shaderName=new EnumMap(Shader.ShaderType.class); + shaderLanguages=new EnumMap(Shader.ShaderType.class); + shaderNames=new EnumMap(Shader.ShaderType.class); } /** @@ -226,7 +225,7 @@ public class TechniqueDef implements Savable { */ @Deprecated public boolean isUsingShaders(){ - return usesShaders; + return true; } /** @@ -258,36 +257,44 @@ public class TechniqueDef implements Savable { * @param fragLanguage The fragment shader language */ public void setShaderFile(String vertexShader, String fragmentShader, String vertLanguage, String fragLanguage) { - this.shaderLanguage.put(Shader.ShaderType.Vertex, vertLanguage); - this.shaderName.put(Shader.ShaderType.Vertex, vertexShader); - this.shaderLanguage.put(Shader.ShaderType.Fragment, fragLanguage); - this.shaderName.put(Shader.ShaderType.Fragment, fragmentShader); + this.shaderLanguages.put(Shader.ShaderType.Vertex, vertLanguage); + this.shaderNames.put(Shader.ShaderType.Vertex, vertexShader); + this.shaderLanguages.put(Shader.ShaderType.Fragment, fragLanguage); + this.shaderNames.put(Shader.ShaderType.Fragment, fragmentShader); + + requiredCaps.clear(); Caps vertCap = Caps.valueOf(vertLanguage); requiredCaps.add(vertCap); Caps fragCap = Caps.valueOf(fragLanguage); requiredCaps.add(fragCap); - - usesShaders = true; } /** * Sets the shaders that this technique definition will use. * - * @param shaderName EnumMap containing all shader names for this stage - * @param shaderLanguage EnumMap containing all shader languages for this stage - */ - public void setShaderFile(EnumMap shaderName, EnumMap shaderLanguage) { - for (Shader.ShaderType shaderType : shaderName.keySet()) { - this.shaderLanguage.put(shaderType,shaderLanguage.get(shaderType)); - this.shaderName.put(shaderType,shaderName.get(shaderType)); - if(shaderType.equals(Shader.ShaderType.Geometry)){ + * @param shaderNames EnumMap containing all shader names for this stage + * @param shaderLanguages EnumMap containing all shader languages for this stage + */ + public void setShaderFile(EnumMap shaderNames, EnumMap shaderLanguages) { + requiredCaps.clear(); + + for (Shader.ShaderType shaderType : shaderNames.keySet()) { + String language = shaderLanguages.get(shaderType); + String shaderFile = shaderNames.get(shaderType); + + this.shaderLanguages.put(shaderType, language); + this.shaderNames.put(shaderType, shaderFile); + + Caps vertCap = Caps.valueOf(language); + requiredCaps.add(vertCap); + + if (shaderType.equals(Shader.ShaderType.Geometry)) { requiredCaps.add(Caps.GeometryShader); - }else if(shaderType.equals(Shader.ShaderType.TessellationControl)){ + } else if (shaderType.equals(Shader.ShaderType.TessellationControl)) { requiredCaps.add(Caps.TesselationShader); } } - usesShaders=true; } /** @@ -362,7 +369,7 @@ public class TechniqueDef implements Savable { * @return the name of the fragment shader to be used. */ public String getFragmentShaderName() { - return shaderName.get(Shader.ShaderType.Fragment); + return shaderNames.get(Shader.ShaderType.Fragment); } @@ -373,42 +380,34 @@ public class TechniqueDef implements Savable { * @return the name of the vertex shader to be used. */ public String getVertexShaderName() { - return shaderName.get(Shader.ShaderType.Vertex); - } - - /** - * @deprecated Use {@link #getVertexShaderLanguage() } instead. - */ - @Deprecated - public String getShaderLanguage() { - return shaderLanguage.get(Shader.ShaderType.Vertex); + return shaderNames.get(Shader.ShaderType.Vertex); } /** * Returns the language of the fragment shader used in this technique. */ public String getFragmentShaderLanguage() { - return shaderLanguage.get(Shader.ShaderType.Fragment); + return shaderLanguages.get(Shader.ShaderType.Fragment); } /** * Returns the language of the vertex shader used in this technique. */ public String getVertexShaderLanguage() { - return shaderLanguage.get(Shader.ShaderType.Vertex); + return shaderLanguages.get(Shader.ShaderType.Vertex); } /**Returns the language for each shader program * @param shaderType */ public String getShaderProgramLanguage(Shader.ShaderType shaderType){ - return shaderLanguage.get(shaderType); + return shaderLanguages.get(shaderType); } /**Returns the name for each shader program * @param shaderType */ public String getShaderProgramName(Shader.ShaderType shaderType){ - return shaderName.get(shaderType); + return shaderNames.get(shaderType); } /** @@ -453,22 +452,21 @@ public class TechniqueDef implements Savable { OutputCapsule oc = ex.getCapsule(this); oc.write(name, "name", null); - oc.write(shaderName.get(Shader.ShaderType.Vertex), "vertName", null); - oc.write(shaderName.get(Shader.ShaderType.Fragment), "fragName", null); - oc.write(shaderName.get(Shader.ShaderType.Geometry), "geomName", null); - oc.write(shaderName.get(Shader.ShaderType.TessellationControl), "tsctrlName", null); - oc.write(shaderName.get(Shader.ShaderType.TessellationEvaluation), "tsevalName", null); - oc.write(shaderLanguage.get(Shader.ShaderType.Vertex), "vertLanguage", null); - oc.write(shaderLanguage.get(Shader.ShaderType.Fragment), "fragLanguage", null); - oc.write(shaderLanguage.get(Shader.ShaderType.Geometry), "geomLanguage", null); - oc.write(shaderLanguage.get(Shader.ShaderType.TessellationControl), "tsctrlLanguage", null); - oc.write(shaderLanguage.get(Shader.ShaderType.TessellationEvaluation), "tsevalLanguage", null); + oc.write(shaderNames.get(Shader.ShaderType.Vertex), "vertName", null); + oc.write(shaderNames.get(Shader.ShaderType.Fragment), "fragName", null); + oc.write(shaderNames.get(Shader.ShaderType.Geometry), "geomName", null); + oc.write(shaderNames.get(Shader.ShaderType.TessellationControl), "tsctrlName", null); + oc.write(shaderNames.get(Shader.ShaderType.TessellationEvaluation), "tsevalName", null); + oc.write(shaderLanguages.get(Shader.ShaderType.Vertex), "vertLanguage", null); + oc.write(shaderLanguages.get(Shader.ShaderType.Fragment), "fragLanguage", null); + oc.write(shaderLanguages.get(Shader.ShaderType.Geometry), "geomLanguage", null); + oc.write(shaderLanguages.get(Shader.ShaderType.TessellationControl), "tsctrlLanguage", null); + oc.write(shaderLanguages.get(Shader.ShaderType.TessellationEvaluation), "tsevalLanguage", null); oc.write(presetDefines, "presetDefines", null); oc.write(lightMode, "lightMode", LightMode.Disable); oc.write(shadowMode, "shadowMode", ShadowMode.Disable); oc.write(renderState, "renderState", null); - oc.write(usesShaders, "usesShaders", false); oc.write(usesNodes, "usesNodes", false); oc.writeSavableArrayList((ArrayList)shaderNodes,"shaderNodes", null); oc.write(shaderGenerationInfo, "shaderGenerationInfo", null); @@ -482,28 +480,27 @@ public class TechniqueDef implements Savable { public void read(JmeImporter im) throws IOException{ InputCapsule ic = im.getCapsule(this); name = ic.readString("name", null); - shaderName.put(Shader.ShaderType.Vertex,ic.readString("vertName", null)); - shaderName.put(Shader.ShaderType.Fragment,ic.readString("fragName", null)); - shaderName.put(Shader.ShaderType.Geometry,ic.readString("geomName", null)); - shaderName.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlName", null)); - shaderName.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalName", null)); + shaderNames.put(Shader.ShaderType.Vertex,ic.readString("vertName", null)); + shaderNames.put(Shader.ShaderType.Fragment,ic.readString("fragName", null)); + shaderNames.put(Shader.ShaderType.Geometry,ic.readString("geomName", null)); + shaderNames.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlName", null)); + shaderNames.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalName", null)); presetDefines = (DefineList) ic.readSavable("presetDefines", null); lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable); shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable); renderState = (RenderState) ic.readSavable("renderState", null); - usesShaders = ic.readBoolean("usesShaders", false); if (ic.getSavableVersion(TechniqueDef.class) == 0) { // Old version - shaderLanguage.put(Shader.ShaderType.Vertex,ic.readString("shaderLang", null)); - shaderLanguage.put(Shader.ShaderType.Fragment,shaderLanguage.get(Shader.ShaderType.Vertex)); + shaderLanguages.put(Shader.ShaderType.Vertex,ic.readString("shaderLang", null)); + shaderLanguages.put(Shader.ShaderType.Fragment,shaderLanguages.get(Shader.ShaderType.Vertex)); } else { // New version - shaderLanguage.put(Shader.ShaderType.Vertex,ic.readString("vertLanguage", null)); - shaderLanguage.put(Shader.ShaderType.Fragment,ic.readString("fragLanguage", null)); - shaderLanguage.put(Shader.ShaderType.Geometry,ic.readString("geomLanguage", null)); - shaderLanguage.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlLanguage", null)); - shaderLanguage.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalLanguage", null)); + shaderLanguages.put(Shader.ShaderType.Vertex,ic.readString("vertLanguage", null)); + shaderLanguages.put(Shader.ShaderType.Fragment,ic.readString("fragLanguage", null)); + shaderLanguages.put(Shader.ShaderType.Geometry,ic.readString("geomLanguage", null)); + shaderLanguages.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlLanguage", null)); + shaderLanguages.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalLanguage", null)); } usesNodes = ic.readBoolean("usesNodes", false); @@ -518,7 +515,6 @@ public class TechniqueDef implements Savable { public void setShaderNodes(List shaderNodes) { this.shaderNodes = shaderNodes; usesNodes = true; - usesShaders = true; } /** @@ -526,7 +522,7 @@ public class TechniqueDef implements Savable { * @return */ public EnumMap getShaderProgramNames() { - return shaderName; + return shaderNames; } /** @@ -534,7 +530,7 @@ public class TechniqueDef implements Savable { * @return */ public EnumMap getShaderProgramLanguages() { - return shaderLanguage; + return shaderLanguages; } public ShaderGenerationInfo getShaderGenerationInfo() { @@ -566,5 +562,4 @@ public class TechniqueDef implements Savable { public void setLightSpace(LightSpace lightSpace) { this.lightSpace = lightSpace; } - } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 9d080384a..9ae1c8cc7 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -1321,13 +1321,6 @@ public class GLRenderer implements Renderer { glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, prevFBO); - try { - checkFrameBufferError(); - } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, "Source FBO:\n{0}", src); - logger.log(Level.SEVERE, "Dest FBO:\n{0}", dst); - throw ex; - } } else { throw new RendererException("Framebuffer blitting not supported by the video hardware"); } @@ -1488,6 +1481,8 @@ public class GLRenderer implements Renderer { updateFrameBufferAttachment(fb, colorBuf); } + checkFrameBufferError(); + fb.clearUpdateNeeded(); } @@ -1645,8 +1640,6 @@ public class GLRenderer implements Renderer { assert context.boundFBO == fb.getId(); context.boundFB = fb; - - checkFrameBufferError(); } } @@ -2221,53 +2214,25 @@ public class GLRenderer implements Renderer { int usage = convertUsage(vb.getUsage()); vb.getData().rewind(); -// if (created || vb.hasDataSizeChanged()) { - // upload data based on format - switch (vb.getFormat()) { - case Byte: - case UnsignedByte: - gl.glBufferData(target, (ByteBuffer) vb.getData(), usage); - break; - // case Half: - case Short: - case UnsignedShort: - gl.glBufferData(target, (ShortBuffer) vb.getData(), usage); - break; - case Int: - case UnsignedInt: - glext.glBufferData(target, (IntBuffer) vb.getData(), usage); - break; - case Float: - gl.glBufferData(target, (FloatBuffer) vb.getData(), usage); - break; - default: - throw new UnsupportedOperationException("Unknown buffer format."); - } -// } else { -// // Invalidate buffer data (orphan) before uploading new data. -// switch (vb.getFormat()) { -// case Byte: -// case UnsignedByte: -// gl.glBufferSubData(target, 0, (ByteBuffer) vb.getData()); -// break; -// case Short: -// case UnsignedShort: -// gl.glBufferSubData(target, 0, (ShortBuffer) vb.getData()); -// break; -// case Int: -// case UnsignedInt: -// gl.glBufferSubData(target, 0, (IntBuffer) vb.getData()); -// break; -// case Float: -// gl.glBufferSubData(target, 0, (FloatBuffer) vb.getData()); -// break; -// case Double: -// gl.glBufferSubData(target, 0, (DoubleBuffer) vb.getData()); -// break; -// default: -// throw new UnsupportedOperationException("Unknown buffer format."); -// } -// } + switch (vb.getFormat()) { + case Byte: + case UnsignedByte: + gl.glBufferData(target, (ByteBuffer) vb.getData(), usage); + break; + case Short: + case UnsignedShort: + gl.glBufferData(target, (ShortBuffer) vb.getData(), usage); + break; + case Int: + case UnsignedInt: + glext.glBufferData(target, (IntBuffer) vb.getData(), usage); + break; + case Float: + gl.glBufferData(target, (FloatBuffer) vb.getData(), usage); + break; + default: + throw new UnsupportedOperationException("Unknown buffer format."); + } vb.clearUpdateNeeded(); } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java index 459e2d3a3..b69d524b4 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java @@ -70,6 +70,7 @@ public final class GLTracer implements InvocationHandler { // noEnumArgs("glTexParameteri", 2); noEnumArgs("glTexImage2D", 1, 3, 4, 5); noEnumArgs("glTexImage3D", 1, 3, 4, 5, 6); + noEnumArgs("glTexSubImage2D", 1, 2, 3, 4, 5); noEnumArgs("glTexSubImage3D", 1, 2, 3, 4, 5, 6, 7); noEnumArgs("glCompressedTexImage2D", 1, 3, 4, 5); noEnumArgs("glCompressedTexSubImage3D", 1, 2, 3, 4, 5, 6, 7); @@ -83,6 +84,8 @@ public final class GLTracer implements InvocationHandler { noEnumArgs("glDrawRangeElements", 1, 2, 3, 5); noEnumArgs("glDrawArrays", 1, 2); noEnumArgs("glDeleteBuffers", 0); + noEnumArgs("glBindVertexArray", 0); + noEnumArgs("glGenVertexArrays", 0); noEnumArgs("glBindFramebufferEXT", 1); noEnumArgs("glBindRenderbufferEXT", 1); @@ -110,6 +113,7 @@ public final class GLTracer implements InvocationHandler { noEnumArgs("glUniform1f", 0); noEnumArgs("glUniform2f", 0); noEnumArgs("glUniform3f", 0); + noEnumArgs("glUniform4", 0); noEnumArgs("glUniform4f", 0); noEnumArgs("glGetAttribLocation", 0, -1); noEnumArgs("glDetachShader", 0, 1); diff --git a/jme3-core/src/main/java/com/jme3/scene/BatchNode.java b/jme3-core/src/main/java/com/jme3/scene/BatchNode.java index 8cfed4244..475cfdf71 100644 --- a/jme3-core/src/main/java/com/jme3/scene/BatchNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/BatchNode.java @@ -64,7 +64,7 @@ import java.util.logging.Logger; * TODO more automagic (batch when needed in the updateLogicalState) * @author Nehon */ -public class BatchNode extends GeometryGroupNode implements Savable { +public class BatchNode extends GeometryGroupNode { private static final Logger logger = Logger.getLogger(BatchNode.class.getName()); /** diff --git a/jme3-core/src/main/java/com/jme3/scene/Node.java b/jme3-core/src/main/java/com/jme3/scene/Node.java index 816140e21..5edfa021b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Node.java +++ b/jme3-core/src/main/java/com/jme3/scene/Node.java @@ -58,7 +58,7 @@ import java.util.logging.Logger; * @author Gregg Patton * @author Joshua Slack */ -public class Node extends Spatial implements Savable { +public class Node extends Spatial { private static final Logger logger = Logger.getLogger(Node.class.getName()); diff --git a/jme3-core/src/main/java/com/jme3/scene/UserData.java b/jme3-core/src/main/java/com/jme3/scene/UserData.java index 4b5d70404..3047618fc 100644 --- a/jme3-core/src/main/java/com/jme3/scene/UserData.java +++ b/jme3-core/src/main/java/com/jme3/scene/UserData.java @@ -33,6 +33,12 @@ package com.jme3.scene; import com.jme3.export.*; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * UserData is used to contain user data objects @@ -48,28 +54,40 @@ public final class UserData implements Savable { * shape generation should ignore them. */ public static final String JME_PHYSICSIGNORE = "JmePhysicsIgnore"; - + /** * For geometries using shared mesh, this will specify the shared * mesh reference. */ - public static final String JME_SHAREDMESH = "JmeSharedMesh"; - - protected byte type; - protected Object value; + public static final String JME_SHAREDMESH = "JmeSharedMesh"; + + private static final int TYPE_INTEGER = 0; + private static final int TYPE_FLOAT = 1; + private static final int TYPE_BOOLEAN = 2; + private static final int TYPE_STRING = 3; + private static final int TYPE_LONG = 4; + private static final int TYPE_SAVABLE = 5; + private static final int TYPE_LIST = 6; + private static final int TYPE_MAP = 7; + private static final int TYPE_ARRAY = 8; + + protected byte type; + protected Object value; public UserData() { } /** - * Creates a new UserData with the given + * Creates a new UserData with the given * type and value. * - * @param type Type of data, should be between 0 and 4. - * @param value Value of the data + * @param type + * Type of data, should be between 0 and 8. + * @param value + * Value of the data */ public UserData(byte type, Object value) { - assert type >= 0 && type <= 4; + assert type >= 0 && type <= 8; this.type = type; this.value = value; } @@ -85,15 +103,23 @@ public final class UserData implements Savable { public static byte getObjectType(Object type) { if (type instanceof Integer) { - return 0; + return TYPE_INTEGER; } else if (type instanceof Float) { - return 1; + return TYPE_FLOAT; } else if (type instanceof Boolean) { - return 2; + return TYPE_BOOLEAN; } else if (type instanceof String) { - return 3; + return TYPE_STRING; } else if (type instanceof Long) { - return 4; + return TYPE_LONG; + } else if (type instanceof Savable) { + return TYPE_SAVABLE; + } else if (type instanceof List) { + return TYPE_LIST; + } else if (type instanceof Map) { + return TYPE_MAP; + } else if (type instanceof Object[]) { + return TYPE_ARRAY; } else { throw new IllegalArgumentException("Unsupported type: " + type.getClass().getName()); } @@ -101,56 +127,195 @@ public final class UserData implements Savable { public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); - oc.write(type, "type", (byte)0); + oc.write(type, "type", (byte) 0); switch (type) { - case 0: + case TYPE_INTEGER: int i = (Integer) value; oc.write(i, "intVal", 0); break; - case 1: + case TYPE_FLOAT: float f = (Float) value; oc.write(f, "floatVal", 0f); break; - case 2: + case TYPE_BOOLEAN: boolean b = (Boolean) value; oc.write(b, "boolVal", false); break; - case 3: + case TYPE_STRING: String s = (String) value; oc.write(s, "strVal", null); break; - case 4: + case TYPE_LONG: Long l = (Long) value; oc.write(l, "longVal", 0l); break; + case TYPE_SAVABLE: + Savable sav = (Savable) value; + oc.write(sav, "savableVal", null); + break; + case TYPE_LIST: + this.writeList(oc, (List) value, "0"); + break; + case TYPE_MAP: + Map map = (Map) value; + this.writeList(oc, map.keySet(), "0"); + this.writeList(oc, map.values(), "1"); + break; + case TYPE_ARRAY: + this.writeList(oc, Arrays.asList((Object[]) value), "0"); + break; default: - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("Unsupported value type: " + value.getClass()); } } public void read(JmeImporter im) throws IOException { InputCapsule ic = im.getCapsule(this); type = ic.readByte("type", (byte) 0); - switch (type) { - case 0: + case TYPE_INTEGER: value = ic.readInt("intVal", 0); break; - case 1: + case TYPE_FLOAT: value = ic.readFloat("floatVal", 0f); break; - case 2: + case TYPE_BOOLEAN: value = ic.readBoolean("boolVal", false); break; - case 3: + case TYPE_STRING: value = ic.readString("strVal", null); break; - case 4: + case TYPE_LONG: value = ic.readLong("longVal", 0l); break; + case TYPE_SAVABLE: + value = ic.readSavable("savableVal", null); + break; + case TYPE_LIST: + value = this.readList(ic, "0"); + break; + case TYPE_MAP: + Map map = new HashMap(); + List keys = this.readList(ic, "0"); + List values = this.readList(ic, "1"); + for (int i = 0; i < keys.size(); ++i) { + map.put(keys.get(i), values.get(i)); + } + value = map; + break; + case TYPE_ARRAY: + value = this.readList(ic, "0").toArray(); + break; default: - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("Unknown type of stored data: " + type); + } + } + + /** + * The method stores a list in the capsule. + * @param oc + * output capsule + * @param list + * the list to be stored + * @throws IOException + */ + private void writeList(OutputCapsule oc, Collection list, String listName) throws IOException { + if (list != null) { + oc.write(list.size(), listName + "size", 0); + int counter = 0; + for (Object o : list) { + // t is for 'type'; v is for 'value' + if (o instanceof Integer) { + oc.write(TYPE_INTEGER, listName + "t" + counter, 0); + oc.write((Integer) o, listName + "v" + counter, 0); + } else if (o instanceof Float) { + oc.write(TYPE_FLOAT, listName + "t" + counter, 0); + oc.write((Float) o, listName + "v" + counter, 0f); + } else if (o instanceof Boolean) { + oc.write(TYPE_BOOLEAN, listName + "t" + counter, 0); + oc.write((Boolean) o, listName + "v" + counter, false); + } else if (o instanceof String || o == null) {// treat null's like Strings just to store them and keep the List like the user intended + oc.write(TYPE_STRING, listName + "t" + counter, 0); + oc.write((String) o, listName + "v" + counter, null); + } else if (o instanceof Long) { + oc.write(TYPE_LONG, listName + "t" + counter, 0); + oc.write((Long) o, listName + "v" + counter, 0L); + } else if (o instanceof Savable) { + oc.write(TYPE_SAVABLE, listName + "t" + counter, 0); + oc.write((Savable) o, listName + "v" + counter, null); + } else if(o instanceof Object[]) { + oc.write(TYPE_ARRAY, listName + "t" + counter, 0); + this.writeList(oc, Arrays.asList((Object[]) o), listName + "v" + counter); + } else if(o instanceof List) { + oc.write(TYPE_LIST, listName + "t" + counter, 0); + this.writeList(oc, (List) o, listName + "v" + counter); + } else if(o instanceof Map) { + oc.write(TYPE_MAP, listName + "t" + counter, 0); + Map map = (Map) o; + this.writeList(oc, map.keySet(), listName + "v(keys)" + counter); + this.writeList(oc, map.values(), listName + "v(vals)" + counter); + } else { + throw new UnsupportedOperationException("Unsupported type stored in the list: " + o.getClass()); + } + + ++counter; + } + } else { + oc.write(0, "size", 0); + } + } + + /** + * The method loads a list from the given input capsule. + * @param ic + * the input capsule + * @return loaded list (an empty list in case its size is 0) + * @throws IOException + */ + private List readList(InputCapsule ic, String listName) throws IOException { + int size = ic.readInt(listName + "size", 0); + List list = new ArrayList(size); + for (int i = 0; i < size; ++i) { + int type = ic.readInt(listName + "t" + i, 0); + switch (type) { + case TYPE_INTEGER: + list.add(ic.readInt(listName + "v" + i, 0)); + break; + case TYPE_FLOAT: + list.add(ic.readFloat(listName + "v" + i, 0)); + break; + case TYPE_BOOLEAN: + list.add(ic.readBoolean(listName + "v" + i, false)); + break; + case TYPE_STRING: + list.add(ic.readString(listName + "v" + i, null)); + break; + case TYPE_LONG: + list.add(ic.readLong(listName + "v" + i, 0L)); + break; + case TYPE_SAVABLE: + list.add(ic.readSavable(listName + "v" + i, null)); + break; + case TYPE_ARRAY: + list.add(this.readList(ic, listName + "v" + i).toArray()); + break; + case TYPE_LIST: + list.add(this.readList(ic, listName + "v" + i)); + break; + case TYPE_MAP: + Map map = new HashMap(); + List keys = this.readList(ic, listName + "v(keys)" + i); + List values = this.readList(ic, listName + "v(vals)" + i); + for (int j = 0; j < keys.size(); ++j) { + map.put(keys.get(j), values.get(j)); + } + list.add(map); + break; + default: + throw new UnsupportedOperationException("Unknown type of stored data in a list: " + type); + } } + return list; } } diff --git a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java index b3cb26da0..554b8606a 100644 --- a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java @@ -57,7 +57,7 @@ public class InstancedNode extends GeometryGroupNode { setGeometryStartIndex(geom, startIndex); } - private static class InstanceTypeKey implements Cloneable { + private static final class InstanceTypeKey implements Cloneable { Mesh mesh; Material material; diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Dome.java b/jme3-core/src/main/java/com/jme3/scene/shape/Dome.java index 3e2cfb031..8060498f9 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Dome.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Dome.java @@ -266,7 +266,7 @@ public class Dome extends Mesh { vars.release(); // pole - vb.put(center.x).put(center.y + radius).put(center.z); + vb.put(this.center.x).put(this.center.y + radius).put(this.center.z); nb.put(0).put(insideView ? -1 : 1).put(0); tb.put(0.5f).put(1.0f); diff --git a/jme3-core/src/main/java/com/jme3/shader/DefineList.java b/jme3-core/src/main/java/com/jme3/shader/DefineList.java index 93d4cbeaf..dd605fc7e 100644 --- a/jme3-core/src/main/java/com/jme3/shader/DefineList.java +++ b/jme3-core/src/main/java/com/jme3/shader/DefineList.java @@ -40,7 +40,7 @@ import java.io.IOException; import java.util.Map; import java.util.TreeMap; -public class DefineList implements Savable, Cloneable { +public final class DefineList implements Savable, Cloneable { private static final String ONE = "1"; diff --git a/jme3-core/src/main/java/com/jme3/system/NullContext.java b/jme3-core/src/main/java/com/jme3/system/NullContext.java index f7d4b3d5a..41204e6c9 100644 --- a/jme3-core/src/main/java/com/jme3/system/NullContext.java +++ b/jme3-core/src/main/java/com/jme3/system/NullContext.java @@ -46,6 +46,8 @@ public class NullContext implements JmeContext, Runnable { protected static final Logger logger = Logger.getLogger(NullContext.class.getName()); + protected static final String THREAD_NAME = "jME3 Headless Main"; + protected AtomicBoolean created = new AtomicBoolean(false); protected AtomicBoolean needClose = new AtomicBoolean(false); protected final Object createdLock = new Object(); @@ -150,7 +152,7 @@ public class NullContext implements JmeContext, Runnable { return; } - new Thread(this, "Headless Application Thread").start(); + new Thread(this, THREAD_NAME).start(); if (waitFor) waitFor(true); } diff --git a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java b/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java index ed053339c..bc21fd82b 100644 --- a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java +++ b/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java @@ -72,9 +72,10 @@ import java.util.ArrayList; * @author Kirill Vainer */ public class FrameBuffer extends NativeObject { - public static int SLOT_UNDEF = -1; - public static int SLOT_DEPTH = -100; - public static int SLOT_DEPTH_STENCIL = -101; + + public static final int SLOT_UNDEF = -1; + public static final int SLOT_DEPTH = -100; + public static final int SLOT_DEPTH_STENCIL = -101; private int width = 0; private int height = 0; diff --git a/jme3-core/src/main/java/com/jme3/texture/Texture.java b/jme3-core/src/main/java/com/jme3/texture/Texture.java index b41d2ac10..582d06565 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Texture.java +++ b/jme3-core/src/main/java/com/jme3/texture/Texture.java @@ -488,7 +488,8 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable /** * @return the anisotropic filtering level for this texture. Default value - * is 1 (no anisotrophy), 2 means x2, 4 is x4, etc. + * is 0 (use value from config), + * 1 means 1x (no anisotrophy), 2 means x2, 4 is x4, etc. */ public int getAnisotropicFilter() { return anisotropicFilter; @@ -499,11 +500,7 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable * the anisotropic filtering level for this texture. */ public void setAnisotropicFilter(int level) { - if (level < 1) { - anisotropicFilter = 1; - } else { - anisotropicFilter = level; - } + anisotropicFilter = Math.max(0, level); } @Override diff --git a/jme3-core/src/main/java/com/jme3/util/IntMap.java b/jme3-core/src/main/java/com/jme3/util/IntMap.java index fed91be06..38ffb2431 100644 --- a/jme3-core/src/main/java/com/jme3/util/IntMap.java +++ b/jme3-core/src/main/java/com/jme3/util/IntMap.java @@ -34,6 +34,7 @@ package com.jme3.util; import com.jme3.util.IntMap.Entry; import java.util.Iterator; import java.util.Map; +import java.util.NoSuchElementException; /** * Similar to a {@link Map} except that ints are used as keys. @@ -234,7 +235,7 @@ public final class IntMap implements Iterable>, Cloneable { public Entry next() { if (el >= size) - throw new IllegalStateException("No more elements!"); + throw new NoSuchElementException("No more elements!"); if (cur != null){ Entry e = cur; diff --git a/jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java b/jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java index ae82b3422..0f9aac1fc 100644 --- a/jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java +++ b/jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java @@ -274,11 +274,9 @@ public class TangentBinormalGenerator { triData.setIndex(index); triData.triangleOffset = i * 3 ; } - if (triData != null) { - vertices.get(index[0]).triangles.add(triData); - vertices.get(index[1]).triangles.add(triData); - vertices.get(index[2]).triangles.add(triData); - } + vertices.get(index[0]).triangles.add(triData); + vertices.get(index[1]).triangles.add(triData); + vertices.get(index[2]).triangles.add(triData); } return vertices; @@ -483,7 +481,7 @@ public class TangentBinormalGenerator { boolean isDegenerate = isDegenerateTriangle(v[0], v[1], v[2]); TriangleData triData = processTriangle(index, v, t); - if (triData != null && !isDegenerate) { + if (!isDegenerate) { vertices.get(index[0]).triangles.add(triData); vertices.get(index[1]).triangles.add(triData); vertices.get(index[2]).triangles.add(triData); @@ -529,11 +527,9 @@ public class TangentBinormalGenerator { populateFromBuffer(t[2], textureBuffer, index[2]); TriangleData triData = processTriangle(index, v, t); - if (triData != null) { - vertices.get(index[0]).triangles.add(triData); - vertices.get(index[1]).triangles.add(triData); - vertices.get(index[2]).triangles.add(triData); - } + vertices.get(index[0]).triangles.add(triData); + vertices.get(index[1]).triangles.add(triData); + vertices.get(index[2]).triangles.add(triData); Vector3f vTemp = v[1]; v[1] = v[2]; diff --git a/jme3-core/src/main/java/com/jme3/util/blockparser/BlockLanguageParser.java b/jme3-core/src/main/java/com/jme3/util/blockparser/BlockLanguageParser.java index 16968aad2..7718a0f4b 100644 --- a/jme3-core/src/main/java/com/jme3/util/blockparser/BlockLanguageParser.java +++ b/jme3-core/src/main/java/com/jme3/util/blockparser/BlockLanguageParser.java @@ -71,7 +71,7 @@ public class BlockLanguageParser { private void load(InputStream in) throws IOException{ reset(); - reader = new InputStreamReader(in); + reader = new InputStreamReader(in, "UTF-8"); StringBuilder buffer = new StringBuilder(); boolean insideComment = false; diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert index 3ee6f38c6..f92b91fff 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert @@ -126,7 +126,8 @@ void main(){ DiffuseSum = m_Diffuse * vec4(lightColor.rgb, 1.0); SpecularSum = (m_Specular * lightColor).rgb; #else - AmbientSum = g_AmbientLightColor.rgb; // Default: ambient color is dark gray + // Defaults: Ambient and diffuse are white, specular is black. + AmbientSum = g_AmbientLightColor.rgb; DiffuseSum = vec4(lightColor.rgb, 1.0); SpecularSum = vec3(0.0); #endif diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert index b0d8828a5..62b206b7e 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert @@ -117,6 +117,7 @@ void main(){ SpecularSum = m_Specular.rgb; DiffuseSum = m_Diffuse; #else + // Defaults: Ambient and diffuse are white, specular is black. AmbientSum = g_AmbientLightColor.rgb; SpecularSum = vec3(0.0); DiffuseSum = vec4(1.0); diff --git a/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib index 6ac1fce5c..fb8f40524 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib @@ -30,6 +30,14 @@ float computeSpotFalloff(in vec4 lightDirection, in vec3 lightVector){ float innerAngleCos = floor(lightDirection.w) * 0.001; float outerAngleCos = fract(lightDirection.w); float innerMinusOuter = innerAngleCos - outerAngleCos; - return clamp((curAngleCos - outerAngleCos) / innerMinusOuter, step(lightDirection.w, 0.001), 1.0); + float falloff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, step(lightDirection.w, 0.001), 1.0); + +#ifdef SRGB + // Use quadratic falloff (notice the ^4) + return pow(clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0), 4.0); +#else + // Use linear falloff + return falloff; +#endif } diff --git a/jme3-core/src/plugins/java/com/jme3/audio/plugins/WAVLoader.java b/jme3-core/src/plugins/java/com/jme3/audio/plugins/WAVLoader.java index 098de7a15..997a8f4fb 100644 --- a/jme3-core/src/plugins/java/com/jme3/audio/plugins/WAVLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/audio/plugins/WAVLoader.java @@ -196,7 +196,7 @@ public class WAVLoader implements AssetLoader { break; case i_data: // Compute duration based on data chunk size - duration = len / bytesPerSec; + duration = (float)(len / bytesPerSec); if (readStream) { readDataChunkForStream(inOffset, len); diff --git a/jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java b/jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java index 933bee721..b34adbca6 100644 --- a/jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java +++ b/jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java @@ -270,9 +270,7 @@ public final class BinaryImporter implements JmeImporter { try { return load(fis, listener); } finally { - if (fis != null) { - fis.close(); - } + fis.close(); } } diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java index 1f4523d4c..90b0b7f1f 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java @@ -186,6 +186,7 @@ public class J3MLoader implements AssetLoader { tex.setWrap(WrapMode.Repeat); } tex.setKey(texKey); + tex.setName(texKey.getName()); } return tex; }else{ diff --git a/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java b/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java index 9466b4a50..ccaecc580 100644 --- a/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java +++ b/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java @@ -71,7 +71,7 @@ public class VideoRecorderAppState extends AbstractAppState { public Thread newThread(Runnable r) { Thread th = new Thread(r); - th.setName("jME Video Processing Thread"); + th.setName("jME3 Video Processor"); th.setDaemon(true); return th; } diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestSweepTest.java b/jme3-examples/src/main/java/jme3test/bullet/TestSweepTest.java index 6f9e3fdd6..0613e5d22 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestSweepTest.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestSweepTest.java @@ -21,8 +21,8 @@ import java.util.List; public class TestSweepTest extends SimpleApplication { private BulletAppState bulletAppState = new BulletAppState(); - private CapsuleCollisionShape obstacleCollisionShape = new CapsuleCollisionShape(0.3f, 0.5f); - private CapsuleCollisionShape capsuleCollisionShape = new CapsuleCollisionShape(1f, 1f); + private CapsuleCollisionShape obstacleCollisionShape; + private CapsuleCollisionShape capsuleCollisionShape; private Node capsule; private Node obstacle; private float dist = .5f; @@ -33,6 +33,9 @@ public class TestSweepTest extends SimpleApplication { @Override public void simpleInitApp() { + obstacleCollisionShape = new CapsuleCollisionShape(0.3f, 0.5f); + capsuleCollisionShape = new CapsuleCollisionShape(1f, 1f); + stateManager.attach(bulletAppState); capsule = new Node("capsule"); @@ -56,14 +59,19 @@ public class TestSweepTest extends SimpleApplication { public void simpleUpdate(float tpf) { float move = tpf * 1; + boolean colliding = false; List sweepTest = bulletAppState.getPhysicsSpace().sweepTest(capsuleCollisionShape, new Transform(capsule.getWorldTranslation()), new Transform(capsule.getWorldTranslation().add(dist, 0, 0))); - if (sweepTest.size() > 0) { - PhysicsSweepTestResult get = sweepTest.get(0); - PhysicsCollisionObject collisionObject = get.getCollisionObject(); - fpsText.setText("Almost colliding with " + collisionObject.getUserObject().toString()); - } else { + for (PhysicsSweepTestResult result : sweepTest) { + if (result.getCollisionObject().getCollisionShape() != capsuleCollisionShape) { + PhysicsCollisionObject collisionObject = result.getCollisionObject(); + fpsText.setText("Almost colliding with " + collisionObject.getUserObject().toString()); + colliding = true; + } + } + + if (!colliding) { // if the sweep is clear then move the spatial capsule.move(move, 0, 0); } diff --git a/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java b/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java index d6b7010f4..8a59d0be8 100644 --- a/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java +++ b/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java @@ -759,14 +759,6 @@ public class IGLESShaderRenderer implements Renderer { Image[] textures = context.boundTextures; int type = convertTextureType(tex.getType()); - if (!context.textureIndexList.moveToNew(unit)) { -// if (context.boundTextureUnit != unit){ -// glActiveTexture(GL_TEXTURE0 + unit); -// context.boundTextureUnit = unit; -// } -// glEnable(type); - } - if (textures[unit] != image) { if (context.boundTextureUnit != unit) { JmeIosGLES.glActiveTexture(JmeIosGLES.GL_TEXTURE0 + unit); @@ -1768,7 +1760,6 @@ public class IGLESShaderRenderer implements Renderer { JmeIosGLES.checkGLError(); } clearVertexAttribs(); - clearTextureUnits(); } private void renderMeshDefault(Mesh mesh, int lod, int count) { @@ -1807,7 +1798,6 @@ public class IGLESShaderRenderer implements Renderer { JmeIosGLES.checkGLError(); } clearVertexAttribs(); - clearTextureUnits(); } @@ -2085,23 +2075,6 @@ public class IGLESShaderRenderer implements Renderer { context.attribIndexList.copyNewToOld(); } - - public void clearTextureUnits() { - IDList textureList = context.textureIndexList; - Image[] textures = context.boundTextures; - for (int i = 0; i < textureList.oldLen; i++) { - int idx = textureList.oldList[i]; -// if (context.boundTextureUnit != idx){ -// glActiveTexture(GL_TEXTURE0 + idx); -// context.boundTextureUnit = idx; -// } -// glDisable(convertTextureType(textures[idx].getType())); - textures[idx] = null; - } - context.textureIndexList.copyNewToOld(); - } - - public void updateFrameBuffer(FrameBuffer fb) { int id = fb.getId(); if (id == -1) { diff --git a/jme3-jogl/build.gradle b/jme3-jogl/build.gradle index 135656ee6..e82139729 100644 --- a/jme3-jogl/build.gradle +++ b/jme3-jogl/build.gradle @@ -5,7 +5,7 @@ if (!hasProperty('mainClass')) { dependencies { compile project(':jme3-core') compile project(':jme3-desktop') - compile 'org.jogamp.gluegen:gluegen-rt-main:2.2.0' - compile 'org.jogamp.jogl:jogl-all-main:2.2.0' - compile 'org.jogamp.joal:joal-main:2.2.0' + compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.1' + compile 'org.jogamp.jogl:jogl-all-main:2.3.1' + compile 'org.jogamp.joal:joal-main:2.3.1' } diff --git a/jme3-jogl/src/main/java/com/jme3/input/jogl/NewtMouseInput.java b/jme3-jogl/src/main/java/com/jme3/input/jogl/NewtMouseInput.java index d50017ccc..773f62b8a 100644 --- a/jme3-jogl/src/main/java/com/jme3/input/jogl/NewtMouseInput.java +++ b/jme3-jogl/src/main/java/com/jme3/input/jogl/NewtMouseInput.java @@ -45,11 +45,11 @@ import com.jogamp.newt.opengl.GLWindow; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.logging.Logger; -import javax.media.nativewindow.util.Dimension; -import javax.media.nativewindow.util.DimensionImmutable; -import javax.media.nativewindow.util.PixelFormat; -import javax.media.nativewindow.util.PixelRectangle; -import javax.media.nativewindow.util.Point; +import com.jogamp.nativewindow.util.Dimension; +import com.jogamp.nativewindow.util.DimensionImmutable; +import com.jogamp.nativewindow.util.PixelFormat; +import com.jogamp.nativewindow.util.PixelRectangle; +import com.jogamp.nativewindow.util.Point; public class NewtMouseInput implements MouseInput, MouseListener { diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java index 85e5c7102..111719265 100644 --- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java @@ -70,15 +70,15 @@ import java.util.EnumSet; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import javax.media.nativewindow.NativeWindowFactory; -import javax.media.opengl.GL; -import javax.media.opengl.GL2; -import javax.media.opengl.GL2ES1; -import javax.media.opengl.GL2ES2; -import javax.media.opengl.GL2ES3; -import javax.media.opengl.GL2GL3; -import javax.media.opengl.GL3; -import javax.media.opengl.GLContext; +import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2; +import com.jogamp.opengl.GL2ES1; +import com.jogamp.opengl.GL2ES2; +import com.jogamp.opengl.GL2ES3; +import com.jogamp.opengl.GL2GL3; +import com.jogamp.opengl.GL3; +import com.jogamp.opengl.GLContext; import jme3tools.converters.MipMapGenerator; import jme3tools.shader.ShaderDebug; @@ -1800,7 +1800,7 @@ public class JoglRenderer implements Renderer { if (samples > 1) { return GL3.GL_TEXTURE_2D_MULTISAMPLE_ARRAY; } else { - return GL.GL_TEXTURE_2D_ARRAY; + return GL2ES3.GL_TEXTURE_2D_ARRAY; } case ThreeDimensional: return GL2ES2.GL_TEXTURE_3D; @@ -2014,7 +2014,7 @@ public class JoglRenderer implements Renderer { for (int i = 0; i < 6; i++) { TextureUtil.uploadTexture(img, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, linearizeSrgbImages); } - } else if (target == GL.GL_TEXTURE_2D_ARRAY) { + } else if (target == GL2ES3.GL_TEXTURE_2D_ARRAY) { if (!caps.contains(Caps.TextureArray)) { throw new RendererException("Texture arrays not supported by graphics hardware"); } diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java index 9bdc0ca2d..8f3c5b156 100644 --- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java @@ -36,14 +36,17 @@ import com.jme3.renderer.RendererException; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; import com.jme3.texture.image.ColorSpace; + import java.nio.ByteBuffer; import java.util.logging.Level; import java.util.logging.Logger; -import javax.media.opengl.GL; -import javax.media.opengl.GL2; -import javax.media.opengl.GL2ES2; -import javax.media.opengl.GL2GL3; -import javax.media.opengl.GLContext; + +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2; +import com.jogamp.opengl.GL2ES2; +import com.jogamp.opengl.GL2ES3; +import com.jogamp.opengl.GL2GL3; +import com.jogamp.opengl.GLContext; public class TextureUtil { @@ -124,9 +127,9 @@ public class TextureUtil { setFormat(Format.RGB32F, GL.GL_RGB32F, GL.GL_RGB, GL.GL_FLOAT, false); // Special RGB formats - setFormat(Format.RGB111110F, GL.GL_R11F_G11F_B10F, GL.GL_RGB, GL.GL_UNSIGNED_INT_10F_11F_11F_REV, false); + setFormat(Format.RGB111110F, GL2ES3.GL_R11F_G11F_B10F, GL.GL_RGB, GL.GL_UNSIGNED_INT_10F_11F_11F_REV, false); setFormat(Format.RGB9E5, GL2GL3.GL_RGB9_E5, GL.GL_RGB, GL2GL3.GL_UNSIGNED_INT_5_9_9_9_REV, false); - setFormat(Format.RGB16F_to_RGB111110F, GL.GL_R11F_G11F_B10F, GL.GL_RGB, GL.GL_HALF_FLOAT, false); + setFormat(Format.RGB16F_to_RGB111110F, GL2ES3.GL_R11F_G11F_B10F, GL.GL_RGB, GL.GL_HALF_FLOAT, false); setFormat(Format.RGB16F_to_RGB9E5, GL2.GL_RGB9_E5, GL.GL_RGB, GL.GL_HALF_FLOAT, false); // RGBA formats @@ -346,7 +349,7 @@ public class TextureUtil { glFmt.format, glFmt.dataType, data); - }else if (target == GL.GL_TEXTURE_2D_ARRAY){ + }else if (target == GL2ES3.GL_TEXTURE_2D_ARRAY){ // prepare data for 2D array // or upload slice if (index == -1){ diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java index 8c6ec6e55..a7f7fbfd7 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java @@ -45,20 +45,20 @@ import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; -import javax.media.opengl.DebugGL2; -import javax.media.opengl.DebugGL3; -import javax.media.opengl.DebugGL3bc; -import javax.media.opengl.DebugGL4; -import javax.media.opengl.DebugGL4bc; -import javax.media.opengl.DebugGLES1; -import javax.media.opengl.DebugGLES2; -import javax.media.opengl.GL; -import javax.media.opengl.GLAutoDrawable; -import javax.media.opengl.GLCapabilities; -import javax.media.opengl.GLEventListener; -import javax.media.opengl.GLProfile; -import javax.media.opengl.GLRunnable; -import javax.media.opengl.awt.GLCanvas; +import com.jogamp.opengl.DebugGL2; +import com.jogamp.opengl.DebugGL3; +import com.jogamp.opengl.DebugGL3bc; +import com.jogamp.opengl.DebugGL4; +import com.jogamp.opengl.DebugGL4bc; +import com.jogamp.opengl.DebugGLES1; +import com.jogamp.opengl.DebugGLES2; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLEventListener; +import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.GLRunnable; +import com.jogamp.opengl.awt.GLCanvas; public abstract class JoglAbstractDisplay extends JoglContext implements GLEventListener { diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java index c4e97570e..9a409b85e 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java @@ -35,7 +35,7 @@ package com.jme3.system.jogl; import com.jme3.system.JmeCanvasContext; import java.awt.Canvas; import java.util.logging.Logger; -import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLAutoDrawable; public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext { diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java index eca36d84b..5d687af8d 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java @@ -47,14 +47,16 @@ import java.nio.IntBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; -import javax.media.opengl.GL; -import javax.media.opengl.GL2GL3; -import javax.media.opengl.GLContext; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2GL3; +import com.jogamp.opengl.GLContext; public abstract class JoglContext implements JmeContext { private static final Logger logger = Logger.getLogger(JoglContext.class.getName()); + protected static final String THREAD_NAME = "jME3 Main"; + protected AtomicBoolean created = new AtomicBoolean(false); protected AtomicBoolean renderable = new AtomicBoolean(false); protected final Object createdLock = new Object(); diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglDisplay.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglDisplay.java index 363ccd096..3bac0ab53 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglDisplay.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglDisplay.java @@ -45,7 +45,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; -import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLAutoDrawable; import javax.swing.JFrame; import javax.swing.SwingUtilities; diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java index 4899c9c39..cb75d5d37 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java @@ -44,19 +44,19 @@ import com.jogamp.opengl.util.AnimatorBase; import com.jogamp.opengl.util.FPSAnimator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; -import javax.media.opengl.DebugGL2; -import javax.media.opengl.DebugGL3; -import javax.media.opengl.DebugGL3bc; -import javax.media.opengl.DebugGL4; -import javax.media.opengl.DebugGL4bc; -import javax.media.opengl.DebugGLES1; -import javax.media.opengl.DebugGLES2; -import javax.media.opengl.GL; -import javax.media.opengl.GLAutoDrawable; -import javax.media.opengl.GLCapabilities; -import javax.media.opengl.GLEventListener; -import javax.media.opengl.GLProfile; -import javax.media.opengl.GLRunnable; +import com.jogamp.opengl.DebugGL2; +import com.jogamp.opengl.DebugGL3; +import com.jogamp.opengl.DebugGL3bc; +import com.jogamp.opengl.DebugGL4; +import com.jogamp.opengl.DebugGL4bc; +import com.jogamp.opengl.DebugGLES1; +import com.jogamp.opengl.DebugGLES2; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLEventListener; +import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.GLRunnable; public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLEventListener { diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java index e4f46d8fb..3ed501580 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java @@ -35,7 +35,7 @@ package com.jme3.system.jogl; import com.jme3.system.JmeCanvasContext; import com.jogamp.newt.awt.NewtCanvasAWT; import java.util.logging.Logger; -import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLAutoDrawable; public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvasContext { diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java index 011002aff..84a99e8d2 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java @@ -42,8 +42,8 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; -import javax.media.nativewindow.util.Dimension; -import javax.media.opengl.GLAutoDrawable; +import com.jogamp.nativewindow.util.Dimension; +import com.jogamp.opengl.GLAutoDrawable; public class JoglNewtDisplay extends JoglNewtAbstractDisplay { diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java index 505808878..cd2b84f73 100644 --- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java +++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java @@ -41,12 +41,12 @@ import com.jogamp.newt.NewtVersion; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; -import javax.media.opengl.GL; -import javax.media.opengl.GLCapabilities; -import javax.media.opengl.GLContext; -import javax.media.opengl.GLDrawableFactory; -import javax.media.opengl.GLOffscreenAutoDrawable; -import javax.media.opengl.GLProfile; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLContext; +import com.jogamp.opengl.GLDrawableFactory; +import com.jogamp.opengl.GLOffscreenAutoDrawable; +import com.jogamp.opengl.GLProfile; public class JoglOffscreenBuffer extends JoglContext implements Runnable { @@ -146,7 +146,7 @@ public class JoglOffscreenBuffer extends JoglContext implements Runnable { return; } - new Thread(this, "JOGL Renderer Thread").start(); + new Thread(this, THREAD_NAME).start(); if (waitFor) { waitFor(true); } diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java index ee6a81c53..7cdca0a57 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java @@ -181,7 +181,9 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna // check input after we synchronize with framerate. // this reduces input lag. - Display.processMessages(); + if (renderable.get()){ + Display.processMessages(); + } // Subclasses just call GLObjectManager clean up objects here // it is safe .. for now. diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java index f1fcc34a2..2452e470b 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java @@ -96,7 +96,7 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex canvas.setFocusable(true); canvas.setIgnoreRepaint(true); - renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread"); + renderThread = new Thread(LwjglCanvas.this, THREAD_NAME); renderThread.start(); }else if (needClose.get()){ return; @@ -162,7 +162,7 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex if (renderThread == null){ logger.log(Level.FINE, "MAIN: Creating OGL thread."); - renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread"); + renderThread = new Thread(LwjglCanvas.this, THREAD_NAME); renderThread.start(); } // do not do anything. diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java index e318d24a2..1286323ef 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java @@ -67,6 +67,8 @@ public abstract class LwjglContext implements JmeContext { private static final Logger logger = Logger.getLogger(LwjglContext.class.getName()); + protected static final String THREAD_NAME = "jME3 Main"; + protected AtomicBoolean created = new AtomicBoolean(false); protected AtomicBoolean renderable = new AtomicBoolean(false); protected final Object createdLock = new Object(); diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java index 4ecfc9a49..853e02933 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java @@ -166,7 +166,7 @@ public class LwjglDisplay extends LwjglAbstractDisplay { return; } - new Thread(this, "LWJGL Renderer Thread").start(); + new Thread(this, THREAD_NAME).start(); if (waitFor) waitFor(true); } diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java index efdb5dfdf..b06db1b29 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java +++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java @@ -170,7 +170,7 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable { return; } - new Thread(this, "LWJGL Renderer Thread").start(); + new Thread(this, THREAD_NAME).start(); if (waitFor) waitFor(true); } diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java index 97f46120a..9dd831020 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java @@ -55,6 +55,7 @@ import com.jme3.texture.Image.Format; import com.jme3.texture.Texture.MagFilter; import com.jme3.texture.Texture.MinFilter; import com.jme3.texture.Texture2D; +import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import de.lessvoid.nifty.render.batch.spi.BatchRenderBackend; @@ -302,7 +303,7 @@ public class JmeBatchRenderBackend implements BatchRenderBackend { initialData.rewind(); modifyTexture( getTextureAtlas(atlasTextureId), - new com.jme3.texture.Image(Format.RGBA8, image.getWidth(), image.getHeight(), initialData), + new com.jme3.texture.Image(Format.RGBA8, image.getWidth(), image.getHeight(), initialData, ColorSpace.sRGB), x, y); } @@ -338,7 +339,7 @@ public class JmeBatchRenderBackend implements BatchRenderBackend { } initialData.rewind(); - Texture2D texture = new Texture2D(new com.jme3.texture.Image(Format.RGBA8, width, height, initialData)); + Texture2D texture = new Texture2D(new com.jme3.texture.Image(Format.RGBA8, width, height, initialData, ColorSpace.sRGB)); texture.setMinFilter(MinFilter.NearestNoMipMaps); texture.setMagFilter(MagFilter.Nearest); return texture; diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java index f52eaee01..8ad2425ad 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java @@ -137,7 +137,7 @@ public class TerrainLodControl extends AbstractControl { return Executors.newSingleThreadExecutor(new ThreadFactory() { public Thread newThread(Runnable r) { Thread th = new Thread(r); - th.setName("jME Terrain Thread"); + th.setName("jME3 Terrain Thread"); th.setDaemon(true); return th; } diff --git a/jme3-testdata/build.gradle b/jme3-testdata/build.gradle index 1df94c4d2..aad9f2ae9 100644 --- a/jme3-testdata/build.gradle +++ b/jme3-testdata/build.gradle @@ -2,5 +2,12 @@ if (!hasProperty('mainClass')) { ext.mainClass = '' } +repositories { + maven { + url 'http://nifty-gui.sourceforge.net/nifty-maven-repo' + } +} + dependencies { + compile 'lessvoid:nifty-examples:1.4.1' } diff --git a/sdk/build.gradle b/sdk/build.gradle index 55722cbfb..745b878c6 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -25,17 +25,18 @@ dependencies { corelibs project(':jme3-niftygui') corelibs project(':jme3-plugins') corelibs project(':jme3-terrain') - + optlibs project(':jme3-bullet') optlibs project(':jme3-jogl') optlibs project(':jme3-android') optlibs project(':jme3-ios') optlibs project(':jme3-android-native') optlibs project(':jme3-bullet-native') + optlibs project(':jme3-bullet-native-android') testdatalibs project(':jme3-testdata') examplelibs project(':jme3-examples') - + } artifacts { @@ -97,7 +98,7 @@ task createBaseXml(dependsOn: configurations.corelibs) <<{ "jme3-core-baselibs and jme3-core-libraries" def jmeJarFiles = [] // jme3 jar files def externalJarFiles = [] // external jar files - + // collect jar files project.configurations.corelibs.dependencies.each {dep -> // collect external jar files @@ -120,7 +121,7 @@ task createBaseXml(dependsOn: configurations.corelibs) <<{ def packages = [] jmeJarFiles.each{jarFile -> ZipFile file = new ZipFile(jarFile) - file.entries().each { entry -> + file.entries().each { entry -> if(entry.name.endsWith('.class')){ // TODO: "/" works on windows? def pathPart = entry.name.substring(0,entry.name.lastIndexOf('/')) @@ -129,14 +130,14 @@ task createBaseXml(dependsOn: configurations.corelibs) <<{ packages.add(classPath) } } - } + } } - + // collect library packages def extPackages = [] externalJarFiles.each{jarFile -> ZipFile file = new ZipFile(jarFile) - file.entries().each { entry -> + file.entries().each { entry -> if(entry.name.endsWith('.class')){ // TODO: "/" works on windows? def pathPart = entry.name.substring(0,entry.name.lastIndexOf('/')) @@ -145,9 +146,9 @@ task createBaseXml(dependsOn: configurations.corelibs) <<{ extPackages.add(classPath) } } - } + } } - + def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.mkp.xmlDeclaration(version:'1.0') @@ -252,7 +253,7 @@ task copyProjectLibs(dependsOn: [configurations.corelibs, configurations.testdat into "jme3-project-libraries/release/libs/" } } - + project.configurations.testdatalibs.dependencies.each {dep -> // copy jme3 test data to jme3-project-testdata dep.dependencyProject.configurations.archives.allArtifacts.each{ artifact-> @@ -281,10 +282,10 @@ def makeFile(builder, nameR) { builder.file(name:nameR, url:nameR) } task createProjectXml(dependsOn: configurations.corelibs) <<{ description "Creates needed J2SE library and layer XML files in jme3-project-baselibs" - - def eol = System.properties.'line.separator' + + def eol = System.properties.'line.separator' def j2seLibraries = [] // created J2SE library descriptors - + // for each dependency in corelibs.. def deps = [] deps.addAll(project.configurations.corelibs.dependencies) @@ -315,7 +316,7 @@ task createProjectXml(dependsOn: configurations.corelibs) <<{ def libraryWriter = new StringWriter() def libraryXml = new MarkupBuilder(libraryWriter) // xml.mkp.xmlDeclaration(version:'1.0') - libraryWriter << '' << eol + libraryWriter << '' << eol libraryWriter << '' << eol libraryXml.library(version:"1.0", encoding: "UTF-8"){ makeName(libraryXml, "${dep.dependencyProject.name}") @@ -352,7 +353,7 @@ task createProjectXml(dependsOn: configurations.corelibs) <<{ def layerWriter = new StringWriter() def layerXml = new MarkupBuilder(layerWriter) // layerXml.mkp.xmlDeclaration(version:'1.0') - layerWriter << '' << eol + layerWriter << '' << eol layerWriter << '' << eol layerXml.filesystem{ folder(name:"org-netbeans-api-project-libraries"){ diff --git a/sdk/jme3-android/src/com/jme3/gde/android/AndroidImportantFiles.java b/sdk/jme3-android/src/com/jme3/gde/android/AndroidImportantFiles.java index cbfb487cc..b96bb769c 100644 --- a/sdk/jme3-android/src/com/jme3/gde/android/AndroidImportantFiles.java +++ b/sdk/jme3-android/src/com/jme3/gde/android/AndroidImportantFiles.java @@ -67,6 +67,18 @@ public class AndroidImportantFiles implements ImportantFiles { node.setDisplayName("Android Properties"); list.add(node); } + FileObject layout = project.getProjectDirectory().getFileObject("mobile/res/layout/main.xml"); + if (layout != null) { + Node node = DataObject.find(layout).getNodeDelegate(); + node.setDisplayName("Android Layout"); + list.add(node); + } + FileObject strings = project.getProjectDirectory().getFileObject("mobile/res/values/strings.xml"); + if (strings != null) { + Node node = DataObject.find(strings).getNodeDelegate(); + node.setDisplayName("Android Strings"); + list.add(node); + } } catch (DataObjectNotFoundException ex) { Exceptions.printStackTrace(ex); } diff --git a/sdk/jme3-android/src/com/jme3/gde/android/AndroidSdkTool.java b/sdk/jme3-android/src/com/jme3/gde/android/AndroidSdkTool.java index b93b089cc..21340ab6e 100644 --- a/sdk/jme3-android/src/com/jme3/gde/android/AndroidSdkTool.java +++ b/sdk/jme3-android/src/com/jme3/gde/android/AndroidSdkTool.java @@ -243,6 +243,7 @@ public class AndroidSdkTool { } updateAndroidManifest(project); updateAndroidApplicationName(project, name); + updateAndroidLayout(project, packag); } public static void updateProject(Project project, String target, String name) { @@ -299,17 +300,15 @@ public class AndroidSdkTool { changed = true; } } - // add the following after AndroidHarness.screenOrientation is depreciated - // for jME 3.1 -// if (sdkActivity != null) { -// if (sdkActivity.hasAttribute("android:screenOrientation")) { -// String attrScreenOrientation = sdkActivity.getAttribute("android:screenOrientation"); -// } else { -// Logger.getLogger(AndroidSdkTool.class.getName()).log(Level.INFO, "creating attrScreenOrientation"); -// sdkActivity.setAttribute("android:screenOrientation", "landscape"); -// changed = true; -// } -// } + if (sdkActivity != null) { + if (sdkActivity.hasAttribute("android:screenOrientation")) { + String attrScreenOrientation = sdkActivity.getAttribute("android:screenOrientation"); + } else { + Logger.getLogger(AndroidSdkTool.class.getName()).log(Level.INFO, "creating attrScreenOrientation"); + sdkActivity.setAttribute("android:screenOrientation", "landscape"); + changed = true; + } + } } Element sdkElement = XmlHelper.findChildElement(configuration.getDocumentElement(), "uses-sdk"); @@ -319,7 +318,7 @@ public class AndroidSdkTool { changed = true; } if (!"8".equals(sdkElement.getAttribute("android:minSdkVersion"))) { - sdkElement.setAttribute("android:minSdkVersion", "8"); + sdkElement.setAttribute("android:minSdkVersion", "9"); changed = true; } Element screensElement = XmlHelper.findChildElement(configuration.getDocumentElement(), "supports-screens"); @@ -413,18 +412,79 @@ public class AndroidSdkTool { } } + private static void updateAndroidLayout(Project project, String packag) { + FileObject layout = project.getProjectDirectory().getFileObject("mobile/res/layout/main.xml"); + if (layout == null) { + Logger.getLogger(AndroidSdkTool.class.getName()).log(Level.WARNING, "Cannot find layout"); + return; + } + InputStream in = null; + FileLock lock = null; + OutputStream out = null; + try { + in = layout.getInputStream(); + Document configuration = XMLUtil.parse(new InputSource(in), false, false, null, null); + in.close(); + in = null; + + Element textViewElement = XmlHelper.findChildElement(configuration.getDocumentElement(), "TextView"); + + Element fragmentElement = configuration.createElement("fragment"); + fragmentElement.setAttribute("android:name", packag+".MainActivity$JmeFragment"); + fragmentElement.setAttribute("android:id", "@+id/jmeFragment"); + fragmentElement.setAttribute("android:layout_width", "match_parent"); + fragmentElement.setAttribute("android:layout_height", "match_parent"); + + if (textViewElement == null) { + configuration.getDocumentElement().appendChild(fragmentElement); + } else { + configuration.getDocumentElement().replaceChild(fragmentElement, textViewElement); + } + + lock = layout.lock(); + out = layout.getOutputStream(lock); + XMLUtil.write(configuration, out, "UTF-8"); + out.close(); + out = null; + lock.releaseLock(); + lock = null; + + } catch (SAXException ex) { + Exceptions.printStackTrace(ex); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } finally { + if (lock != null) { + lock.releaseLock(); + } + try { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } catch (IOException ex1) { + Exceptions.printStackTrace(ex1); + } + } + } + private static String mainActivityString(String mainClass, String packag) { String str = "package " + packag + ";\n" + " \n" - + "import android.content.pm.ActivityInfo;\n" - + "import com.jme3.app.AndroidHarness;\n" - + "import com.jme3.system.android.AndroidConfigChooser.ConfigType;\n" + + "import com.jme3.app.DefaultAndroidProfiler;\n" + + "import android.app.Activity;\n" + + "import android.app.FragmentManager;\n" + + "import android.os.Bundle;\n" + + "import android.view.Window;\n" + + "import android.view.WindowManager;\n" + + "import com.jme3.app.AndroidHarnessFragment;\n" + "import java.util.logging.Level;\n" + "import java.util.logging.LogManager;\n" + " \n" - + "public class MainActivity extends AndroidHarness{\n" - + " \n" + + "public class MainActivity extends Activity {\n" + " /*\n" + " * Note that you can ignore the errors displayed in this file,\n" + " * the android project will build regardless.\n" @@ -433,24 +493,72 @@ public class AndroidSdkTool { + " */\n" + " \n" + " public MainActivity(){\n" - + " // Set the application class to run\n" - + " appClass = \"" + mainClass + "\";\n" - + " // Try ConfigType.FASTEST; or ConfigType.LEGACY if you have problems\n" - + " eglConfigType = ConfigType.BEST;\n" - + " // Exit Dialog title & message\n" - + " exitDialogTitle = \"Exit?\";\n" - + " exitDialogMessage = \"Press Yes\";\n" - + " // Enable verbose logging\n" - + " eglConfigVerboseLogging = false;\n" - + " // Choose screen orientation\n" - + " screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;\n" - + " // Enable MouseEvents being generated from TouchEvents (default = true)\n" - + " mouseEventsEnabled = true;\n" + " // Set the default logging level (default=Level.INFO, Level.ALL=All Debug Info)\n" + " LogManager.getLogManager().getLogger(\"\").setLevel(Level.INFO);\n" + " }\n" + " \n" + + " @Override\n" + + " protected void onCreate(Bundle savedInstanceState) {\n" + + " super.onCreate(savedInstanceState);\n" + + " // Set window fullscreen and remove title bar\n" + + " requestWindowFeature(Window.FEATURE_NO_TITLE);\n" + + " getWindow().setFlags(\n" + + " WindowManager.LayoutParams.FLAG_FULLSCREEN,\n" + + " WindowManager.LayoutParams.FLAG_FULLSCREEN);\n" + + " setContentView(R.layout.main);\n" + + " \n" + + " // find the fragment\n" + + " FragmentManager fm = getFragmentManager();\n" + + " AndroidHarnessFragment jmeFragment =\n" + + " (AndroidHarnessFragment) fm.findFragmentById(R.id.jmeFragment);\n" + + " \n" + + " // uncomment the next line to add the default android profiler to the project\n" + + " //jmeFragment.getJmeApplication().setAppProfiler(new DefaultAndroidProfiler());\n" + + " }\n" + + " \n" + + " \n" + + " public static class JmeFragment extends AndroidHarnessFragment {\n" + + " public JmeFragment() {\n" + + " // Set main project class (fully qualified path)\n" + + " appClass = \"" + mainClass + "\";\n" + + " \n" + + " // Set the desired EGL configuration\n" + + " eglBitsPerPixel = 24;\n" + + " eglAlphaBits = 0;\n" + + " eglDepthBits = 16;\n" + + " eglSamples = 0;\n" + + " eglStencilBits = 0;\n" + + " \n" + + " // Set the maximum framerate\n" + + " // (default = -1 for unlimited)\n" + + " frameRate = -1;\n" + + " \n" + + " // Set the maximum resolution dimension\n" + + " // (the smaller side, height or width, is set automatically\n" + + " // to maintain the original device screen aspect ratio)\n" + + " // (default = -1 to match device screen resolution)\n" + + " maxResolutionDimension = -1;\n" + + " \n" + + " // Set input configuration settings\n" + + " joystickEventsEnabled = false;\n" + + " keyEventsEnabled = true;\n" + + " mouseEventsEnabled = true;\n" + + " \n" + + " // Set application exit settings\n" + + " finishOnAppStop = true;\n" + + " handleExitHook = true;\n" + + " exitDialogTitle = \"Do you want to exit?\";\n" + + " exitDialogMessage = \"Use your home key to bring this app into the background or exit to terminate it.\";\n" + + " \n" + + " // Set splash screen resource id, if used\n" + + " // (default = 0, no splash screen)\n" + + " // For example, if the image file name is \"splash\"...\n" + + " // splashPicID = R.drawable.splash;\n" + + " splashPicID = 0;\n" + + " }\n" + + " }\n" + "}\n"; + return str; } diff --git a/sdk/jme3-android/src/com/jme3/gde/android/mobile-targets.xml b/sdk/jme3-android/src/com/jme3/gde/android/mobile-targets.xml index 437500ba6..2a302ee57 100644 --- a/sdk/jme3-android/src/com/jme3/gde/android/mobile-targets.xml +++ b/sdk/jme3-android/src/com/jme3/gde/android/mobile-targets.xml @@ -29,58 +29,58 @@ - + - - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + Adding libraries for android. - + + + + - - Replacing bullet library with android native version. - - - - - + + Replacing bullet library with android native bullet version. + + + + + - - - - - - - @@ -91,25 +91,9 @@ - - Adding OpenAL Soft - - - - - - - - - - - - - - - + diff --git a/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/jme3-android.xml b/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/jme3-android.xml index 801e5bb3b..d937b98a9 100644 --- a/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/jme3-android.xml +++ b/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/jme3-android.xml @@ -7,7 +7,6 @@ classpath jar:nbinst://com.jme3.gde.project.baselibs/libs/jme3-android-3.1.0-snapshot-github.jar!/ - jar:nbinst://com.jme3.gde.project.libraries/libs/android.jar!/ src diff --git a/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/jme3-bullet-native-android.xml b/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/jme3-bullet-native-android.xml new file mode 100644 index 000000000..083970e02 --- /dev/null +++ b/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/jme3-bullet-native-android.xml @@ -0,0 +1,19 @@ + + + + jme3-bullet-native-android + j2se + com.jme3.gde.project.baselibs.Bundle + + classpath + jar:nbinst://com.jme3.gde.project.baselibs/libs/jme3-bullet-native-android-3.1.0-snapshot-github.jar!/ + + + src + jar:nbinst://com.jme3.gde.project.baselibs/libs/jme3-bullet-native-android-3.1.0-snapshot-github-sources.jar!/ + + + javadoc + jar:nbinst://com.jme3.gde.project.baselibs/libs/jme3-bullet-native-android-3.1.0-snapshot-github-javadoc.jar!/ + + \ No newline at end of file diff --git a/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/layer.xml b/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/layer.xml index 1a7e7bf0e..837ff7e16 100644 --- a/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/layer.xml +++ b/sdk/jme3-project-baselibs/src/com/jme3/gde/project/baselibs/layer.xml @@ -20,6 +20,7 @@ + \ No newline at end of file