Merge branch 'master' into PBRisComing

Conflicts:
	jme3-core/src/main/java/com/jme3/material/TechniqueDef.java
define_list_fix
Nehon 10 years ago
commit e1d0e06c59
  1. 6
      jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
  2. 13
      jme3-android/src/main/java/com/jme3/app/AndroidHarnessFragment.java
  3. 2
      jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java
  4. 2
      jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
  5. 257
      jme3-android/src/main/java/com/jme3/input/android/AndroidJoyInputHandler.java
  6. 168
      jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java
  7. 17
      jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java
  8. 20
      jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
  9. 2
      jme3-android/src/main/java/jme3test/android/DemoAndroidHarness.java
  10. 312
      jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
  11. 130
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
  12. 53
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
  13. 213
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
  14. 71
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderModelLoader.java
  15. 7
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/AnimationHelper.java
  16. 12
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BlenderAction.java
  17. 9
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java
  18. 13
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java
  19. 15
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/cameras/CameraHelper.java
  20. 23
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/BezierCurve.java
  21. 77
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/FileBlockHeader.java
  22. 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java
  23. 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java
  24. 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/lights/LightHelper.java
  25. 23
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  26. 10
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialHelper.java
  27. 13
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  28. 52
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/objects/ObjectHelper.java
  29. 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java
  30. 11
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java
  31. 72
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java
  32. 27
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java
  33. 18
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/AbstractTextureBlender.java
  34. 4
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
  35. 5
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderDDS.java
  36. 7
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderFactory.java
  37. 4
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderLuminance.java
  38. 18
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/AWTPixelInputOutput.java
  39. 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/io/PixelIOFactory.java
  40. 2
      jme3-bullet-native/build.gradle
  41. 61
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.cpp
  42. 9
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_PhysicsSpace.h
  43. 90
      jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp
  44. 3
      jme3-bullet-native/src/native/cpp/jmeBulletUtil.h
  45. 79
      jme3-bullet-native/src/native/cpp/jmeClasses.cpp
  46. 12
      jme3-bullet-native/src/native/cpp/jmeClasses.h
  47. 61
      jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java
  48. 25
      jme3-core/build.gradle
  49. 1
      jme3-core/src/main/java/com/jme3/animation/AnimChannel.java
  50. 2
      jme3-core/src/main/java/com/jme3/asset/AssetConfig.java
  51. 5
      jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java
  52. 6
      jme3-core/src/main/java/com/jme3/asset/ImplHandler.java
  53. 2
      jme3-core/src/main/java/com/jme3/asset/ShaderNodeDefinitionKey.java
  54. 6
      jme3-core/src/main/java/com/jme3/asset/cache/WeakRefCloneAssetCache.java
  55. 5
      jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java
  56. 3
      jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java
  57. 5
      jme3-core/src/main/java/com/jme3/collision/CollisionResult.java
  58. 2
      jme3-core/src/main/java/com/jme3/light/SpotLight.java
  59. 117
      jme3-core/src/main/java/com/jme3/material/TechniqueDef.java
  60. 39
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  61. 4
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java
  62. 2
      jme3-core/src/main/java/com/jme3/scene/BatchNode.java
  63. 2
      jme3-core/src/main/java/com/jme3/scene/Node.java
  64. 207
      jme3-core/src/main/java/com/jme3/scene/UserData.java
  65. 2
      jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java
  66. 2
      jme3-core/src/main/java/com/jme3/scene/shape/Dome.java
  67. 2
      jme3-core/src/main/java/com/jme3/shader/DefineList.java
  68. 4
      jme3-core/src/main/java/com/jme3/system/NullContext.java
  69. 7
      jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java
  70. 9
      jme3-core/src/main/java/com/jme3/texture/Texture.java
  71. 3
      jme3-core/src/main/java/com/jme3/util/IntMap.java
  72. 6
      jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java
  73. 2
      jme3-core/src/main/java/com/jme3/util/blockparser/BlockLanguageParser.java
  74. 3
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert
  75. 1
      jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert
  76. 10
      jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib
  77. 2
      jme3-core/src/plugins/java/com/jme3/audio/plugins/WAVLoader.java
  78. 2
      jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java
  79. 1
      jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
  80. 2
      jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java
  81. 20
      jme3-examples/src/main/java/jme3test/bullet/TestSweepTest.java
  82. 27
      jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java
  83. 6
      jme3-jogl/build.gradle
  84. 10
      jme3-jogl/src/main/java/com/jme3/input/jogl/NewtMouseInput.java
  85. 22
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java
  86. 19
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java
  87. 28
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java
  88. 2
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java
  89. 8
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java
  90. 2
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglDisplay.java
  91. 26
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java
  92. 2
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java
  93. 4
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java
  94. 14
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java
  95. 2
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
  96. 4
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java
  97. 2
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
  98. 2
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglDisplay.java
  99. 2
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java
  100. 5
      jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -167,12 +167,6 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
*/ */
protected int splashPicID = 0; 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 OGLESContext ctx;
protected GLSurfaceView view = null; protected GLSurfaceView view = null;

@ -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."; 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 * 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 to the value of the Resource ID (i.e. R.drawable.picname). If

@ -74,7 +74,7 @@ public class VideoRecorderAppState extends AbstractAppState {
public Thread newThread(Runnable r) { public Thread newThread(Runnable r) {
Thread th = new Thread(r); Thread th = new Thread(r);
th.setName("jME Video Processing Thread"); th.setName("jME3 Video Processor");
th.setDaemon(true); th.setDaemon(true);
return th; return th;
} }

@ -62,7 +62,6 @@ public class AndroidInputHandler implements TouchInput {
private boolean mouseEventsInvertX = false; private boolean mouseEventsInvertX = false;
private boolean mouseEventsInvertY = false; private boolean mouseEventsInvertY = false;
private boolean keyboardEventsEnabled = false; private boolean keyboardEventsEnabled = false;
private boolean joystickEventsEnabled = false;
private boolean dontSendHistory = false; private boolean dontSendHistory = false;
@ -140,7 +139,6 @@ public class AndroidInputHandler implements TouchInput {
mouseEventsEnabled = settings.isEmulateMouse(); mouseEventsEnabled = settings.isEmulateMouse();
mouseEventsInvertX = settings.isEmulateMouseFlipX(); mouseEventsInvertX = settings.isEmulateMouseFlipX();
mouseEventsInvertY = settings.isEmulateMouseFlipY(); mouseEventsInvertY = settings.isEmulateMouseFlipY();
joystickEventsEnabled = settings.useJoysticks();
// view width and height are 0 until the view is displayed on the screen // view width and height are 0 until the view is displayed on the screen
if (view.getWidth() != 0 && view.getHeight() != 0) { if (view.getWidth() != 0 && view.getHeight() != 0) {

@ -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. </br>
* 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
* <uses-permission android:name="android.permission.VIBRATE"/>
*
* @author iwgeric
*/
public class AndroidJoyInputHandler implements JoyInput {
private static final Logger logger = Logger.getLogger(AndroidJoyInputHandler.class.getName());
private List<Joystick> joystickList = new ArrayList<Joystick>();
// private boolean dontSendHistory = false;
// Internal
private GLSurfaceView view;
private boolean initialized = false;
private RawInputListener listener = null;
private ConcurrentLinkedQueue<InputEvent> eventQueue = new ConcurrentLinkedQueue<InputEvent>();
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);
}
}
}
}
}

@ -37,7 +37,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent; import android.hardware.SensorEvent;
import android.hardware.SensorEventListener; import android.hardware.SensorEventListener;
import android.hardware.SensorManager; import android.hardware.SensorManager;
import android.os.Vibrator; import android.opengl.GLSurfaceView;
import android.view.Surface; import android.view.Surface;
import android.view.WindowManager; import android.view.WindowManager;
import com.jme3.input.AbstractJoystick; import com.jme3.input.AbstractJoystick;
@ -47,10 +47,8 @@ import com.jme3.input.JoyInput;
import com.jme3.input.Joystick; import com.jme3.input.Joystick;
import com.jme3.input.JoystickAxis; import com.jme3.input.JoystickAxis;
import com.jme3.input.SensorJoystickAxis; import com.jme3.input.SensorJoystickAxis;
import com.jme3.input.RawInputListener;
import com.jme3.input.event.JoyAxisEvent; import com.jme3.input.event.JoyAxisEvent;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.system.android.JmeAndroidSystem;
import com.jme3.util.IntMap; import com.jme3.util.IntMap;
import com.jme3.util.IntMap.Entry; import com.jme3.util.IntMap.Entry;
import java.util.ArrayList; 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 * A single joystick is configured and includes data for all configured sensors
* as seperate axes of the joystick. * 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 * Refer to the strings defined in SensorJoystickAxis for a list of supported
* sensors and their axis data. Each sensor type defined in SensorJoystickAxis * 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 * 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 * 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. * 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
* <uses-permission android:name="android.permission.VIBRATE"/>
*
* @author iwgeric * @author iwgeric
*/ */
public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { public class AndroidSensorJoyInput implements SensorEventListener {
private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName()); private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName());
private Context context = null; private AndroidJoyInputHandler joyHandler;
private InputManager inputManager = null;
private SensorManager sensorManager = null; private SensorManager sensorManager = null;
private WindowManager windowManager = null; private WindowManager windowManager = null;
private Vibrator vibrator = null;
private boolean vibratorActive = false;
private long maxRumbleTime = 250; // 250ms
private RawInputListener listener = null;
private IntMap<SensorData> sensors = new IntMap<SensorData>(); private IntMap<SensorData> sensors = new IntMap<SensorData>();
private AndroidJoystick[] joysticks;
private int lastRotation = 0; private int lastRotation = 0;
private boolean initialized = false;
private boolean loaded = false; private boolean loaded = false;
private final ArrayList<JoyAxisEvent> eventQueue = new ArrayList<JoyAxisEvent>(); public AndroidSensorJoyInput(AndroidJoyInputHandler joyHandler) {
this.joyHandler = joyHandler;
}
/** /**
* Internal class to enclose data for each sensor. * Internal class to enclose data for each sensor.
@ -120,7 +93,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
int androidSensorType = -1; int androidSensorType = -1;
int androidSensorSpeed = SensorManager.SENSOR_DELAY_GAME; int androidSensorSpeed = SensorManager.SENSOR_DELAY_GAME;
Sensor sensor = null; Sensor sensor = null;
int sensorAccuracy = 0; int sensorAccuracy = -1;
float[] lastValues; float[] lastValues;
final Object valuesLock = new Object(); final Object valuesLock = new Object();
ArrayList<AndroidJoystickAxis> axes = new ArrayList<AndroidJoystickAxis>(); ArrayList<AndroidJoystickAxis> axes = new ArrayList<AndroidJoystickAxis>();
@ -134,16 +107,19 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
} }
private void initSensorManager() { public void setView(GLSurfaceView view) {
this.context = JmeAndroidSystem.getView().getContext(); pauseSensors();
if (sensorManager != null) {
sensorManager.unregisterListener(this);
}
if (view == null) {
windowManager = null;
sensorManager = null;
} else {
// Get instance of the WindowManager from the current Context // Get instance of the WindowManager from the current Context
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); windowManager = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
// Get instance of the SensorManager from the current Context // Get instance of the SensorManager from the current Context
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); sensorManager = (SensorManager) view.getContext().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.");
} }
} }
@ -222,9 +198,6 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
unRegisterListener(entry.getKey()); unRegisterListener(entry.getKey());
} }
} }
if (vibrator != null && vibratorActive) {
vibrator.cancel();
}
} }
/** /**
@ -400,10 +373,8 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
if (!sensorData.haveData) { if (!sensorData.haveData) {
sensorData.haveData = true; sensorData.haveData = true;
} else { } else {
synchronized (eventQueue){
if (axis.isChanged()) { if (axis.isChanged()) {
eventQueue.add(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); joyHandler.addEvent(new JoyAxisEvent(axis, axis.getJoystickAxisValue()));
}
} }
} }
} }
@ -428,47 +399,14 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
// Start of JoyInput methods // Start of JoyInput methods
public void setJoyRumble(int joyId, float amount) { public Joystick loadJoystick(int joyId, InputManager inputManager) {
// 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();
SensorData sensorData; SensorData sensorData;
List<Joystick> list = new ArrayList<Joystick>();
AndroidJoystick joystick;
AndroidJoystickAxis axis; AndroidJoystickAxis axis;
joystick = new AndroidJoystick(inputManager, AndroidJoystick joystick = new AndroidJoystick(inputManager,
this, joyHandler,
list.size(), joyId,
"AndroidSensorsJoystick"); "AndroidSensorsJoystick");
list.add(joystick);
List<Sensor> availSensors = sensorManager.getSensorList(Sensor.TYPE_ALL); List<Sensor> availSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor: availSensors) { for (Sensor sensor: availSensors) {
@ -555,14 +493,8 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
// } // }
joysticks = list.toArray( new AndroidJoystick[list.size()] );
loaded = true; loaded = true;
return joysticks; return joystick;
}
public void initialize() {
initialized = true;
loaded = false;
} }
public void update() { public void update() {
@ -570,15 +502,6 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
return; return;
} }
updateOrientation(); 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() { public void destroy() {
@ -588,39 +511,27 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
sensorManager.unregisterListener(this); sensorManager.unregisterListener(this);
} }
sensors.clear(); sensors.clear();
eventQueue.clear();
initialized = false;
loaded = false; loaded = false;
joysticks = null;
sensorManager = 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 // Start of Android SensorEventListener methods
@Override
public void onSensorChanged(SensorEvent se) { public void onSensorChanged(SensorEvent se) {
if (!initialized || !loaded) { if (!loaded) {
return; 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(); int sensorType = se.sensor.getType();
SensorData sensorData = sensors.get(sensorType); 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 != null && sensorData.sensor.equals(se.sensor) && sensorData.enabled) {
if (sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) { if (sensorData.sensorAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
@ -641,10 +552,11 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
if (!sensorData.haveData) { if (!sensorData.haveData) {
sensorData.haveData = true; sensorData.haveData = true;
} else { } else {
synchronized (eventQueue){
if (axis.isChanged()) { if (axis.isChanged()) {
eventQueue.add(new JoyAxisEvent(axis, axis.getJoystickAxisValue())); 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) { public void onAccuracyChanged(Sensor sensor, int i) {
int sensorType = sensor.getType(); int sensorType = sensor.getType();
SensorData sensorData = sensors.get(sensorType); SensorData sensorData = sensors.get(sensorType);
@ -697,7 +610,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
AndroidJoystickAxis axis; AndroidJoystickAxis axis;
axis = new AndroidJoystickAxis( axis = new AndroidJoystickAxis(
inputManager, // InputManager (InputManager) getInputManager(), // InputManager (InputManager)
this, // parent Joystick (Joystick) this, // parent Joystick (Joystick)
axisNum, // Axis Index (int) axisNum, // Axis Index (int)
axisName, // Axis Name (String) axisName, // Axis Name (String)
@ -758,10 +671,12 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
this.maxRawValue = maxRawValue; this.maxRawValue = maxRawValue;
} }
@Override
public float getMaxRawValue() { public float getMaxRawValue() {
return maxRawValue; return maxRawValue;
} }
@Override
public void setMaxRawValue(float maxRawValue) { public void setMaxRawValue(float maxRawValue) {
this.maxRawValue = maxRawValue; this.maxRawValue = maxRawValue;
} }
@ -787,6 +702,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener {
return hasChanged; return hasChanged;
} }
@Override
public void calibrateCenter() { public void calibrateCenter() {
zeroRawValue = lastRawValue; zeroRawValue = lastRawValue;
logger.log(Level.FINE, "Calibrating axis {0} to {1}", logger.log(Level.FINE, "Calibrating axis {0} to {1}",

@ -1850,21 +1850,6 @@ public class OGLESShaderRenderer implements Renderer {
TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y); 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) { public void deleteImage(Image image) {
int texId = image.getId(); int texId = image.getId();
if (texId != -1) { if (texId != -1) {
@ -2339,7 +2324,6 @@ public class OGLESShaderRenderer implements Renderer {
RendererUtil.checkGLError(); RendererUtil.checkGLError();
} }
clearVertexAttribs(); clearVertexAttribs();
clearTextureUnits();
} }
private void renderMeshDefault(Mesh mesh, int lod, int count) { private void renderMeshDefault(Mesh mesh, int lod, int count) {
@ -2378,7 +2362,6 @@ public class OGLESShaderRenderer implements Renderer {
RendererUtil.checkGLError(); RendererUtil.checkGLError();
} }
clearVertexAttribs(); clearVertexAttribs();
clearTextureUnits();
} }
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {

@ -46,17 +46,15 @@ import android.view.ViewGroup.LayoutParams;
import android.widget.EditText; import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import com.jme3.input.*; import com.jme3.input.*;
import com.jme3.input.android.AndroidSensorJoyInput;
import com.jme3.input.android.AndroidInputHandler; import com.jme3.input.android.AndroidInputHandler;
import com.jme3.input.android.AndroidJoyInputHandler;
import com.jme3.input.controls.SoftTextDialogInputListener; import com.jme3.input.controls.SoftTextDialogInputListener;
import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyKeyInput;
import com.jme3.input.dummy.DummyMouseInput; import com.jme3.input.dummy.DummyMouseInput;
import com.jme3.renderer.android.AndroidGL; import com.jme3.renderer.android.AndroidGL;
import com.jme3.renderer.opengl.GL; import com.jme3.renderer.opengl.GL;
import com.jme3.renderer.opengl.GLDebugES;
import com.jme3.renderer.opengl.GLExt; import com.jme3.renderer.opengl.GLExt;
import com.jme3.renderer.opengl.GLRenderer; import com.jme3.renderer.opengl.GLRenderer;
import com.jme3.renderer.opengl.GLTracer;
import com.jme3.system.*; import com.jme3.system.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
@ -77,9 +75,9 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
protected SystemListener listener; protected SystemListener listener;
protected boolean autoFlush = true; protected boolean autoFlush = true;
protected AndroidInputHandler androidInput; protected AndroidInputHandler androidInput;
protected AndroidJoyInputHandler androidJoyInput = null;
protected long minFrameDuration = 0; // No FPS cap protected long minFrameDuration = 0; // No FPS cap
protected long lastUpdateTime = 0; protected long lastUpdateTime = 0;
protected JoyInput androidSensorJoyInput = null;
public OGLESContext() { public OGLESContext() {
} }
@ -119,6 +117,12 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
androidInput.setView(view); androidInput.setView(view);
androidInput.loadSettings(settings); androidInput.loadSettings(settings);
if (androidJoyInput == null) {
androidJoyInput = new AndroidJoyInputHandler();
}
androidJoyInput.setView(view);
androidJoyInput.loadSettings(settings);
// setEGLContextClientVersion must be set before calling setRenderer // setEGLContextClientVersion must be set before calling setRenderer
// this means it cannot be set in AndroidConfigChooser (too late) // this means it cannot be set in AndroidConfigChooser (too late)
view.setEGLContextClientVersion(2); view.setEGLContextClientVersion(2);
@ -231,6 +235,9 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
if (androidInput != null) { if (androidInput != null) {
androidInput.loadSettings(settings); androidInput.loadSettings(settings);
} }
if (androidJoyInput != null) {
androidJoyInput.loadSettings(settings);
}
if (settings.getFrameRate() > 0) { if (settings.getFrameRate() > 0) {
minFrameDuration = (long)(1000d / (double)settings.getFrameRate()); // ms minFrameDuration = (long)(1000d / (double)settings.getFrameRate()); // ms
@ -267,10 +274,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
@Override @Override
public JoyInput getJoyInput() { public JoyInput getJoyInput() {
if (androidSensorJoyInput == null) { return androidJoyInput;
androidSensorJoyInput = new AndroidSensorJoyInput();
}
return androidSensorJoyInput;
} }
@Override @Override

@ -19,8 +19,6 @@ public class DemoAndroidHarness extends AndroidHarness
exitDialogTitle = "Close Demo?"; exitDialogTitle = "Close Demo?";
exitDialogMessage = "Press Yes"; exitDialogMessage = "Press Yes";
screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
} }

@ -32,36 +32,19 @@
package com.jme3.asset; package com.jme3.asset;
import java.io.IOException; 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.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.RenderState.FaceCullMode; 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. * Blender key. Contains path of the blender file and its loading properties.
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public class BlenderKey extends ModelKey { public class BlenderKey extends ModelKey {
protected static final int DEFAULT_FPS = 25; protected static final int DEFAULT_FPS = 25;
/** /**
* FramesPerSecond parameter describe how many frames there are in each second. It allows to calculate the time * 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. * This variable is a bitwise flag of FeatureToLoad interface values; By default everything is being loaded.
*/ */
protected int featuresToLoad = FeaturesToLoad.ALL; 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; protected boolean loadUnlinkedAssets;
/** The root path for all the assets. */ /** The root path for all the assets. */
protected String assetRootPath; protected String assetRootPath;
@ -268,6 +251,7 @@ public class BlenderKey extends ModelKey {
* @param featuresToLoad * @param featuresToLoad
* bitwise flag of FeaturesToLoad interface values * bitwise flag of FeaturesToLoad interface values
*/ */
@Deprecated
public void includeInLoading(int featuresToLoad) { public void includeInLoading(int featuresToLoad) {
this.featuresToLoad |= featuresToLoad; this.featuresToLoad |= featuresToLoad;
} }
@ -277,10 +261,12 @@ public class BlenderKey extends ModelKey {
* @param featuresNotToLoad * @param featuresNotToLoad
* bitwise flag of FeaturesToLoad interface values * bitwise flag of FeaturesToLoad interface values
*/ */
@Deprecated
public void excludeFromLoading(int featuresNotToLoad) { public void excludeFromLoading(int featuresNotToLoad) {
featuresToLoad &= ~featuresNotToLoad; featuresToLoad &= ~featuresNotToLoad;
} }
@Deprecated
public boolean shouldLoad(int featureToLoad) { public boolean shouldLoad(int featureToLoad) {
return (featuresToLoad & featureToLoad) != 0; return (featuresToLoad & featureToLoad) != 0;
} }
@ -290,6 +276,7 @@ public class BlenderKey extends ModelKey {
* the blender file loader. * the blender file loader.
* @return features that will be loaded by the blender file loader * @return features that will be loaded by the blender file loader
*/ */
@Deprecated
public int getFeaturesToLoad() { public int getFeaturesToLoad() {
return featuresToLoad; return featuresToLoad;
} }
@ -317,15 +304,6 @@ public class BlenderKey extends ModelKey {
this.loadUnlinkedAssets = loadUnlinkedAssets; 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 * 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. * 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. * 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) * @author Marcin Roguski (Kaelthas)
*/ */
@Deprecated
public static interface FeaturesToLoad { public static interface FeaturesToLoad {
int SCENES = 0x0000FFFF; int SCENES = 0x0000FFFF;
@ -745,281 +726,4 @@ public class BlenderKey extends ModelKey {
*/ */
ALL_NAMES_MATCH; 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<Node> scenes;
/** Objects from all scenes. */
private List<Node> objects;
/** Materials from all objects. */
private List<Material> materials;
/** Textures from all objects. */
private List<Texture> textures;
/** Animations of all objects. */
private List<Animation> animations;
/** All cameras from the file. */
private List<CameraNode> cameras;
/** All lights from the file. */
private List<LightNode> lights;
/** Loaded sky. */
private Spatial sky;
/** Scene filters (ie. FOG). */
private List<Filter> 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<Node>();
}
if ((featuresToLoad & FeaturesToLoad.OBJECTS) != 0) {
objects = new ArrayList<Node>();
if ((featuresToLoad & FeaturesToLoad.MATERIALS) != 0) {
materials = new ArrayList<Material>();
if ((featuresToLoad & FeaturesToLoad.TEXTURES) != 0) {
textures = new ArrayList<Texture>();
}
}
if ((featuresToLoad & FeaturesToLoad.ANIMATIONS) != 0) {
animations = new ArrayList<Animation>();
}
}
if ((featuresToLoad & FeaturesToLoad.CAMERAS) != 0) {
cameras = new ArrayList<CameraNode>();
}
if ((featuresToLoad & FeaturesToLoad.LIGHTS) != 0) {
lights = new ArrayList<LightNode>();
}
}
/**
* 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<Filter>(5);
}
filters.add(filter);
}
}
/**
* @param backgroundColor
* the background color
*/
public void setBackgroundColor(ColorRGBA backgroundColor) {
this.backgroundColor = backgroundColor;
}
/**
* @return all loaded scenes
*/
public List<Node> getScenes() {
return scenes;
}
/**
* @return all loaded objects
*/
public List<Node> getObjects() {
return objects;
}
/**
* @return all loaded materials
*/
public List<Material> getMaterials() {
return materials;
}
/**
* @return all loaded textures
*/
public List<Texture> getTextures() {
return textures;
}
/**
* @return all loaded animations
*/
public List<Animation> getAnimations() {
return animations;
}
/**
* @return all loaded cameras
*/
public List<CameraNode> getCameras() {
return cameras;
}
/**
* @return all loaded lights
*/
public List<LightNode> getLights() {
return lights;
}
/**
* @return the scene's sky
*/
public Spatial getSky() {
return sky;
}
/**
* @return scene filters
*/
public List<Filter> 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<Spatial> queue) {
}
}
} }

@ -31,17 +31,34 @@
*/ */
package com.jme3.scene.plugins.blender; package com.jme3.scene.plugins.blender;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; 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.export.Savable;
import com.jme3.light.Light;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; 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.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; 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.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 * 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,6 +66,8 @@ import com.jme3.scene.plugins.blender.objects.Properties;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public abstract class AbstractBlenderHelper { public abstract class AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(AbstractBlenderHelper.class.getName());
/** The blender context. */ /** The blender context. */
protected BlenderContext blenderContext; protected BlenderContext blenderContext;
/** The version of the blend file. */ /** The version of the blend file. */
@ -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<String> pathsToCheck = new ArrayList<String>();
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<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData");
for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) {
String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
List<Node> scenes = (List<Node>) entry.getValue().get("scenes");
for (Node scene : scenes) {
blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene);
}
List<Node> objects = (List<Node>) entry.getValue().get("objects");
for (Node object : objects) {
blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object);
}
List<TemporalMesh> meshes = (List<TemporalMesh>) entry.getValue().get("meshes");
for (TemporalMesh mesh : meshes) {
blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh);
}
List<MaterialContext> materials = (List<MaterialContext>) entry.getValue().get("materials");
for (MaterialContext materialContext : materials) {
blenderContext.addLinkedFeature(linkedDataFilePath, "MA" + materialContext.getName(), materialContext);
}
List<Texture> textures = (List<Texture>) entry.getValue().get("textures");
for (Texture texture : textures) {
blenderContext.addLinkedFeature(linkedDataFilePath, "TE" + texture.getName(), texture);
}
List<Texture> images = (List<Texture>) entry.getValue().get("images");
for (Texture image : images) {
blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image);
}
List<Animation> animations = (List<Animation>) entry.getValue().get("animations");
for (Animation animation : animations) {
blenderContext.addLinkedFeature(linkedDataFilePath, "AC" + animation.getName(), animation);
}
List<Camera> cameras = (List<Camera>) entry.getValue().get("cameras");
for (Camera camera : cameras) {
blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera);
}
List<Light> lights = (List<Light>) 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<Filter> filters = (List<Filter>) 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;
}
} }

@ -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.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.DnaBlockData; import com.jme3.scene.plugins.blender.file.DnaBlockData;
import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
/** /**
@ -82,13 +83,15 @@ public class BlenderContext {
*/ */
private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>(); private Map<Long, FileBlockHeader> fileBlockHeadersByOma = new HashMap<Long, FileBlockHeader>();
/** A map containing the file block headers. The key is the block code. */ /** A map containing the file block headers. The key is the block code. */
private Map<Integer, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<Integer, List<FileBlockHeader>>(); private Map<BlockCode, List<FileBlockHeader>> fileBlockHeadersByCode = new HashMap<BlockCode, List<FileBlockHeader>>();
/** /**
* This map stores the loaded features by their old memory address. The * 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 - * first object in the value table is the loaded structure and the second -
* the structure already converted into proper data. * the structure already converted into proper data.
*/ */
private Map<Long, Map<LoadedDataType, Object>> loadedFeatures = new HashMap<Long, Map<LoadedDataType, Object>>(); private Map<Long, Map<LoadedDataType, Object>> loadedFeatures = new HashMap<Long, Map<LoadedDataType, Object>>();
/** Features loaded from external blender files. The key is the file path and the value is a map between feature name and loaded feature. */
private Map<String, Map<String, Object>> linkedFeatures = new HashMap<String, Map<String, Object>>();
/** A stack that hold the parent structure of currently loaded feature. */ /** A stack that hold the parent structure of currently loaded feature. */
private Stack<Structure> parentStack = new Stack<Structure>(); private Stack<Structure> parentStack = new Stack<Structure>();
/** A list of constraints for the specified object. */ /** A list of constraints for the specified object. */
@ -231,10 +234,10 @@ public class BlenderContext {
*/ */
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(Integer.valueOf(fileBlockHeader.getCode())); List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
if (headers == null) { if (headers == null) {
headers = new ArrayList<FileBlockHeader>(); headers = new ArrayList<FileBlockHeader>();
fileBlockHeadersByCode.put(Integer.valueOf(fileBlockHeader.getCode()), headers); fileBlockHeadersByCode.put(fileBlockHeader.getCode(), headers);
} }
headers.add(fileBlockHeader); headers.add(fileBlockHeader);
} }
@ -258,7 +261,7 @@ public class BlenderContext {
* the code of file blocks * the code of file blocks
* @return a list of file blocks' headers of a specified code * @return a list of file blocks' headers of a specified code
*/ */
public List<FileBlockHeader> getFileBlocks(Integer code) { public List<FileBlockHeader> getFileBlocks(BlockCode code) {
return fileBlockHeadersByCode.get(code); return fileBlockHeadersByCode.get(code);
} }
@ -325,6 +328,48 @@ public class BlenderContext {
return null; 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<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
if (linkedFeatures == null) {
linkedFeatures = new HashMap<String, Object>();
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<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
return linkedFeatures != null ? linkedFeatures.get(featureName) : null;
}
/**
* @return all linked features for the current blend file
*/
public Map<String, Map<String, Object>> getLinkedFeatures() {
return linkedFeatures;
}
/** /**
* This method adds the structure to the parent stack. * This method adds the structure to the parent stack.
* *

@ -33,17 +33,21 @@ package com.jme3.scene.plugins.blender;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.animation.Animation;
import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoader; import com.jme3.asset.AssetLoader;
import com.jme3.asset.BlenderKey; 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.asset.ModelKey;
import com.jme3.light.Light; 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.CameraNode;
import com.jme3.scene.LightNode; import com.jme3.scene.LightNode;
import com.jme3.scene.Node; 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.BlenderFileException;
import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.FileBlockHeader; 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.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.landscape.LandscapeHelper; import com.jme3.scene.plugins.blender.landscape.LandscapeHelper;
import com.jme3.scene.plugins.blender.lights.LightHelper; 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.materials.MaterialHelper;
import com.jme3.scene.plugins.blender.meshes.MeshHelper; 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.modifiers.ModifierHelper;
import com.jme3.scene.plugins.blender.objects.ObjectHelper; import com.jme3.scene.plugins.blender.objects.ObjectHelper;
import com.jme3.scene.plugins.blender.particles.ParticlesHelper; import com.jme3.scene.plugins.blender.particles.ParticlesHelper;
import com.jme3.scene.plugins.blender.textures.TextureHelper; 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. * 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 { try {
this.setup(assetInfo); this.setup(assetInfo);
List<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
BlenderKey blenderKey = blenderContext.getBlenderKey();
LoadingResults loadingResults = blenderKey.prepareLoadingResults();
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.loadAnimations(); animationHelper.loadAnimations();
BlenderKey blenderKey = blenderContext.getBlenderKey();
LoadedFeatures loadedFeatures = new LoadedFeatures();
for (FileBlockHeader block : blocks) { for (FileBlockHeader block : blocks) {
switch (block.getCode()) { switch (block.getCode()) {
case FileBlockHeader.BLOCK_OB00:// Object case BLOCK_OB00:
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Object object = objectHelper.toObject(block.getStructure(blenderContext), blenderContext); Node object = (Node) 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)) { 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() }); LOGGER.log(Level.FINE, "{0}: {1}--> {2}", new Object[] { object.getName(), object.getLocalTranslation().toString(), object.getParent() == null ? "null" : object.getParent().getName() });
} }
if (this.isRootObject(loadingResults, (Node) object)) { if (object.getParent() == null) {
loadingResults.addObject((Node) object); 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; break;
// case FileBlockHeader.BLOCK_MA00:// Material case BLOCK_SC00:// Scene
// MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); loadedFeatures.sceneBlocks.add(block);
// MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext); break;
// if (blenderKey.isLoadUnlinkedAssets() && blenderKey.shouldLoad(FeaturesToLoad.MATERIALS)) { case BLOCK_MA00:// Material
// loadingResults.addMaterial(this.toMaterial(block.getStructure(blenderContext))); MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
// } MaterialContext materialContext = materialHelper.toMaterialContext(block.getStructure(blenderContext), blenderContext);
// break; loadedFeatures.materials.add(materialContext);
case FileBlockHeader.BLOCK_SC00:// Scene break;
if (blenderKey.shouldLoad(FeaturesToLoad.SCENES)) { case BLOCK_ME00:// Mesh
sceneBlocks.add(block); 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; break;
case FileBlockHeader.BLOCK_WO00:// World case BLOCK_TE00:
if (blenderKey.shouldLoad(FeaturesToLoad.WORLD)) { 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); Structure worldStructure = block.getStructure(blenderContext);
String worldName = worldStructure.getName(); String worldName = worldStructure.getName();
if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) { if (blenderKey.getUsedWorld() == null || blenderKey.getUsedWorld().equals(worldName)) {
LandscapeHelper landscapeHelper = blenderContext.getHelper(LandscapeHelper.class);
Light ambientLight = landscapeHelper.toAmbientLight(worldStructure); Light ambientLight = landscapeHelper.toAmbientLight(worldStructure);
if (ambientLight != null) { if (ambientLight != null) {
loadingResults.addLight(new LightNode(null, ambientLight)); loadedFeatures.objects.add(new LightNode(null, ambientLight));
loadedFeatures.lights.add(ambientLight);
} }
loadingResults.setSky(landscapeHelper.toSky(worldStructure)); loadedFeatures.sky = landscapeHelper.toSky(worldStructure);
loadingResults.addFilter(landscapeHelper.toFog(worldStructure)); loadedFeatures.backgroundColor = landscapeHelper.toBackgroundColor(worldStructure);
loadingResults.setBackgroundColor(landscapeHelper.toBackgroundColor(worldStructure));
Filter fogFilter = landscapeHelper.toFog(worldStructure);
if (fogFilter != null) {
loadedFeatures.filters.add(landscapeHelper.toFog(worldStructure));
} }
} }
break; 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 constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper.bakeConstraints(blenderContext); constraintHelper.bakeConstraints(blenderContext);
// load the scene at the very end so that the root nodes have no parent during loading or constraints applying LOGGER.fine("Loading scenes and attaching them to the root object.");
for (FileBlockHeader sceneBlock : sceneBlocks) { for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
loadingResults.addScene(this.toScene(sceneBlock.getStructure(blenderContext))); 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<String, Map<String, Object>> linkedData = new HashMap<String, Map<String, Object>>();
Map<String, Object> thisFileData = new HashMap<String, Object>();
thisFileData.put("scenes", loadedFeatures.scenes == null ? new ArrayList<Object>() : loadedFeatures.scenes);
thisFileData.put("objects", loadedFeatures.objects == null ? new ArrayList<Object>() : loadedFeatures.objects);
thisFileData.put("meshes", loadedFeatures.meshes == null ? new ArrayList<Object>() : loadedFeatures.meshes);
thisFileData.put("materials", loadedFeatures.materials == null ? new ArrayList<Object>() : loadedFeatures.materials);
thisFileData.put("textures", loadedFeatures.textures == null ? new ArrayList<Object>() : loadedFeatures.textures);
thisFileData.put("images", loadedFeatures.images == null ? new ArrayList<Object>() : loadedFeatures.images);
thisFileData.put("animations", loadedFeatures.animations == null ? new ArrayList<Object>() : loadedFeatures.animations);
thisFileData.put("cameras", loadedFeatures.cameras == null ? new ArrayList<Object>() : loadedFeatures.cameras);
thisFileData.put("lights", loadedFeatures.lights == null ? new ArrayList<Object>() : loadedFeatures.lights);
thisFileData.put("filters", loadedFeatures.filters == null ? new ArrayList<Object>() : loadedFeatures.filters);
thisFileData.put("backgroundColor", loadedFeatures.backgroundColor);
thisFileData.put("sky", loadedFeatures.sky);
linkedData.put("this", thisFileData);
linkedData.putAll(blenderContext.getLinkedFeatures());
modelRoot.setUserData("linkedData", linkedData);
}
return modelRoot;
} catch (BlenderFileException e) { } catch (BlenderFileException e) {
throw new IOException(e.getLocalizedMessage(), e); throw new IOException(e.getLocalizedMessage(), e);
} catch (Exception e) { } catch (Exception e) {
@ -158,40 +224,17 @@ 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 <b>true</b> if the given spatial is a root object and
* <b>false</b> 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. * This method converts the given structure to a scene node.
* @param structure * @param structure
* structure of a scene * structure of a scene
* @return scene's node * @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); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Node result = new Node(structure.getName()); Node result = new Node(structure.getName());
try {
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase(); List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
for (Structure b : base) { for (Structure b : base) {
Pointer pObject = (Pointer) b.getFieldValue("object"); Pointer pObject = (Pointer) b.getFieldValue("object");
@ -200,7 +243,7 @@ public class BlenderLoader implements AssetLoader {
Object object = objectHelper.toObject(objectStructure, blenderContext); Object object = objectHelper.toObject(objectStructure, blenderContext);
if (object instanceof LightNode) { if (object instanceof LightNode) {
result.addLight(((LightNode) object).getLight()); result.addLight(((LightNode) object).getLight());// FIXME: check if this is needed !!!
result.attachChild((LightNode) object); result.attachChild((LightNode) object);
} else if (object instanceof Node) { } else if (object instanceof Node) {
if (LOGGER.isLoggable(Level.FINE)) { if (LOGGER.isLoggable(Level.FINE)) {
@ -212,9 +255,6 @@ public class BlenderLoader implements AssetLoader {
} }
} }
} }
} catch (BlenderFileException e) {
LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
}
return result; return result;
} }
@ -269,7 +309,7 @@ public class BlenderLoader implements AssetLoader {
if (!fileBlock.isDnaBlock()) { if (!fileBlock.isDnaBlock()) {
blocks.add(fileBlock); blocks.add(fileBlock);
// save the scene's file block // save the scene's file block
if (fileBlock.getCode() == FileBlockHeader.BLOCK_SC00) { if (fileBlock.getCode() == BlockCode.BLOCK_SC00) {
sceneFileBlock = fileBlock; sceneFileBlock = fileBlock;
} }
} }
@ -287,4 +327,39 @@ public class BlenderLoader implements AssetLoader {
blenderContext = null; blenderContext = null;
blocks = 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<FileBlockHeader> sceneBlocks = new ArrayList<FileBlockHeader>();
/** The scenes from the file. */
private List<Node> scenes = new ArrayList<Node>();
/** Objects from all scenes. */
private List<Node> objects = new ArrayList<Node>();
/** All meshes. */
private List<TemporalMesh> meshes = new ArrayList<TemporalMesh>();
/** Materials from all objects. */
private List<MaterialContext> materials = new ArrayList<MaterialContext>();
/** Textures from all objects. */
private List<Texture> textures = new ArrayList<Texture>();
/** The images stored in the blender file. */
private List<Texture> images = new ArrayList<Texture>();
/** Animations of all objects. */
private List<Animation> animations = new ArrayList<Animation>();
/** All cameras from the file. */
private List<Camera> cameras = new ArrayList<Camera>();
/** All lights from the file. */
private List<Light> lights = new ArrayList<Light>();
/** Loaded sky. */
private Spatial sky;
/** Scene filters (ie. FOG). */
private List<Filter> filters = new ArrayList<Filter>();
/**
* The background color of the render loaded from the horizon color of the world. If no world is used than the gray color
* is set to default (as in blender editor.
*/
private ColorRGBA backgroundColor = ColorRGBA.Gray;
}
} }

@ -31,79 +31,10 @@
*/ */
package com.jme3.scene.plugins.blender; 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. * 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) * @author Marcin Roguski (Kaelthas)
*/ */
public class BlenderModelLoader extends BlenderLoader { 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<Node> rootObjects = new ArrayList<Node>();
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();
}
}
} }

@ -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.BlenderFileException;
import com.jme3.scene.plugins.blender.file.BlenderInputStream; import com.jme3.scene.plugins.blender.file.BlenderInputStream;
import com.jme3.scene.plugins.blender.file.FileBlockHeader; 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.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.objects.ObjectHelper; import com.jme3.scene.plugins.blender.objects.ObjectHelper;
@ -48,7 +49,7 @@ public class AnimationHelper extends AbstractBlenderHelper {
*/ */
public void loadAnimations() throws BlenderFileException { public void loadAnimations() throws BlenderFileException {
LOGGER.info("Loading animations that will be later applied to scene features."); LOGGER.info("Loading animations that will be later applied to scene features.");
List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00)); List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(BlockCode.BLOCK_AC00);
if (actionHeaders != null) { if (actionHeaders != null) {
for (FileBlockHeader header : actionHeaders) { for (FileBlockHeader header : actionHeaders) {
Structure actionStructure = header.getStructure(blenderContext); Structure actionStructure = header.getStructure(blenderContext);
@ -70,7 +71,7 @@ public class AnimationHelper extends AbstractBlenderHelper {
if (actions.size() > 0) { if (actions.size() > 0) {
List<Animation> animations = new ArrayList<Animation>(); List<Animation> animations = new ArrayList<Animation>();
for (BlenderAction action : actions) { for (BlenderAction action : actions) {
SpatialTrack[] tracks = action.toTracks(node); SpatialTrack[] tracks = action.toTracks(node, blenderContext);
if (tracks != null && tracks.length > 0) { if (tracks != null && tracks.length > 0) {
Animation spatialAnimation = new Animation(action.getName(), action.getAnimationTime()); Animation spatialAnimation = new Animation(action.getName(), action.getAnimationTime());
spatialAnimation.setTracks(tracks); spatialAnimation.setTracks(tracks);
@ -109,7 +110,7 @@ public class AnimationHelper extends AbstractBlenderHelper {
if (actions.size() > 0) { if (actions.size() > 0) {
List<Animation> animations = new ArrayList<Animation>(); List<Animation> animations = new ArrayList<Animation>();
for (BlenderAction action : actions) { for (BlenderAction action : actions) {
BoneTrack[] tracks = action.toTracks(skeleton); BoneTrack[] tracks = action.toTracks(skeleton, blenderContext);
if (tracks != null && tracks.length > 0) { if (tracks != null && tracks.length > 0) {
Animation boneAnimation = new Animation(action.getName(), action.getAnimationTime()); Animation boneAnimation = new Animation(action.getName(), action.getAnimationTime());
boneAnimation.setTracks(tracks); boneAnimation.setTracks(tracks);

@ -10,9 +10,8 @@ import java.util.Map.Entry;
import com.jme3.animation.BoneTrack; import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton; import com.jme3.animation.Skeleton;
import com.jme3.animation.SpatialTrack; import com.jme3.animation.SpatialTrack;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.BlenderContext;
/** /**
* An abstract representation of animation. The data stored here is mainly a * 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 * the node that will be animated
* @return the spatial tracks for the node * @return the spatial tracks for the node
*/ */
public SpatialTrack[] toTracks(Node node) { public SpatialTrack[] toTracks(Node node, BlenderContext blenderContext) {
List<SpatialTrack> tracks = new ArrayList<SpatialTrack>(featuresTracks.size()); List<SpatialTrack> tracks = new ArrayList<SpatialTrack>(featuresTracks.size());
for (Entry<String, Ipo> entry : featuresTracks.entrySet()) { for (Entry<String, Ipo> 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()]); return tracks.toArray(new SpatialTrack[tracks.size()]);
} }
@ -84,11 +83,12 @@ public class BlenderAction implements Cloneable {
* the skeleton that will be animated * the skeleton that will be animated
* @return the bone tracks for the node * @return the bone tracks for the node
*/ */
public BoneTrack[] toTracks(Skeleton skeleton) { public BoneTrack[] toTracks(Skeleton skeleton, BlenderContext blenderContext) {
List<BoneTrack> tracks = new ArrayList<BoneTrack>(featuresTracks.size()); List<BoneTrack> tracks = new ArrayList<BoneTrack>(featuresTracks.size());
for (Entry<String, Ipo> entry : featuresTracks.entrySet()) { for (Entry<String, Ipo> entry : featuresTracks.entrySet()) {
int boneIndex = skeleton.getBoneIndex(entry.getKey()); 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()]); return tracks.toArray(new BoneTrack[tracks.size()]);
} }

@ -25,9 +25,12 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
*/ */
public class BoneContext { public class BoneContext {
// the flags of the bone // the flags of the bone
public static final int SELECTED = 0x0001; public static final int SELECTED = 0x000001;
public static final int CONNECTED_TO_PARENT = 0x0010; public static final int CONNECTED_TO_PARENT = 0x000010;
public static final int DEFORM = 0x1000; 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). * 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).

@ -137,7 +137,7 @@ public class Ipo {
* as jme while other features have different one (Z is UP) * as jme while other features have different one (Z is UP)
* @return bone track for the specified bone * @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) { if (calculatedTrack == null) {
// preparing data for track // preparing data for track
int framesAmount = stopFrame - startFrame; int framesAmount = stopFrame - startFrame;
@ -236,6 +236,15 @@ public class Ipo {
} }
} }
translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2])); 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) { if (queternionRotationUsed) {
rotations[index] = new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); rotations[index] = new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
} else { } else {
@ -292,7 +301,7 @@ public class Ipo {
} }
@Override @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!"); throw new IllegalStateException("Constatnt ipo object cannot be used for calculating bone tracks!");
} }
} }

@ -5,7 +5,6 @@ import java.util.logging.Logger;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.file.BlenderFileException; 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 * an exception is thrown when there are problems with the
* blender file * blender file
*/ */
public CameraNode toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException { public Camera toCamera(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
if (blenderVersion >= 250) { if (blenderVersion >= 250) {
return this.toCamera250(structure, blenderContext.getSceneStructure()); return this.toCamera250(structure, blenderContext.getSceneStructure());
} else { } else {
@ -63,7 +62,7 @@ public class CameraHelper extends AbstractBlenderHelper {
* an exception is thrown when there are problems with the * an exception is thrown when there are problems with the
* blender file * 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 width = DEFAULT_CAM_WIDTH;
int height = DEFAULT_CAM_HEIGHT; int height = DEFAULT_CAM_HEIGHT;
if (sceneStructure != null) { if (sceneStructure != null) {
@ -99,7 +98,7 @@ public class CameraHelper extends AbstractBlenderHelper {
sensor = ((Number) structure.getFieldValue(sensorName)).floatValue(); sensor = ((Number) structure.getFieldValue(sensorName)).floatValue();
} }
float focalLength = ((Number) structure.getFieldValue("lens")).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) { if (sensorVertical) {
fovY = fov * FastMath.RAD_TO_DEG; fovY = fov * FastMath.RAD_TO_DEG;
} else { } else {
@ -111,7 +110,8 @@ public class CameraHelper extends AbstractBlenderHelper {
fovY = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); fovY = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
} }
camera.setFrustumPerspective(fovY, aspect, clipsta, clipend); 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 * an exception is thrown when there are problems with the
* blender file * 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); Camera camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
int type = ((Number) structure.getFieldValue("type")).intValue(); int type = ((Number) structure.getFieldValue("type")).intValue();
if (type != 0 && type != 1) { if (type != 0 && type != 1) {
@ -142,6 +142,7 @@ public class CameraHelper extends AbstractBlenderHelper {
aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue(); aspect = ((Number) structure.getFieldValue("ortho_scale")).floatValue();
} }
camera.setFrustumPerspective(aspect, camera.getWidth() / camera.getHeight(), clipsta, clipend); camera.setFrustumPerspective(aspect, camera.getWidth() / camera.getHeight(), clipsta, clipend);
return new CameraNode(null, camera); camera.setName(structure.getName());
return camera;
} }
} }

@ -1,10 +1,11 @@
package com.jme3.scene.plugins.blender.curves; package com.jme3.scene.plugins.blender.curves;
import java.util.ArrayList;
import java.util.List;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.file.DynamicArray; import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Structure; 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 * A class that helps to calculate the bezier curves calues. It uses doubles for performing calculations to minimize
@ -12,6 +13,9 @@ import java.util.List;
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public class BezierCurve { 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 X_VALUE = 0;
public static final int Y_VALUE = 1; public static final int Y_VALUE = 1;
@ -27,6 +31,8 @@ public class BezierCurve {
private double[][][] bezierPoints; private double[][][] bezierPoints;
/** Array that stores a radius for each bezier triple. */ /** 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<Structure> bezTriples, final int dimension) { public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension) {
this(type, bezTriples, dimension, false); 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 // the third index specifies the coordinates of the specific point in a bezier triple
bezierPoints = new double[bezTriples.size()][3][dimension]; bezierPoints = new double[bezTriples.size()][3][dimension];
radiuses = new double[bezTriples.size()]; radiuses = new double[bezTriples.size()];
interpolations = new int[bezTriples.size()];
int i = 0, j, k; int i = 0, j, k;
for (Structure bezTriple : bezTriples) { for (Structure bezTriple : bezTriples) {
DynamicArray<Number> vec = (DynamicArray<Number>) bezTriple.getFieldValue("vec"); DynamicArray<Number> vec = (DynamicArray<Number>) bezTriple.getFieldValue("vec");
@ -57,7 +64,8 @@ public class BezierCurve {
bezierPoints[i][j][1] = temp; 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) { for (int i = 0; i < bezierPoints.length - 1; ++i) {
if (frame >= bezierPoints[i][1][0] && frame <= bezierPoints[i + 1][1][0]) { 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 t = (frame - bezierPoints[i][1][0]) / (bezierPoints[i + 1][1][0] - bezierPoints[i][1][0]);
switch (interpolations[i]) {
case IPO_BEZIER:
double oneMinusT = 1.0f - t; double oneMinusT = 1.0f - t;
double oneMinusT2 = oneMinusT * oneMinusT; double oneMinusT2 = oneMinusT * oneMinusT;
double t2 = t * t; 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; 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]) { if (frame < bezierPoints[0][1][0]) {

@ -31,6 +31,8 @@
*/ */
package com.jme3.scene.plugins.blender.file; package com.jme3.scene.plugins.blender.file;
import java.util.logging.Logger;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
/** /**
@ -39,26 +41,10 @@ import com.jme3.scene.plugins.blender.BlenderContext;
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class FileBlockHeader { 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]. */ /** 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]. */ /** Total length of the data after the file-block-header [4 bytes]. */
private int size; private int size;
/** /**
@ -84,13 +70,13 @@ public class FileBlockHeader {
*/ */
public FileBlockHeader(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException { public FileBlockHeader(BlenderInputStream inputStream, BlenderContext blenderContext) throws BlenderFileException {
inputStream.alignPosition(4); 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(); size = inputStream.readInt();
oldMemoryAddress = inputStream.readPointer(); oldMemoryAddress = inputStream.readPointer();
sdnaIndex = inputStream.readInt(); sdnaIndex = inputStream.readInt();
count = inputStream.readInt(); count = inputStream.readInt();
blockPosition = inputStream.getPosition(); blockPosition = inputStream.getPosition();
if (FileBlockHeader.BLOCK_DNA1 == code) { if (BlockCode.BLOCK_DNA1 == code) {
blenderContext.setBlockData(new DnaBlockData(inputStream, blenderContext)); blenderContext.setBlockData(new DnaBlockData(inputStream, blenderContext));
} else { } else {
inputStream.setPosition(blockPosition + size); inputStream.setPosition(blockPosition + size);
@ -116,7 +102,7 @@ public class FileBlockHeader {
* This method returns the code of this data block. * This method returns the code of this data block.
* @return the code of this data block * @return the code of this data block
*/ */
public int getCode() { public BlockCode getCode() {
return code; return code;
} }
@ -157,7 +143,7 @@ public class FileBlockHeader {
* @return true if this block is the last one in the file nad false otherwise * @return true if this block is the last one in the file nad false otherwise
*/ */
public boolean isLastBlock() { 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 * @return true if this block is the SDNA block and false otherwise
*/ */
public boolean isDnaBlock() { public boolean isDnaBlock() {
return FileBlockHeader.BLOCK_DNA1 == code; return BlockCode.BLOCK_DNA1 == code;
} }
@Override @Override
public String toString() { public String toString() {
return "FILE BLOCK HEADER [" + this.codeToString(code) + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]"; return "FILE BLOCK HEADER [" + code.toString() + " : " + size + " : " + oldMemoryAddress + " : " + sdnaIndex + " : " + count + "]";
} }
/** public static enum BlockCode {
* This method transforms the coded bloch id into a string value. BLOCK_ME00('M' << 24 | 'E' << 16), // mesh
* @param code BLOCK_CA00('C' << 24 | 'A' << 16), // camera
* the id of the block BLOCK_LA00('L' << 24 | 'A' << 16), // lamp
* @return the string value of the block id BLOCK_OB00('O' << 24 | 'B' << 16), // object
*/ BLOCK_MA00('M' << 24 | 'A' << 16), // material
protected String codeToString(int code) { BLOCK_SC00('S' << 24 | 'C' << 16), // scene
char c1 = (char) ((code & 0xFF000000) >> 24); BLOCK_WO00('W' << 24 | 'O' << 16), // world
char c2 = (char) ((code & 0xFF0000) >> 16); BLOCK_TX00('T' << 24 | 'X' << 16), // texture
char c3 = (char) ((code & 0xFF00) >> 8); BLOCK_IP00('I' << 24 | 'P' << 16), // ipo
char c4 = (char) (code & 0xFF); BLOCK_AC00('A' << 24 | 'C' << 16), // action
return String.valueOf(c1) + c2 + c3 + c4; 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;
}
} }
} }

@ -254,7 +254,8 @@ public class Structure implements Cloneable {
Structure id = (Structure) fieldValue; Structure id = (Structure) fieldValue;
return id == null ? null : id.getFieldValue("name").toString().substring(2);// blender adds 2-charactes as a name prefix 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 @Override

@ -208,6 +208,6 @@ public class LandscapeHelper extends AbstractBlenderHelper {
} }
LOGGER.fine("Sky texture created. Creating sky."); LOGGER.fine("Sky texture created. Creating sky.");
return SkyFactory.createSky(blenderContext.getAssetManager(), texture, false); return SkyFactory.createSky(blenderContext.getAssetManager(), texture, SkyFactory.EnvMapType.CubeMap);
} }
} }

@ -40,7 +40,6 @@ import com.jme3.light.PointLight;
import com.jme3.light.SpotLight; import com.jme3.light.SpotLight;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.scene.LightNode;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper; import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext; import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
@ -67,8 +66,8 @@ public class LightHelper extends AbstractBlenderHelper {
super(blenderVersion, blenderContext); super(blenderVersion, blenderContext);
} }
public LightNode toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException { public Light toLight(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
LightNode result = (LightNode) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE); Light result = (Light) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (result != null) { if (result != null) {
return result; return result;
} }
@ -111,6 +110,7 @@ public class LightHelper extends AbstractBlenderHelper {
float g = ((Number) structure.getFieldValue("g")).floatValue(); float g = ((Number) structure.getFieldValue("g")).floatValue();
float b = ((Number) structure.getFieldValue("b")).floatValue(); float b = ((Number) structure.getFieldValue("b")).floatValue();
light.setColor(new ColorRGBA(r, g, b, 1.0f)); light.setColor(new ColorRGBA(r, g, b, 1.0f));
return new LightNode(structure.getName(), light); light.setName(structure.getName());
return light;
} }
} }

@ -1,11 +1,15 @@
package com.jme3.scene.plugins.blender.materials; package com.jme3.scene.plugins.blender.materials;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; 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.Material;
import com.jme3.material.RenderState.BlendMode; import com.jme3.material.RenderState.BlendMode;
import com.jme3.material.RenderState.FaceCullMode; import com.jme3.material.RenderState.FaceCullMode;
@ -30,7 +34,7 @@ import com.jme3.util.BufferUtils;
* This class holds the data about the material. * This class holds the data about the material.
* @author Marcin Roguski (Kaelthas) * @author Marcin Roguski (Kaelthas)
*/ */
public final class MaterialContext { public final class MaterialContext implements Savable {
private static final Logger LOGGER = Logger.getLogger(MaterialContext.class.getName()); private static final Logger LOGGER = Logger.getLogger(MaterialContext.class.getName());
// texture mapping types // texture mapping types
@ -107,6 +111,13 @@ public final class MaterialContext {
this.transparent = transparent; this.transparent = transparent;
} }
/**
* @return the name of the material
*/
public String getName() {
return name;
}
/** /**
* Applies material to a given geometry. * Applies material to a given geometry.
* *
@ -314,4 +325,14 @@ public final class MaterialContext {
float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue(); float alpha = ((Number) materialStructure.getFieldValue("alpha")).floatValue();
return new ColorRGBA(r, g, b, alpha); 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!");
}
} }

@ -51,6 +51,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import com.jme3.texture.image.ColorSpace;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
@ -161,12 +162,17 @@ public class MaterialHelper extends AbstractBlenderHelper {
* an exception is throw when problems with blend file occur * an exception is throw when problems with blend file occur
*/ */
public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException { public MaterialContext toMaterialContext(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.FINE, "Loading material.");
MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE); MaterialContext result = (MaterialContext) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (result != null) { if (result != null) {
return result; 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); result = new MaterialContext(structure, blenderContext);
LOGGER.log(Level.FINE, "Material''s name: {0}", result.name); LOGGER.log(Level.FINE, "Material''s name: {0}", result.name);
Long oma = structure.getOldMemoryAddress(); 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); texture.setImage(image);
result.setTextureParam("Texture", VarType.Texture2D, texture); result.setTextureParam("Texture", VarType.Texture2D, texture);

@ -40,7 +40,6 @@ import java.util.Map.Entry;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.asset.BlenderKey.FeaturesToLoad;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f; 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.DynamicArray;
import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; 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.materials.MaterialHelper;
import com.jme3.scene.plugins.blender.objects.Properties; import com.jme3.scene.plugins.blender.objects.Properties;
@ -106,17 +104,18 @@ public class MeshHelper extends AbstractBlenderHelper {
return temporalMesh.clone(); 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(); String name = meshStructure.getName();
LOGGER.log(Level.FINE, "Reading mesh: {0}.", name); LOGGER.log(Level.FINE, "Reading mesh: {0}.", name);
temporalMesh = new TemporalMesh(meshStructure, blenderContext); temporalMesh = new TemporalMesh(meshStructure, blenderContext);
LOGGER.fine("Loading materials."); LOGGER.fine("Loading materials.");
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
MaterialContext[] materials = null; temporalMesh.setMaterials(materialHelper.getMaterials(meshStructure, blenderContext));
if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
materials = materialHelper.getMaterials(meshStructure, blenderContext);
}
temporalMesh.setMaterials(materials);
LOGGER.fine("Reading custom properties."); LOGGER.fine("Reading custom properties.");
Properties properties = this.loadProperties(meshStructure, blenderContext); Properties properties = this.loadProperties(meshStructure, blenderContext);

@ -40,12 +40,15 @@ import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; 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.FastMath;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.math.Transform; import com.jme3.math.Transform;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.LightNode;
import com.jme3.scene.Mesh.Mode; import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
@ -106,35 +109,30 @@ public class ObjectHelper extends AbstractBlenderHelper {
* an exception is thrown when the given data is inapropriate * an exception is thrown when the given data is inapropriate
*/ */
public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException { 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(); int type = ((Number) objectStructure.getFieldValue("type")).intValue();
ObjectType objectType = ObjectType.valueOf(type); ObjectType objectType = ObjectType.valueOf(type);
LOGGER.log(Level.FINE, "Type of the object: {0}.", objectType); 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(); int lay = ((Number) objectStructure.getFieldValue("lay")).intValue();
if ((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) { if ((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) {
LOGGER.fine("The layer this object is located in is not included in loading."); LOGGER.fine("The layer this object is located in is not included in loading.");
return null; 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); blenderContext.pushParent(objectStructure);
String name = objectStructure.getName(); String name = objectStructure.getName();
LOGGER.log(Level.FINE, "Loading obejct: {0}", name); LOGGER.log(Level.FINE, "Loading obejct: {0}", name);
@ -193,10 +191,12 @@ public class ObjectHelper extends AbstractBlenderHelper {
if (pLamp.isNotNull()) { if (pLamp.isNotNull()) {
LightHelper lightHelper = blenderContext.getHelper(LightHelper.class); LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
List<Structure> lampsArray = pLamp.fetchData(); List<Structure> lampsArray = pLamp.fetchData();
result = lightHelper.toLight(lampsArray.get(0), blenderContext); Light light = lightHelper.toLight(lampsArray.get(0), blenderContext);
if (result == null) { if (light == null) {
// probably some light type is not supported, just create a node so that we can maintain child-parent relationship for nodes // 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); result = new Node(name);
} else {
result = new LightNode(name, light);
} }
} }
break; break;
@ -205,7 +205,13 @@ public class ObjectHelper extends AbstractBlenderHelper {
if (pCamera.isNotNull()) { if (pCamera.isNotNull()) {
CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class); CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
List<Structure> camerasArray = pCamera.fetchData(); List<Structure> 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; break;
default: default:

@ -444,13 +444,17 @@ public class CombinedTexture {
case RGB8: case RGB8:
return true;// these types have no alpha by definition return true;// these types have no alpha by definition
case ABGR8: case ABGR8:
case DXT1A:
case DXT3: case DXT3:
case DXT5: case DXT5:
case Luminance16FAlpha16F: case Luminance16FAlpha16F:
case Luminance8Alpha8: case Luminance8Alpha8:
case RGBA16F: case RGBA16F:
case RGBA32F: 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()); PixelInputOutput pixelInputOutput = PixelIOFactory.getPixelIO(image.getFormat());
TexturePixel pixel = new TexturePixel(); TexturePixel pixel = new TexturePixel();
int depth = image.getDepth() == 0 ? 1 : image.getDepth(); int depth = image.getDepth() == 0 ? 1 : image.getDepth();
@ -465,6 +469,8 @@ public class CombinedTexture {
} }
} }
return true; return true;
default:
throw new IllegalStateException("Unknown image format: " + image.getFormat());
} }
} }
} }

@ -41,13 +41,13 @@ public final class ImageUtils {
public static Image createEmptyImage(Format format, int width, int height, int depth) { public static Image createEmptyImage(Format format, int width, int height, int depth) {
int bufferSize = width * height * (format.getBitsPerPixel() >> 3); int bufferSize = width * height * (format.getBitsPerPixel() >> 3);
if (depth < 2) { 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<ByteBuffer> data = new ArrayList<ByteBuffer>(depth); ArrayList<ByteBuffer> data = new ArrayList<ByteBuffer>(depth);
for (int i = 0; i < depth; ++i) { for (int i = 0; i < depth; ++i) {
data.add(BufferUtils.createByteBuffer(bufferSize)); 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[0] = data.get() * 255.0f;
alphas[1] = 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 //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. if (alphas[0] > alphas[1]) {// 6 interpolated alpha values.
alphas[2] = (6 * alphas[0] + alphas[1]) / 7; alphas[2] = (6 * alphas[0] + alphas[1]) / 7;
alphas[3] = (5 * alphas[0] + 2 * 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)); 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) { if (newMipmapSizes != null) {
result.setMipMapSizes(newMipmapSizes); result.setMipMapSizes(newMipmapSizes);
} }
@ -467,6 +468,6 @@ public final class ImageUtils {
private static Image toJmeImage(BufferedImage bufferedImage, Format format) { private static Image toJmeImage(BufferedImage bufferedImage, Format format) {
ByteBuffer byteBuffer = BufferUtils.createByteBuffer(bufferedImage.getWidth() * bufferedImage.getHeight() * 3); ByteBuffer byteBuffer = BufferUtils.createByteBuffer(bufferedImage.getWidth() * bufferedImage.getHeight() * 3);
ImageToAwt.convert(bufferedImage, format, byteBuffer); 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);
} }
} }

@ -121,7 +121,7 @@ public class TextureHelper extends AbstractBlenderHelper {
* data. The returned texture has the name set to the value of its blender * data. The returned texture has the name set to the value of its blender
* type. * type.
* *
* @param tex * @param textureStructure
* texture structure filled with data * texture structure filled with data
* @param blenderContext * @param blenderContext
* the blender context * the blender context
@ -130,23 +130,29 @@ public class TextureHelper extends AbstractBlenderHelper {
* this exception is thrown when the blend file structure is * this exception is thrown when the blend file structure is
* somehow invalid or corrupted * somehow invalid or corrupted
*/ */
public Texture getTexture(Structure tex, Structure mTex, BlenderContext blenderContext) throws BlenderFileException { public Texture getTexture(Structure textureStructure, Structure mTex, BlenderContext blenderContext) throws BlenderFileException {
Texture result = (Texture) blenderContext.getLoadedFeature(tex.getOldMemoryAddress(), LoadedDataType.FEATURE); Texture result = (Texture) blenderContext.getLoadedFeature(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (result != null) { if (result != null) {
return result; 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) { switch (type) {
case TEX_IMAGE:// (it is first because probably this will be most commonly used) 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()) { if (pImage.isNotNull()) {
Structure image = pImage.fetchData().get(0); Structure image = pImage.fetchData().get(0);
Texture loadedTexture = this.loadTexture(image, imaflag, blenderContext); Texture loadedTexture = this.loadImageAsTexture(image, imaflag, blenderContext);
if (loadedTexture != null) { if (loadedTexture != null) {
result = loadedTexture; result = loadedTexture;
this.applyColorbandAndColorFactors(tex, result.getImage(), blenderContext); this.applyColorbandAndColorFactors(textureStructure, result.getImage(), blenderContext);
} }
} }
break; break;
@ -160,7 +166,7 @@ public class TextureHelper extends AbstractBlenderHelper {
case TEX_MUSGRAVE: case TEX_MUSGRAVE:
case TEX_VORONOI: case TEX_VORONOI:
case TEX_DISTNOISE: case TEX_DISTNOISE:
result = new GeneratedTexture(tex, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext); result = new GeneratedTexture(textureStructure, mTex, textureGeneratorFactory.createTextureGenerator(type), blenderContext);
break; break;
case TEX_NONE:// No texture, do nothing case TEX_NONE:// No texture, do nothing
break; break;
@ -169,13 +175,13 @@ public class TextureHelper extends AbstractBlenderHelper {
case TEX_PLUGIN: case TEX_PLUGIN:
case TEX_ENVMAP: case TEX_ENVMAP:
case TEX_OCEAN: 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; break;
default: 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) { if (result != null) {
result.setName(tex.getName()); result.setName(textureStructure.getName());
result.setWrap(WrapMode.Repeat); result.setWrap(WrapMode.Repeat);
// decide if the mipmaps will be generated // 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 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)) { 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(textureStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, textureStructure);
blenderContext.addLoadedFeatures(tex.getOldMemoryAddress(), LoadedDataType.FEATURE, result); blenderContext.addLoadedFeatures(textureStructure.getOldMemoryAddress(), LoadedDataType.FEATURE, result);
} }
return result; return result;
} }
@ -222,11 +228,15 @@ public class TextureHelper extends AbstractBlenderHelper {
* this exception is thrown when the blend file structure is * this exception is thrown when the blend file structure is
* somehow invalid or corrupted * 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()); LOGGER.log(Level.FINE, "Fetching texture with OMA = {0}", imageStructure.getOldMemoryAddress());
Texture result = null; Texture result = null;
Image im = (Image) blenderContext.getLoadedFeature(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE); Image im = (Image) blenderContext.getLoadedFeature(imageStructure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (im == null) { // 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 {
String texturePath = imageStructure.getFieldValue("name").toString(); String texturePath = imageStructure.getFieldValue("name").toString();
Pointer pPackedFile = (Pointer) imageStructure.getFieldValue("packedfile"); Pointer pPackedFile = (Pointer) imageStructure.getFieldValue("packedfile");
if (pPackedFile.isNull()) { if (pPackedFile.isNull()) {
@ -238,13 +248,19 @@ public class TextureHelper extends AbstractBlenderHelper {
Pointer pData = (Pointer) packedFile.getFieldValue("data"); Pointer pData = (Pointer) packedFile.getFieldValue("data");
FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress()); FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pData.getOldMemoryAddress());
blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition()); blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition());
ImageLoader imageLoader = new ImageLoader();
// Should the texture be flipped? It works for sinbad .. // Should the texture be flipped? It works for sinbad ..
result = new Texture2D(imageLoader.loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true)); 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; return result;
} }
@ -524,6 +540,18 @@ public class TextureHelper extends AbstractBlenderHelper {
return result; 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") @SuppressWarnings("unchecked")
public List<CombinedTexture> readTextureData(Structure structure, float[] diffuseColorArray, boolean skyTexture) throws BlenderFileException { public List<CombinedTexture> readTextureData(Structure structure, float[] diffuseColorArray, boolean skyTexture) throws BlenderFileException {
DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex"); DynamicArray<Pointer> mtexsArray = (DynamicArray<Pointer>) structure.getFieldValue("mtex");

@ -31,6 +31,7 @@ import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D; import com.jme3.texture.Texture2D;
import com.jme3.texture.image.ColorSpace;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
/** /**
@ -77,7 +78,7 @@ import com.jme3.util.BufferUtils;
for (int i = 0; i < facesCount; ++i) { for (int i = 0; i < facesCount; ++i) {
faceTextures.add(new TriangleTextureElement(i, texture2d.getImage(), uvs, true, blenderContext)); 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) { public void blend(TextureBlender textureBlender, TriangulatedTexture baseTexture, BlenderContext blenderContext) {
Format newFormat = null; Format newFormat = null;
for (TriangleTextureElement triangleTextureElement : this.faceTextures) { for (TriangleTextureElement triangleTextureElement : faceTextures) {
Image baseImage = baseTexture == null ? null : baseTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image; Image baseImage = baseTexture == null ? null : baseTexture.getFaceTextureElement(triangleTextureElement.faceIndex).image;
triangleTextureElement.image = textureBlender.blend(triangleTextureElement.image, baseImage, blenderContext); triangleTextureElement.image = textureBlender.blend(triangleTextureElement.image, baseImage, blenderContext);
if (newFormat == null) { 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!"); 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]); 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); resultTexture = new Texture2D(resultImage);
for (Entry<TriangleTextureElement, Integer[]> entry : imageLayoutData.entrySet()) { for (Entry<TriangleTextureElement, Integer[]> entry : imageLayoutData.entrySet()) {
if (!duplicatedFaceIndexes.contains(entry.getKey().faceIndex)) { if (!duplicatedFaceIndexes.contains(entry.getKey().faceIndex)) {
@ -420,7 +421,7 @@ import com.jme3.util.BufferUtils;
data.put(pixel.getA8()); 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 // modify the UV values so that they fit the new image
float heightUV = maxUVY - minUVY; float heightUV = maxUVY - minUVY;
@ -481,7 +482,7 @@ import com.jme3.util.BufferUtils;
imageHeight = 1; imageHeight = 1;
} }
ByteBuffer data = BufferUtils.createByteBuffer(imageWidth * imageHeight * (imageFormat.getBitsPerPixel() >> 3)); 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 // computing the pixels
PixelInputOutput pixelWriter = PixelIOFactory.getPixelIO(imageFormat); 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) { public void computeFinalUVCoordinates(int totalImageWidth, int totalImageHeight, int xPos, int yPos, Vector2f[] result) {
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
result[i] = new Vector2f(); result[i] = new Vector2f();
result[i].x = xPos / (float) totalImageWidth + this.uv[i].x * (this.image.getWidth() / (float) totalImageWidth); result[i].x = xPos / (float) totalImageWidth + uv[i].x * (image.getWidth() / (float) totalImageWidth);
result[i].y = yPos / (float) totalImageHeight + this.uv[i].y * (this.image.getHeight() / (float) totalImageHeight); 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 * a position in 3D space
*/ */
public RectangleEnvelope(Vector3f pointPosition) { public RectangleEnvelope(Vector3f pointPosition) {
this.min = pointPosition; min = pointPosition;
this.h = this.w = Vector3f.ZERO; h = w = Vector3f.ZERO;
this.width = this.height = 1; width = height = 1;
} }
/** /**
@ -642,8 +643,8 @@ import com.jme3.util.BufferUtils;
this.min = min; this.min = min;
this.h = h; this.h = h;
this.w = w; this.w = w;
this.width = w.length(); width = w.length();
this.height = h.length(); height = h.length();
} }
@Override @Override

@ -1,10 +1,12 @@
package com.jme3.scene.plugins.blender.textures.blending; 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.BlenderContext;
import com.jme3.scene.plugins.blender.materials.MaterialHelper; import com.jme3.scene.plugins.blender.materials.MaterialHelper;
import com.jme3.texture.Image; 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 * 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) { public void copyBlendingData(TextureBlender textureBlender) {
if (textureBlender instanceof AbstractTextureBlender) { if (textureBlender instanceof AbstractTextureBlender) {
this.flag = ((AbstractTextureBlender) textureBlender).flag; flag = ((AbstractTextureBlender) textureBlender).flag;
this.negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture; negateTexture = ((AbstractTextureBlender) textureBlender).negateTexture;
this.blendType = ((AbstractTextureBlender) textureBlender).blendType; blendType = ((AbstractTextureBlender) textureBlender).blendType;
this.materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone(); materialColor = ((AbstractTextureBlender) textureBlender).materialColor.clone();
this.color = ((AbstractTextureBlender) textureBlender).color.clone(); color = ((AbstractTextureBlender) textureBlender).color.clone();
this.blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor; blendFactor = ((AbstractTextureBlender) textureBlender).blendFactor;
} else { } else {
LOGGER.warning("Cannot copy blending data from other types than " + this.getClass()); LOGGER.warning("Cannot copy blending data from other types than " + this.getClass());
} }

@ -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.scene.plugins.blender.textures.io.PixelInputOutput;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import com.jme3.texture.image.ColorSpace;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@ -141,7 +143,7 @@ public class TextureBlenderAWT extends AbstractTextureBlender {
dataArray.add(newData); 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) { if (image.getMipMapSizes() != null) {
result.setMipMapSizes(image.getMipMapSizes().clone()); result.setMipMapSizes(image.getMipMapSizes().clone());
} }

@ -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.scene.plugins.blender.textures.io.PixelInputOutput;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import com.jme3.texture.image.ColorSpace;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import jme3tools.converters.RGB565; import jme3tools.converters.RGB565;
/** /**
@ -119,7 +122,7 @@ public class TextureBlenderDDS extends TextureBlenderAWT {
dataArray.add(newData); 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) { if (image.getMipMapSizes() != null) {
result.setMipMapSizes(image.getMipMapSizes().clone()); result.setMipMapSizes(image.getMipMapSizes().clone());
} }

@ -31,13 +31,13 @@
*/ */
package com.jme3.scene.plugins.blender.textures.blending; 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.scene.plugins.blender.BlenderContext;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; 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. * This class creates the texture blending class depending on the texture type.
* *
@ -66,7 +66,6 @@ public class TextureBlenderFactory {
* the texture format * the texture format
* @return texture blending class * @return texture blending class
*/ */
@SuppressWarnings("deprecation")
public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) { public static TextureBlender createTextureBlender(Format format, int flag, boolean negate, int blendType, float[] materialColor, float[] color, float colfac) {
switch (format) { switch (format) {
case Luminance8: case Luminance8:

@ -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.scene.plugins.blender.textures.io.PixelInputOutput;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import com.jme3.texture.image.ColorSpace;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.logging.Level; import java.util.logging.Level;
@ -93,7 +95,7 @@ public class TextureBlenderLuminance extends AbstractTextureBlender {
dataArray.add(newData); 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) { if (image.getMipMapSizes() != null) {
result.setMipMapSizes(image.getMipMapSizes().clone()); result.setMipMapSizes(image.getMipMapSizes().clone());
} }

@ -17,12 +17,18 @@ import jme3tools.converters.RGB565;
case RGBA8: case RGBA8:
pixel.fromARGB8(data.get(index + 3), data.get(index), data.get(index + 1), data.get(index + 2)); pixel.fromARGB8(data.get(index + 3), data.get(index), data.get(index + 1), data.get(index + 2));
break; break;
case ARGB8:
pixel.fromARGB8(data.get(index), data.get(index + 1), data.get(index + 2), data.get(index + 3));
break;
case ABGR8: case ABGR8:
pixel.fromARGB8(data.get(index), data.get(index + 3), data.get(index + 2), data.get(index + 1)); pixel.fromARGB8(data.get(index), data.get(index + 3), data.get(index + 2), data.get(index + 1));
break; break;
case BGR8: case BGR8:
pixel.fromARGB8((byte) 0xFF, data.get(index + 2), data.get(index + 1), data.get(index)); pixel.fromARGB8((byte) 0xFF, data.get(index + 2), data.get(index + 1), data.get(index));
break; break;
case BGRA8:
pixel.fromARGB8(data.get(index + 3), data.get(index + 2), data.get(index + 1), data.get(index));
break;
case RGB8: case RGB8:
pixel.fromARGB8((byte) 0xFF, data.get(index), data.get(index + 1), data.get(index + 2)); pixel.fromARGB8((byte) 0xFF, data.get(index), data.get(index + 1), data.get(index + 2));
break; break;
@ -72,6 +78,12 @@ import jme3tools.converters.RGB565;
data.put(index + 2, pixel.getB8()); data.put(index + 2, pixel.getB8());
data.put(index + 3, pixel.getA8()); data.put(index + 3, pixel.getA8());
break; 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: case ABGR8:
data.put(index, pixel.getA8()); data.put(index, pixel.getA8());
data.put(index + 1, pixel.getB8()); data.put(index + 1, pixel.getB8());
@ -83,6 +95,12 @@ import jme3tools.converters.RGB565;
data.put(index + 1, pixel.getG8()); data.put(index + 1, pixel.getG8());
data.put(index + 2, pixel.getR8()); data.put(index + 2, pixel.getR8());
break; 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: case RGB8:
data.put(index, pixel.getR8()); data.put(index, pixel.getR8());
data.put(index + 1, pixel.getG8()); data.put(index + 1, pixel.getG8());

@ -26,7 +26,9 @@ public class PixelIOFactory {
case ABGR8: case ABGR8:
case RGBA8: case RGBA8:
case BGR8: case BGR8:
case BGRA8:
case RGB8: case RGB8:
case ARGB8:
case RGB111110F: case RGB111110F:
case RGB16F: case RGB16F:
case RGB16F_to_RGB111110F: case RGB16F_to_RGB111110F:

@ -175,7 +175,7 @@ binaries.withType(SharedLibraryBinary) { binary ->
// Add depend on build // Add depend on build
jar.dependsOn builderTask jar.dependsOn builderTask
// Add output to libs folder // Add output to libs folder
task "copyBinaryToLibs${targetPlatform}"(type: Copy) { task "copyBinaryToLibs${targetPlatform}"(type: Copy, dependsOn: builderTask) {
from builderTask.outputFile from builderTask.outputFile
into "libs/native/${targetPlatform.operatingSystem.name}/${targetPlatform.architecture.name}" into "libs/native/${targetPlatform.operatingSystem.name}/${targetPlatform.architecture.name}"
} }

@ -468,6 +468,67 @@ extern "C" {
return; 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<jmePhysicsSpace*> (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<btCollisionShape*> (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 #ifdef __cplusplus
} }
#endif #endif

@ -165,6 +165,15 @@ JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_initNativePhysics
JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_finalizeNative JNIEXPORT void JNICALL Java_com_jme3_bullet_PhysicsSpace_finalizeNative
(JNIEnv *, jobject, jlong); (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 #ifdef __cplusplus
} }
#endif #endif

@ -59,6 +59,38 @@ void jmeBulletUtil::convert(JNIEnv* env, jobject in, btVector3* out) {
out->setZ(z); 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) { void jmeBulletUtil::convert(JNIEnv* env, const btVector3* in, jobject out) {
if (in == NULL || out == NULL) { if (in == NULL || out == NULL) {
jmeClasses::throwNPE(env); jmeClasses::throwNPE(env);
@ -325,3 +357,61 @@ void jmeBulletUtil::addResult(JNIEnv* env, jobject resultlist, btVector3* hitnor
return; 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);
}

@ -42,10 +42,13 @@ public:
static void convert(JNIEnv* env, jobject in, btVector3* out); static void convert(JNIEnv* env, jobject in, btVector3* out);
static void convert(JNIEnv* env, const btVector3* in, jobject 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, btMatrix3x3* out);
static void convert(JNIEnv* env, jobject in, btQuaternion* out);
static void convert(JNIEnv* env, const btMatrix3x3* in, jobject 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, jobject in, btMatrix3x3* out);
static void convertQuat(JNIEnv* env, const btMatrix3x3* in, jobject 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 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: private:
jmeBulletUtil(){}; jmeBulletUtil(){};
~jmeBulletUtil(){}; ~jmeBulletUtil(){};

@ -91,6 +91,21 @@ jfieldID jmeClasses::PhysicsRay_collisionObject;
jclass jmeClasses::PhysicsRay_listresult; jclass jmeClasses::PhysicsRay_listresult;
jmethodID jmeClasses::PhysicsRay_addmethod; 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 //private fields
//JNIEnv* jmeClasses::env; //JNIEnv* jmeClasses::env;
JavaVM* jmeClasses::vm; JavaVM* jmeClasses::vm;
@ -240,6 +255,70 @@ void jmeClasses::initJavaClasses(JNIEnv* env) {
env->Throw(env->ExceptionOccurred()); env->Throw(env->ExceptionOccurred());
return; 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, "<init>", "()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) { void jmeClasses::throwNPE(JNIEnv* env) {

@ -89,6 +89,18 @@ public:
static jclass PhysicsRay_listresult; static jclass PhysicsRay_listresult;
static jmethodID PhysicsRay_addmethod; 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 jclass DebugMeshCallback;
static jmethodID DebugMeshCallback_addVector; static jmethodID DebugMeshCallback_addVector;

@ -820,6 +820,10 @@ public class PhysicsSpace {
// return lrr.hitFraction; // return lrr.hitFraction;
// } // }
// } // }
//
//
/** /**
* Performs a sweep collision test and returns the results as a list of * Performs a sweep collision test and returns the results as a list of
* PhysicsSweepTestResults<br/> You have to use different Transforms for * PhysicsSweepTestResults<br/> You have to use different Transforms for
@ -828,48 +832,47 @@ public class PhysicsSpace {
* center. * center.
*/ */
public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end) { public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end) {
List<PhysicsSweepTestResult> results = new LinkedList<PhysicsSweepTestResult>(); List results = new LinkedList();
// if (!(shape.getCShape() instanceof ConvexShape)) { sweepTest(shape, start, end , results);
// logger.log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!"); return (List<PhysicsSweepTestResult>) results;
// return results; }
// }
// dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results));
return results;
public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results) {
return sweepTest(shape, start, end, results, 0.0f);
} }
public native void sweepTest_native(long shape, Transform from, Transform to, long physicsSpaceId, List<PhysicsSweepTestResult> results, float allowedCcdPenetration);
/** /**
* Performs a sweep collision test and returns the results as a list of * Performs a sweep collision test and returns the results as a list of
* PhysicsSweepTestResults<br/> You have to use different Transforms for * PhysicsSweepTestResults<br/> 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 * collision if it starts INSIDE an object and is moving AWAY from its
* center. * center.
*/ */
public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results) { public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results, float allowedCcdPenetration ) {
results.clear(); results.clear();
// if (!(shape.getCShape() instanceof ConvexShape)) { sweepTest_native(shape.getObjectId(), start, end, physicsSpaceId, results, allowedCcdPenetration);
// 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; return results;
} }
// private class InternalSweepListener extends CollisionWorld.ConvexResultCallback { /* private class InternalSweepListener extends CollisionWorld.ConvexResultCallback {
//
// private List<PhysicsSweepTestResult> results; private List<PhysicsSweepTestResult> results;
//
// public InternalSweepListener(List<PhysicsSweepTestResult> results) { public InternalSweepListener(List<PhysicsSweepTestResult> results) {
// this.results = results; this.results = results;
// } }
//
// @Override @Override
// public float addSingleResult(LocalConvexResult lcr, boolean bln) { public float addSingleResult(LocalConvexResult lcr, boolean bln) {
// PhysicsCollisionObject obj = (PhysicsCollisionObject) lcr.hitCollisionObject.getUserPointer(); PhysicsCollisionObject obj = (PhysicsCollisionObject) lcr.hitCollisionObject.getUserPointer();
// results.add(new PhysicsSweepTestResult(obj, Converter.convert(lcr.hitNormalLocal), lcr.hitFraction, bln)); results.add(new PhysicsSweepTestResult(obj, Converter.convert(lcr.hitNormalLocal), lcr.hitFraction, bln));
// return lcr.hitFraction; return lcr.hitFraction;
// } }
// } }
*/
/** /**
* destroys the current PhysicsSpace so that a new one can be created * destroys the current PhysicsSpace so that a new one can be created
*/ */

@ -26,14 +26,29 @@ import org.ajoberstar.grgit.*
task updateVersion << { task updateVersion << {
def verfile = file('src/main/java/com/jme3/system/JmeVersion.java')
def jmeGitHash
def jmeShortGitHash
def jmeBuildDate
def jmeBranchName
try {
def grgit = Grgit.open(project.file('.').parent) 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.")
def jmeGitHash = grgit.head().id jmeGitHash = ""
def jmeShortGitHash = grgit.head().abbreviatedId jmeShortGitHash = ""
def jmeBuildDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date()) jmeBuildDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date())
def jmeBranchName = grgit.branch.current.name jmeBranchName = "unknown"
}
def verfile = file('src/main/java/com/jme3/system/JmeVersion.java')
verfile.text = "\npackage com.jme3.system;\n\n" + verfile.text = "\npackage com.jme3.system;\n\n" +
"/**\n * THIS IS AN AUTO-GENERATED FILE..\n * DO NOT MODIFY!\n */\n" + "/**\n * THIS IS AN AUTO-GENERATED FILE..\n * DO NOT MODIFY!\n */\n" +
"public class JmeVersion {\n" + "public class JmeVersion {\n" +

@ -312,7 +312,6 @@ public final class AnimChannel {
} }
} }
animation = null; animation = null;
// System.out.println("Setting notified false");
notified = false; notified = false;
} }

@ -69,7 +69,7 @@ public final class AssetConfig {
public static void loadText(AssetManager assetManager, URL configUrl) throws IOException{ public static void loadText(AssetManager assetManager, URL configUrl) throws IOException{
InputStream in = configUrl.openStream(); InputStream in = configUrl.openStream();
try { try {
Scanner scan = new Scanner(in); Scanner scan = new Scanner(in, "UTF-8");
scan.useLocale(Locale.US); // Fix commas / periods ?? scan.useLocale(Locale.US); // Fix commas / periods ??
while (scan.hasNext()){ while (scan.hasNext()){
String cmd = scan.next(); String cmd = scan.next();

@ -312,13 +312,12 @@ public class DesktopAssetManager implements AssetManager {
protected <T> T registerAndCloneSmartAsset(AssetKey<T> key, T obj, AssetProcessor proc, AssetCache cache) { protected <T> T registerAndCloneSmartAsset(AssetKey<T> key, T obj, AssetProcessor proc, AssetCache cache) {
// object obj is the original asset // object obj is the original asset
// create an instance for user // create an instance for user
T clone = (T) obj;
if (proc == null) { if (proc == null) {
throw new IllegalStateException("Asset implements " throw new IllegalStateException("Asset implements "
+ "CloneableSmartAsset but doesn't " + "CloneableSmartAsset but doesn't "
+ "have processor to handle cloning"); + "have processor to handle cloning");
} else { } else {
clone = (T) proc.createClone(obj); T clone = (T) proc.createClone(obj);
if (cache != null && clone != obj) { if (cache != null && clone != obj) {
cache.registerAssetClone(key, clone); cache.registerAssetClone(key, clone);
} else { } else {
@ -326,9 +325,9 @@ public class DesktopAssetManager implements AssetManager {
+ "CloneableSmartAsset but doesn't have cache or " + "CloneableSmartAsset but doesn't have cache or "
+ "was not cloned"); + "was not cloned");
} }
}
return clone; return clone;
} }
}
@Override @Override
public <T> T loadAssetFromStream(AssetKey<T> key, InputStream inputStream) { public <T> T loadAssetFromStream(AssetKey<T> key, InputStream inputStream) {

@ -47,7 +47,7 @@ import java.util.logging.Logger;
* This is done by keeping an instance of each asset loader and asset * This is done by keeping an instance of each asset loader and asset
* locator object in a thread local. * locator object in a thread local.
*/ */
public class ImplHandler { final class ImplHandler {
private static final Logger logger = Logger.getLogger(ImplHandler.class.getName()); private static final Logger logger = Logger.getLogger(ImplHandler.class.getName());
@ -75,7 +75,7 @@ public class ImplHandler {
this.assetManager = assetManager; this.assetManager = assetManager;
} }
protected class ImplThreadLocal<T> extends ThreadLocal { protected static class ImplThreadLocal<T> extends ThreadLocal {
private final Class<T> type; private final Class<T> type;
private final String path; private final String path;
@ -83,7 +83,7 @@ public class ImplHandler {
public ImplThreadLocal(Class<T> type, String[] extensions){ public ImplThreadLocal(Class<T> type, String[] extensions){
this.type = type; this.type = type;
this.extensions = extensions; this.extensions = extensions.clone();
this.path = null; this.path = null;
} }

@ -39,8 +39,6 @@ import java.util.List;
* Used for loading {@link ShaderNodeDefinition shader nodes definition} * Used for loading {@link ShaderNodeDefinition shader nodes definition}
* *
* Tells if the defintion has to be loaded with or without its documentation * Tells if the defintion has to be loaded with or without its documentation
*
* @author Kirill Vainer
*/ */
public class ShaderNodeDefinitionKey extends AssetKey<List<ShaderNodeDefinition>> { public class ShaderNodeDefinitionKey extends AssetKey<List<ShaderNodeDefinition>> {

@ -155,11 +155,7 @@ public class WeakRefCloneAssetCache implements AssetCache {
} }
public <T> T getFromCache(AssetKey<T> key) { public <T> T getFromCache(AssetKey<T> key) {
AssetRef smartInfo; AssetRef smartInfo = smartCache.get(key);
synchronized (smartCache){
smartInfo = smartCache.get(key);
}
if (smartInfo == null) { if (smartInfo == null) {
return null; return null;
} else { } else {

@ -51,6 +51,9 @@ import static com.jme3.audio.openal.EFX.*;
public class ALAudioRenderer implements AudioRenderer, Runnable { public class ALAudioRenderer implements AudioRenderer, Runnable {
private static final Logger logger = Logger.getLogger(ALAudioRenderer.class.getName()); 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(); private final NativeObjectManager objManager = new NativeObjectManager();
// When multiplied by STREAMING_BUFFER_COUNT, will equal 44100 * 2 * 2 // When multiplied by STREAMING_BUFFER_COUNT, will equal 44100 * 2 * 2
// which is exactly 1 second of audio. // which is exactly 1 second of audio.
@ -75,7 +78,7 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
// Fill streaming sources every 50 ms // Fill streaming sources every 50 ms
private static final float UPDATE_RATE = 0.05f; 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 Object threadLock = new Object();
private final AL al; private final AL al;

@ -640,8 +640,7 @@ public class BoundingSphere extends BoundingVolume {
return rVal; return rVal;
} }
return new BoundingSphere(radius, return new BoundingSphere(radius, center.clone());
(center != null ? (Vector3f) center.clone() : null));
} }
/** /**

@ -109,6 +109,11 @@ public class CollisionResult implements Comparable<CollisionResult> {
return super.equals(obj); return super.equals(obj);
} }
@Override
public int hashCode() {
return Float.floatToIntBits(distance);
}
public Vector3f getContactPoint() { public Vector3f getContactPoint() {
return contactPoint; return contactPoint;
} }

@ -56,7 +56,7 @@ import java.io.IOException;
* the light intensity slowly decrease between the inner cone and the outer cone. * the light intensity slowly decrease between the inner cone and the outer cone.
* @author Nehon * @author Nehon
*/ */
public class SpotLight extends Light implements Savable { public class SpotLight extends Light {
protected Vector3f position = new Vector3f(); protected Vector3f position = new Vector3f();
protected Vector3f direction = new Vector3f(0,-1,0); protected Vector3f direction = new Vector3f(0,-1,0);

@ -103,11 +103,10 @@ public class TechniqueDef implements Savable {
private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class); private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
private String name; private String name;
private EnumMap<Shader.ShaderType,String> shaderLanguage; private EnumMap<Shader.ShaderType,String> shaderLanguages;
private EnumMap<Shader.ShaderType,String> shaderName; private EnumMap<Shader.ShaderType,String> shaderNames;
private DefineList presetDefines; private DefineList presetDefines;
private boolean usesShaders;
private boolean usesNodes = false; private boolean usesNodes = false;
private List<ShaderNode> shaderNodes; private List<ShaderNode> shaderNodes;
private ShaderGenerationInfo shaderGenerationInfo; private ShaderGenerationInfo shaderGenerationInfo;
@ -140,8 +139,8 @@ public class TechniqueDef implements Savable {
* Serialization only. Do not use. * Serialization only. Do not use.
*/ */
public TechniqueDef(){ public TechniqueDef(){
shaderLanguage=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); shaderLanguages=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
shaderName=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); shaderNames=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
} }
/** /**
@ -226,7 +225,7 @@ public class TechniqueDef implements Savable {
*/ */
@Deprecated @Deprecated
public boolean isUsingShaders(){ public boolean isUsingShaders(){
return usesShaders; return true;
} }
/** /**
@ -258,36 +257,44 @@ public class TechniqueDef implements Savable {
* @param fragLanguage The fragment shader language * @param fragLanguage The fragment shader language
*/ */
public void setShaderFile(String vertexShader, String fragmentShader, String vertLanguage, String fragLanguage) { public void setShaderFile(String vertexShader, String fragmentShader, String vertLanguage, String fragLanguage) {
this.shaderLanguage.put(Shader.ShaderType.Vertex, vertLanguage); this.shaderLanguages.put(Shader.ShaderType.Vertex, vertLanguage);
this.shaderName.put(Shader.ShaderType.Vertex, vertexShader); this.shaderNames.put(Shader.ShaderType.Vertex, vertexShader);
this.shaderLanguage.put(Shader.ShaderType.Fragment, fragLanguage); this.shaderLanguages.put(Shader.ShaderType.Fragment, fragLanguage);
this.shaderName.put(Shader.ShaderType.Fragment, fragmentShader); this.shaderNames.put(Shader.ShaderType.Fragment, fragmentShader);
requiredCaps.clear();
Caps vertCap = Caps.valueOf(vertLanguage); Caps vertCap = Caps.valueOf(vertLanguage);
requiredCaps.add(vertCap); requiredCaps.add(vertCap);
Caps fragCap = Caps.valueOf(fragLanguage); Caps fragCap = Caps.valueOf(fragLanguage);
requiredCaps.add(fragCap); requiredCaps.add(fragCap);
usesShaders = true;
} }
/** /**
* Sets the shaders that this technique definition will use. * Sets the shaders that this technique definition will use.
* *
* @param shaderName EnumMap containing all shader names for this stage * @param shaderNames EnumMap containing all shader names for this stage
* @param shaderLanguage EnumMap containing all shader languages for this stage * @param shaderLanguages EnumMap containing all shader languages for this stage
*/ */
public void setShaderFile(EnumMap<Shader.ShaderType, String> shaderName, EnumMap<Shader.ShaderType, String> shaderLanguage) { public void setShaderFile(EnumMap<Shader.ShaderType, String> shaderNames, EnumMap<Shader.ShaderType, String> shaderLanguages) {
for (Shader.ShaderType shaderType : shaderName.keySet()) { requiredCaps.clear();
this.shaderLanguage.put(shaderType,shaderLanguage.get(shaderType));
this.shaderName.put(shaderType,shaderName.get(shaderType)); 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)) { if (shaderType.equals(Shader.ShaderType.Geometry)) {
requiredCaps.add(Caps.GeometryShader); requiredCaps.add(Caps.GeometryShader);
} else if (shaderType.equals(Shader.ShaderType.TessellationControl)) { } else if (shaderType.equals(Shader.ShaderType.TessellationControl)) {
requiredCaps.add(Caps.TesselationShader); 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. * @return the name of the fragment shader to be used.
*/ */
public String getFragmentShaderName() { 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. * @return the name of the vertex shader to be used.
*/ */
public String getVertexShaderName() { public String getVertexShaderName() {
return shaderName.get(Shader.ShaderType.Vertex); return shaderNames.get(Shader.ShaderType.Vertex);
}
/**
* @deprecated Use {@link #getVertexShaderLanguage() } instead.
*/
@Deprecated
public String getShaderLanguage() {
return shaderLanguage.get(Shader.ShaderType.Vertex);
} }
/** /**
* Returns the language of the fragment shader used in this technique. * Returns the language of the fragment shader used in this technique.
*/ */
public String getFragmentShaderLanguage() { 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. * Returns the language of the vertex shader used in this technique.
*/ */
public String getVertexShaderLanguage() { public String getVertexShaderLanguage() {
return shaderLanguage.get(Shader.ShaderType.Vertex); return shaderLanguages.get(Shader.ShaderType.Vertex);
} }
/**Returns the language for each shader program /**Returns the language for each shader program
* @param shaderType * @param shaderType
*/ */
public String getShaderProgramLanguage(Shader.ShaderType shaderType){ public String getShaderProgramLanguage(Shader.ShaderType shaderType){
return shaderLanguage.get(shaderType); return shaderLanguages.get(shaderType);
} }
/**Returns the name for each shader program /**Returns the name for each shader program
* @param shaderType * @param shaderType
*/ */
public String getShaderProgramName(Shader.ShaderType 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); OutputCapsule oc = ex.getCapsule(this);
oc.write(name, "name", null); oc.write(name, "name", null);
oc.write(shaderName.get(Shader.ShaderType.Vertex), "vertName", null); oc.write(shaderNames.get(Shader.ShaderType.Vertex), "vertName", null);
oc.write(shaderName.get(Shader.ShaderType.Fragment), "fragName", null); oc.write(shaderNames.get(Shader.ShaderType.Fragment), "fragName", null);
oc.write(shaderName.get(Shader.ShaderType.Geometry), "geomName", null); oc.write(shaderNames.get(Shader.ShaderType.Geometry), "geomName", null);
oc.write(shaderName.get(Shader.ShaderType.TessellationControl), "tsctrlName", null); oc.write(shaderNames.get(Shader.ShaderType.TessellationControl), "tsctrlName", null);
oc.write(shaderName.get(Shader.ShaderType.TessellationEvaluation), "tsevalName", null); oc.write(shaderNames.get(Shader.ShaderType.TessellationEvaluation), "tsevalName", null);
oc.write(shaderLanguage.get(Shader.ShaderType.Vertex), "vertLanguage", null); oc.write(shaderLanguages.get(Shader.ShaderType.Vertex), "vertLanguage", null);
oc.write(shaderLanguage.get(Shader.ShaderType.Fragment), "fragLanguage", null); oc.write(shaderLanguages.get(Shader.ShaderType.Fragment), "fragLanguage", null);
oc.write(shaderLanguage.get(Shader.ShaderType.Geometry), "geomLanguage", null); oc.write(shaderLanguages.get(Shader.ShaderType.Geometry), "geomLanguage", null);
oc.write(shaderLanguage.get(Shader.ShaderType.TessellationControl), "tsctrlLanguage", null); oc.write(shaderLanguages.get(Shader.ShaderType.TessellationControl), "tsctrlLanguage", null);
oc.write(shaderLanguage.get(Shader.ShaderType.TessellationEvaluation), "tsevalLanguage", null); oc.write(shaderLanguages.get(Shader.ShaderType.TessellationEvaluation), "tsevalLanguage", null);
oc.write(presetDefines, "presetDefines", null); oc.write(presetDefines, "presetDefines", null);
oc.write(lightMode, "lightMode", LightMode.Disable); oc.write(lightMode, "lightMode", LightMode.Disable);
oc.write(shadowMode, "shadowMode", ShadowMode.Disable); oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
oc.write(renderState, "renderState", null); oc.write(renderState, "renderState", null);
oc.write(usesShaders, "usesShaders", false);
oc.write(usesNodes, "usesNodes", false); oc.write(usesNodes, "usesNodes", false);
oc.writeSavableArrayList((ArrayList)shaderNodes,"shaderNodes", null); oc.writeSavableArrayList((ArrayList)shaderNodes,"shaderNodes", null);
oc.write(shaderGenerationInfo, "shaderGenerationInfo", null); oc.write(shaderGenerationInfo, "shaderGenerationInfo", null);
@ -482,28 +480,27 @@ public class TechniqueDef implements Savable {
public void read(JmeImporter im) throws IOException{ public void read(JmeImporter im) throws IOException{
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
name = ic.readString("name", null); name = ic.readString("name", null);
shaderName.put(Shader.ShaderType.Vertex,ic.readString("vertName", null)); shaderNames.put(Shader.ShaderType.Vertex,ic.readString("vertName", null));
shaderName.put(Shader.ShaderType.Fragment,ic.readString("fragName", null)); shaderNames.put(Shader.ShaderType.Fragment,ic.readString("fragName", null));
shaderName.put(Shader.ShaderType.Geometry,ic.readString("geomName", null)); shaderNames.put(Shader.ShaderType.Geometry,ic.readString("geomName", null));
shaderName.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlName", null)); shaderNames.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlName", null));
shaderName.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalName", null)); shaderNames.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalName", null));
presetDefines = (DefineList) ic.readSavable("presetDefines", null); presetDefines = (DefineList) ic.readSavable("presetDefines", null);
lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable); lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable); shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
renderState = (RenderState) ic.readSavable("renderState", null); renderState = (RenderState) ic.readSavable("renderState", null);
usesShaders = ic.readBoolean("usesShaders", false);
if (ic.getSavableVersion(TechniqueDef.class) == 0) { if (ic.getSavableVersion(TechniqueDef.class) == 0) {
// Old version // Old version
shaderLanguage.put(Shader.ShaderType.Vertex,ic.readString("shaderLang", null)); shaderLanguages.put(Shader.ShaderType.Vertex,ic.readString("shaderLang", null));
shaderLanguage.put(Shader.ShaderType.Fragment,shaderLanguage.get(Shader.ShaderType.Vertex)); shaderLanguages.put(Shader.ShaderType.Fragment,shaderLanguages.get(Shader.ShaderType.Vertex));
} else { } else {
// New version // New version
shaderLanguage.put(Shader.ShaderType.Vertex,ic.readString("vertLanguage", null)); shaderLanguages.put(Shader.ShaderType.Vertex,ic.readString("vertLanguage", null));
shaderLanguage.put(Shader.ShaderType.Fragment,ic.readString("fragLanguage", null)); shaderLanguages.put(Shader.ShaderType.Fragment,ic.readString("fragLanguage", null));
shaderLanguage.put(Shader.ShaderType.Geometry,ic.readString("geomLanguage", null)); shaderLanguages.put(Shader.ShaderType.Geometry,ic.readString("geomLanguage", null));
shaderLanguage.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlLanguage", null)); shaderLanguages.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlLanguage", null));
shaderLanguage.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalLanguage", null)); shaderLanguages.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalLanguage", null));
} }
usesNodes = ic.readBoolean("usesNodes", false); usesNodes = ic.readBoolean("usesNodes", false);
@ -518,7 +515,6 @@ public class TechniqueDef implements Savable {
public void setShaderNodes(List<ShaderNode> shaderNodes) { public void setShaderNodes(List<ShaderNode> shaderNodes) {
this.shaderNodes = shaderNodes; this.shaderNodes = shaderNodes;
usesNodes = true; usesNodes = true;
usesShaders = true;
} }
/** /**
@ -526,7 +522,7 @@ public class TechniqueDef implements Savable {
* @return * @return
*/ */
public EnumMap<Shader.ShaderType, String> getShaderProgramNames() { public EnumMap<Shader.ShaderType, String> getShaderProgramNames() {
return shaderName; return shaderNames;
} }
/** /**
@ -534,7 +530,7 @@ public class TechniqueDef implements Savable {
* @return * @return
*/ */
public EnumMap<Shader.ShaderType, String> getShaderProgramLanguages() { public EnumMap<Shader.ShaderType, String> getShaderProgramLanguages() {
return shaderLanguage; return shaderLanguages;
} }
public ShaderGenerationInfo getShaderGenerationInfo() { public ShaderGenerationInfo getShaderGenerationInfo() {
@ -566,5 +562,4 @@ public class TechniqueDef implements Savable {
public void setLightSpace(LightSpace lightSpace) { public void setLightSpace(LightSpace lightSpace) {
this.lightSpace = lightSpace; this.lightSpace = lightSpace;
} }
} }

@ -1321,13 +1321,6 @@ public class GLRenderer implements Renderer {
glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, prevFBO); 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 { } else {
throw new RendererException("Framebuffer blitting not supported by the video hardware"); throw new RendererException("Framebuffer blitting not supported by the video hardware");
} }
@ -1488,6 +1481,8 @@ public class GLRenderer implements Renderer {
updateFrameBufferAttachment(fb, colorBuf); updateFrameBufferAttachment(fb, colorBuf);
} }
checkFrameBufferError();
fb.clearUpdateNeeded(); fb.clearUpdateNeeded();
} }
@ -1645,8 +1640,6 @@ public class GLRenderer implements Renderer {
assert context.boundFBO == fb.getId(); assert context.boundFBO == fb.getId();
context.boundFB = fb; context.boundFB = fb;
checkFrameBufferError();
} }
} }
@ -2221,14 +2214,11 @@ public class GLRenderer implements Renderer {
int usage = convertUsage(vb.getUsage()); int usage = convertUsage(vb.getUsage());
vb.getData().rewind(); vb.getData().rewind();
// if (created || vb.hasDataSizeChanged()) {
// upload data based on format
switch (vb.getFormat()) { switch (vb.getFormat()) {
case Byte: case Byte:
case UnsignedByte: case UnsignedByte:
gl.glBufferData(target, (ByteBuffer) vb.getData(), usage); gl.glBufferData(target, (ByteBuffer) vb.getData(), usage);
break; break;
// case Half:
case Short: case Short:
case UnsignedShort: case UnsignedShort:
gl.glBufferData(target, (ShortBuffer) vb.getData(), usage); gl.glBufferData(target, (ShortBuffer) vb.getData(), usage);
@ -2243,31 +2233,6 @@ public class GLRenderer implements Renderer {
default: default:
throw new UnsupportedOperationException("Unknown buffer format."); 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.");
// }
// }
vb.clearUpdateNeeded(); vb.clearUpdateNeeded();
} }

@ -70,6 +70,7 @@ public final class GLTracer implements InvocationHandler {
// noEnumArgs("glTexParameteri", 2); // noEnumArgs("glTexParameteri", 2);
noEnumArgs("glTexImage2D", 1, 3, 4, 5); noEnumArgs("glTexImage2D", 1, 3, 4, 5);
noEnumArgs("glTexImage3D", 1, 3, 4, 5, 6); noEnumArgs("glTexImage3D", 1, 3, 4, 5, 6);
noEnumArgs("glTexSubImage2D", 1, 2, 3, 4, 5);
noEnumArgs("glTexSubImage3D", 1, 2, 3, 4, 5, 6, 7); noEnumArgs("glTexSubImage3D", 1, 2, 3, 4, 5, 6, 7);
noEnumArgs("glCompressedTexImage2D", 1, 3, 4, 5); noEnumArgs("glCompressedTexImage2D", 1, 3, 4, 5);
noEnumArgs("glCompressedTexSubImage3D", 1, 2, 3, 4, 5, 6, 7); 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("glDrawRangeElements", 1, 2, 3, 5);
noEnumArgs("glDrawArrays", 1, 2); noEnumArgs("glDrawArrays", 1, 2);
noEnumArgs("glDeleteBuffers", 0); noEnumArgs("glDeleteBuffers", 0);
noEnumArgs("glBindVertexArray", 0);
noEnumArgs("glGenVertexArrays", 0);
noEnumArgs("glBindFramebufferEXT", 1); noEnumArgs("glBindFramebufferEXT", 1);
noEnumArgs("glBindRenderbufferEXT", 1); noEnumArgs("glBindRenderbufferEXT", 1);
@ -110,6 +113,7 @@ public final class GLTracer implements InvocationHandler {
noEnumArgs("glUniform1f", 0); noEnumArgs("glUniform1f", 0);
noEnumArgs("glUniform2f", 0); noEnumArgs("glUniform2f", 0);
noEnumArgs("glUniform3f", 0); noEnumArgs("glUniform3f", 0);
noEnumArgs("glUniform4", 0);
noEnumArgs("glUniform4f", 0); noEnumArgs("glUniform4f", 0);
noEnumArgs("glGetAttribLocation", 0, -1); noEnumArgs("glGetAttribLocation", 0, -1);
noEnumArgs("glDetachShader", 0, 1); noEnumArgs("glDetachShader", 0, 1);

@ -64,7 +64,7 @@ import java.util.logging.Logger;
* TODO more automagic (batch when needed in the updateLogicalState) * TODO more automagic (batch when needed in the updateLogicalState)
* @author Nehon * @author Nehon
*/ */
public class BatchNode extends GeometryGroupNode implements Savable { public class BatchNode extends GeometryGroupNode {
private static final Logger logger = Logger.getLogger(BatchNode.class.getName()); private static final Logger logger = Logger.getLogger(BatchNode.class.getName());
/** /**

@ -58,7 +58,7 @@ import java.util.logging.Logger;
* @author Gregg Patton * @author Gregg Patton
* @author Joshua Slack * @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()); private static final Logger logger = Logger.getLogger(Node.class.getName());

@ -33,6 +33,12 @@ package com.jme3.scene;
import com.jme3.export.*; import com.jme3.export.*;
import java.io.IOException; 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;
/** /**
* <code>UserData</code> is used to contain user data objects * <code>UserData</code> is used to contain user data objects
@ -55,6 +61,16 @@ public final class UserData implements Savable {
*/ */
public static final String JME_SHAREDMESH = "JmeSharedMesh"; 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 byte type;
protected Object value; protected Object value;
@ -65,11 +81,13 @@ public final class UserData implements Savable {
* Creates a new <code>UserData</code> with the given * Creates a new <code>UserData</code> with the given
* type and value. * type and value.
* *
* @param type Type of data, should be between 0 and 4. * @param type
* @param value Value of the data * Type of data, should be between 0 and 8.
* @param value
* Value of the data
*/ */
public UserData(byte type, Object value) { public UserData(byte type, Object value) {
assert type >= 0 && type <= 4; assert type >= 0 && type <= 8;
this.type = type; this.type = type;
this.value = value; this.value = value;
} }
@ -85,15 +103,23 @@ public final class UserData implements Savable {
public static byte getObjectType(Object type) { public static byte getObjectType(Object type) {
if (type instanceof Integer) { if (type instanceof Integer) {
return 0; return TYPE_INTEGER;
} else if (type instanceof Float) { } else if (type instanceof Float) {
return 1; return TYPE_FLOAT;
} else if (type instanceof Boolean) { } else if (type instanceof Boolean) {
return 2; return TYPE_BOOLEAN;
} else if (type instanceof String) { } else if (type instanceof String) {
return 3; return TYPE_STRING;
} else if (type instanceof Long) { } 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 { } else {
throw new IllegalArgumentException("Unsupported type: " + type.getClass().getName()); throw new IllegalArgumentException("Unsupported type: " + type.getClass().getName());
} }
@ -104,53 +130,192 @@ public final class UserData implements Savable {
oc.write(type, "type", (byte) 0); oc.write(type, "type", (byte) 0);
switch (type) { switch (type) {
case 0: case TYPE_INTEGER:
int i = (Integer) value; int i = (Integer) value;
oc.write(i, "intVal", 0); oc.write(i, "intVal", 0);
break; break;
case 1: case TYPE_FLOAT:
float f = (Float) value; float f = (Float) value;
oc.write(f, "floatVal", 0f); oc.write(f, "floatVal", 0f);
break; break;
case 2: case TYPE_BOOLEAN:
boolean b = (Boolean) value; boolean b = (Boolean) value;
oc.write(b, "boolVal", false); oc.write(b, "boolVal", false);
break; break;
case 3: case TYPE_STRING:
String s = (String) value; String s = (String) value;
oc.write(s, "strVal", null); oc.write(s, "strVal", null);
break; break;
case 4: case TYPE_LONG:
Long l = (Long) value; Long l = (Long) value;
oc.write(l, "longVal", 0l); oc.write(l, "longVal", 0l);
break; 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: default:
throw new UnsupportedOperationException(); throw new UnsupportedOperationException("Unsupported value type: " + value.getClass());
} }
} }
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
type = ic.readByte("type", (byte) 0); type = ic.readByte("type", (byte) 0);
switch (type) { switch (type) {
case 0: case TYPE_INTEGER:
value = ic.readInt("intVal", 0); value = ic.readInt("intVal", 0);
break; break;
case 1: case TYPE_FLOAT:
value = ic.readFloat("floatVal", 0f); value = ic.readFloat("floatVal", 0f);
break; break;
case 2: case TYPE_BOOLEAN:
value = ic.readBoolean("boolVal", false); value = ic.readBoolean("boolVal", false);
break; break;
case 3: case TYPE_STRING:
value = ic.readString("strVal", null); value = ic.readString("strVal", null);
break; break;
case 4: case TYPE_LONG:
value = ic.readLong("longVal", 0l); value = ic.readLong("longVal", 0l);
break; break;
case TYPE_SAVABLE:
value = ic.readSavable("savableVal", null);
break;
case TYPE_LIST:
value = this.readList(ic, "0");
break;
case TYPE_MAP:
Map<Object, Object> map = new HashMap<Object, Object>();
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: 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<Object> list = new ArrayList<Object>(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<Object, Object> map = new HashMap<Object, Object>();
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;
} }
} }

@ -57,7 +57,7 @@ public class InstancedNode extends GeometryGroupNode {
setGeometryStartIndex(geom, startIndex); setGeometryStartIndex(geom, startIndex);
} }
private static class InstanceTypeKey implements Cloneable { private static final class InstanceTypeKey implements Cloneable {
Mesh mesh; Mesh mesh;
Material material; Material material;

@ -266,7 +266,7 @@ public class Dome extends Mesh {
vars.release(); vars.release();
// pole // 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); nb.put(0).put(insideView ? -1 : 1).put(0);
tb.put(0.5f).put(1.0f); tb.put(0.5f).put(1.0f);

@ -40,7 +40,7 @@ import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
public class DefineList implements Savable, Cloneable { public final class DefineList implements Savable, Cloneable {
private static final String ONE = "1"; private static final String ONE = "1";

@ -46,6 +46,8 @@ public class NullContext implements JmeContext, Runnable {
protected static final Logger logger = Logger.getLogger(NullContext.class.getName()); 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 created = new AtomicBoolean(false);
protected AtomicBoolean needClose = new AtomicBoolean(false); protected AtomicBoolean needClose = new AtomicBoolean(false);
protected final Object createdLock = new Object(); protected final Object createdLock = new Object();
@ -150,7 +152,7 @@ public class NullContext implements JmeContext, Runnable {
return; return;
} }
new Thread(this, "Headless Application Thread").start(); new Thread(this, THREAD_NAME).start();
if (waitFor) if (waitFor)
waitFor(true); waitFor(true);
} }

@ -72,9 +72,10 @@ import java.util.ArrayList;
* @author Kirill Vainer * @author Kirill Vainer
*/ */
public class FrameBuffer extends NativeObject { public class FrameBuffer extends NativeObject {
public static int SLOT_UNDEF = -1;
public static int SLOT_DEPTH = -100; public static final int SLOT_UNDEF = -1;
public static int SLOT_DEPTH_STENCIL = -101; public static final int SLOT_DEPTH = -100;
public static final int SLOT_DEPTH_STENCIL = -101;
private int width = 0; private int width = 0;
private int height = 0; private int height = 0;

@ -488,7 +488,8 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
/** /**
* @return the anisotropic filtering level for this texture. Default value * @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() { public int getAnisotropicFilter() {
return anisotropicFilter; return anisotropicFilter;
@ -499,11 +500,7 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
* the anisotropic filtering level for this texture. * the anisotropic filtering level for this texture.
*/ */
public void setAnisotropicFilter(int level) { public void setAnisotropicFilter(int level) {
if (level < 1) { anisotropicFilter = Math.max(0, level);
anisotropicFilter = 1;
} else {
anisotropicFilter = level;
}
} }
@Override @Override

@ -34,6 +34,7 @@ package com.jme3.util;
import com.jme3.util.IntMap.Entry; import com.jme3.util.IntMap.Entry;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException;
/** /**
* Similar to a {@link Map} except that ints are used as keys. * Similar to a {@link Map} except that ints are used as keys.
@ -234,7 +235,7 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
public Entry next() { public Entry next() {
if (el >= size) if (el >= size)
throw new IllegalStateException("No more elements!"); throw new NoSuchElementException("No more elements!");
if (cur != null){ if (cur != null){
Entry e = cur; Entry e = cur;

@ -274,12 +274,10 @@ public class TangentBinormalGenerator {
triData.setIndex(index); triData.setIndex(index);
triData.triangleOffset = i * 3 ; triData.triangleOffset = i * 3 ;
} }
if (triData != null) {
vertices.get(index[0]).triangles.add(triData); vertices.get(index[0]).triangles.add(triData);
vertices.get(index[1]).triangles.add(triData); vertices.get(index[1]).triangles.add(triData);
vertices.get(index[2]).triangles.add(triData); vertices.get(index[2]).triangles.add(triData);
} }
}
return vertices; return vertices;
} }
@ -483,7 +481,7 @@ public class TangentBinormalGenerator {
boolean isDegenerate = isDegenerateTriangle(v[0], v[1], v[2]); boolean isDegenerate = isDegenerateTriangle(v[0], v[1], v[2]);
TriangleData triData = processTriangle(index, v, t); TriangleData triData = processTriangle(index, v, t);
if (triData != null && !isDegenerate) { if (!isDegenerate) {
vertices.get(index[0]).triangles.add(triData); vertices.get(index[0]).triangles.add(triData);
vertices.get(index[1]).triangles.add(triData); vertices.get(index[1]).triangles.add(triData);
vertices.get(index[2]).triangles.add(triData); vertices.get(index[2]).triangles.add(triData);
@ -529,11 +527,9 @@ public class TangentBinormalGenerator {
populateFromBuffer(t[2], textureBuffer, index[2]); populateFromBuffer(t[2], textureBuffer, index[2]);
TriangleData triData = processTriangle(index, v, t); TriangleData triData = processTriangle(index, v, t);
if (triData != null) {
vertices.get(index[0]).triangles.add(triData); vertices.get(index[0]).triangles.add(triData);
vertices.get(index[1]).triangles.add(triData); vertices.get(index[1]).triangles.add(triData);
vertices.get(index[2]).triangles.add(triData); vertices.get(index[2]).triangles.add(triData);
}
Vector3f vTemp = v[1]; Vector3f vTemp = v[1];
v[1] = v[2]; v[1] = v[2];

@ -71,7 +71,7 @@ public class BlockLanguageParser {
private void load(InputStream in) throws IOException{ private void load(InputStream in) throws IOException{
reset(); reset();
reader = new InputStreamReader(in); reader = new InputStreamReader(in, "UTF-8");
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
boolean insideComment = false; boolean insideComment = false;

@ -126,7 +126,8 @@ void main(){
DiffuseSum = m_Diffuse * vec4(lightColor.rgb, 1.0); DiffuseSum = m_Diffuse * vec4(lightColor.rgb, 1.0);
SpecularSum = (m_Specular * lightColor).rgb; SpecularSum = (m_Specular * lightColor).rgb;
#else #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); DiffuseSum = vec4(lightColor.rgb, 1.0);
SpecularSum = vec3(0.0); SpecularSum = vec3(0.0);
#endif #endif

@ -117,6 +117,7 @@ void main(){
SpecularSum = m_Specular.rgb; SpecularSum = m_Specular.rgb;
DiffuseSum = m_Diffuse; DiffuseSum = m_Diffuse;
#else #else
// Defaults: Ambient and diffuse are white, specular is black.
AmbientSum = g_AmbientLightColor.rgb; AmbientSum = g_AmbientLightColor.rgb;
SpecularSum = vec3(0.0); SpecularSum = vec3(0.0);
DiffuseSum = vec4(1.0); DiffuseSum = vec4(1.0);

@ -30,6 +30,14 @@ float computeSpotFalloff(in vec4 lightDirection, in vec3 lightVector){
float innerAngleCos = floor(lightDirection.w) * 0.001; float innerAngleCos = floor(lightDirection.w) * 0.001;
float outerAngleCos = fract(lightDirection.w); float outerAngleCos = fract(lightDirection.w);
float innerMinusOuter = innerAngleCos - outerAngleCos; 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
} }

@ -196,7 +196,7 @@ public class WAVLoader implements AssetLoader {
break; break;
case i_data: case i_data:
// Compute duration based on data chunk size // Compute duration based on data chunk size
duration = len / bytesPerSec; duration = (float)(len / bytesPerSec);
if (readStream) { if (readStream) {
readDataChunkForStream(inOffset, len); readDataChunkForStream(inOffset, len);

@ -270,11 +270,9 @@ public final class BinaryImporter implements JmeImporter {
try { try {
return load(fis, listener); return load(fis, listener);
} finally { } finally {
if (fis != null) {
fis.close(); fis.close();
} }
} }
}
public Savable load(byte[] data) throws IOException { public Savable load(byte[] data) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(data); ByteArrayInputStream bais = new ByteArrayInputStream(data);

@ -186,6 +186,7 @@ public class J3MLoader implements AssetLoader {
tex.setWrap(WrapMode.Repeat); tex.setWrap(WrapMode.Repeat);
} }
tex.setKey(texKey); tex.setKey(texKey);
tex.setName(texKey.getName());
} }
return tex; return tex;
}else{ }else{

@ -71,7 +71,7 @@ public class VideoRecorderAppState extends AbstractAppState {
public Thread newThread(Runnable r) { public Thread newThread(Runnable r) {
Thread th = new Thread(r); Thread th = new Thread(r);
th.setName("jME Video Processing Thread"); th.setName("jME3 Video Processor");
th.setDaemon(true); th.setDaemon(true);
return th; return th;
} }

@ -21,8 +21,8 @@ import java.util.List;
public class TestSweepTest extends SimpleApplication { public class TestSweepTest extends SimpleApplication {
private BulletAppState bulletAppState = new BulletAppState(); private BulletAppState bulletAppState = new BulletAppState();
private CapsuleCollisionShape obstacleCollisionShape = new CapsuleCollisionShape(0.3f, 0.5f); private CapsuleCollisionShape obstacleCollisionShape;
private CapsuleCollisionShape capsuleCollisionShape = new CapsuleCollisionShape(1f, 1f); private CapsuleCollisionShape capsuleCollisionShape;
private Node capsule; private Node capsule;
private Node obstacle; private Node obstacle;
private float dist = .5f; private float dist = .5f;
@ -33,6 +33,9 @@ public class TestSweepTest extends SimpleApplication {
@Override @Override
public void simpleInitApp() { public void simpleInitApp() {
obstacleCollisionShape = new CapsuleCollisionShape(0.3f, 0.5f);
capsuleCollisionShape = new CapsuleCollisionShape(1f, 1f);
stateManager.attach(bulletAppState); stateManager.attach(bulletAppState);
capsule = new Node("capsule"); capsule = new Node("capsule");
@ -56,14 +59,19 @@ public class TestSweepTest extends SimpleApplication {
public void simpleUpdate(float tpf) { public void simpleUpdate(float tpf) {
float move = tpf * 1; float move = tpf * 1;
boolean colliding = false;
List<PhysicsSweepTestResult> sweepTest = bulletAppState.getPhysicsSpace().sweepTest(capsuleCollisionShape, new Transform(capsule.getWorldTranslation()), new Transform(capsule.getWorldTranslation().add(dist, 0, 0))); List<PhysicsSweepTestResult> sweepTest = bulletAppState.getPhysicsSpace().sweepTest(capsuleCollisionShape, new Transform(capsule.getWorldTranslation()), new Transform(capsule.getWorldTranslation().add(dist, 0, 0)));
if (sweepTest.size() > 0) { for (PhysicsSweepTestResult result : sweepTest) {
PhysicsSweepTestResult get = sweepTest.get(0); if (result.getCollisionObject().getCollisionShape() != capsuleCollisionShape) {
PhysicsCollisionObject collisionObject = get.getCollisionObject(); PhysicsCollisionObject collisionObject = result.getCollisionObject();
fpsText.setText("Almost colliding with " + collisionObject.getUserObject().toString()); fpsText.setText("Almost colliding with " + collisionObject.getUserObject().toString());
} else { colliding = true;
}
}
if (!colliding) {
// if the sweep is clear then move the spatial // if the sweep is clear then move the spatial
capsule.move(move, 0, 0); capsule.move(move, 0, 0);
} }

@ -759,14 +759,6 @@ public class IGLESShaderRenderer implements Renderer {
Image[] textures = context.boundTextures; Image[] textures = context.boundTextures;
int type = convertTextureType(tex.getType()); 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 (textures[unit] != image) {
if (context.boundTextureUnit != unit) { if (context.boundTextureUnit != unit) {
JmeIosGLES.glActiveTexture(JmeIosGLES.GL_TEXTURE0 + unit); JmeIosGLES.glActiveTexture(JmeIosGLES.GL_TEXTURE0 + unit);
@ -1768,7 +1760,6 @@ public class IGLESShaderRenderer implements Renderer {
JmeIosGLES.checkGLError(); JmeIosGLES.checkGLError();
} }
clearVertexAttribs(); clearVertexAttribs();
clearTextureUnits();
} }
private void renderMeshDefault(Mesh mesh, int lod, int count) { private void renderMeshDefault(Mesh mesh, int lod, int count) {
@ -1807,7 +1798,6 @@ public class IGLESShaderRenderer implements Renderer {
JmeIosGLES.checkGLError(); JmeIosGLES.checkGLError();
} }
clearVertexAttribs(); clearVertexAttribs();
clearTextureUnits();
} }
@ -2085,23 +2075,6 @@ public class IGLESShaderRenderer implements Renderer {
context.attribIndexList.copyNewToOld(); 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) { public void updateFrameBuffer(FrameBuffer fb) {
int id = fb.getId(); int id = fb.getId();
if (id == -1) { if (id == -1) {

@ -5,7 +5,7 @@ if (!hasProperty('mainClass')) {
dependencies { dependencies {
compile project(':jme3-core') compile project(':jme3-core')
compile project(':jme3-desktop') compile project(':jme3-desktop')
compile 'org.jogamp.gluegen:gluegen-rt-main:2.2.0' compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.1'
compile 'org.jogamp.jogl:jogl-all-main:2.2.0' compile 'org.jogamp.jogl:jogl-all-main:2.3.1'
compile 'org.jogamp.joal:joal-main:2.2.0' compile 'org.jogamp.joal:joal-main:2.3.1'
} }

@ -45,11 +45,11 @@ import com.jogamp.newt.opengl.GLWindow;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.nativewindow.util.Dimension; import com.jogamp.nativewindow.util.Dimension;
import javax.media.nativewindow.util.DimensionImmutable; import com.jogamp.nativewindow.util.DimensionImmutable;
import javax.media.nativewindow.util.PixelFormat; import com.jogamp.nativewindow.util.PixelFormat;
import javax.media.nativewindow.util.PixelRectangle; import com.jogamp.nativewindow.util.PixelRectangle;
import javax.media.nativewindow.util.Point; import com.jogamp.nativewindow.util.Point;
public class NewtMouseInput implements MouseInput, MouseListener { public class NewtMouseInput implements MouseInput, MouseListener {

@ -70,15 +70,15 @@ import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.nativewindow.NativeWindowFactory; import com.jogamp.nativewindow.NativeWindowFactory;
import javax.media.opengl.GL; import com.jogamp.opengl.GL;
import javax.media.opengl.GL2; import com.jogamp.opengl.GL2;
import javax.media.opengl.GL2ES1; import com.jogamp.opengl.GL2ES1;
import javax.media.opengl.GL2ES2; import com.jogamp.opengl.GL2ES2;
import javax.media.opengl.GL2ES3; import com.jogamp.opengl.GL2ES3;
import javax.media.opengl.GL2GL3; import com.jogamp.opengl.GL2GL3;
import javax.media.opengl.GL3; import com.jogamp.opengl.GL3;
import javax.media.opengl.GLContext; import com.jogamp.opengl.GLContext;
import jme3tools.converters.MipMapGenerator; import jme3tools.converters.MipMapGenerator;
import jme3tools.shader.ShaderDebug; import jme3tools.shader.ShaderDebug;
@ -1800,7 +1800,7 @@ public class JoglRenderer implements Renderer {
if (samples > 1) { if (samples > 1) {
return GL3.GL_TEXTURE_2D_MULTISAMPLE_ARRAY; return GL3.GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
} else { } else {
return GL.GL_TEXTURE_2D_ARRAY; return GL2ES3.GL_TEXTURE_2D_ARRAY;
} }
case ThreeDimensional: case ThreeDimensional:
return GL2ES2.GL_TEXTURE_3D; return GL2ES2.GL_TEXTURE_3D;
@ -2014,7 +2014,7 @@ public class JoglRenderer implements Renderer {
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
TextureUtil.uploadTexture(img, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, linearizeSrgbImages); 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)) { if (!caps.contains(Caps.TextureArray)) {
throw new RendererException("Texture arrays not supported by graphics hardware"); throw new RendererException("Texture arrays not supported by graphics hardware");
} }

@ -36,14 +36,17 @@ import com.jme3.renderer.RendererException;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ColorSpace;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.opengl.GL;
import javax.media.opengl.GL2; import com.jogamp.opengl.GL;
import javax.media.opengl.GL2ES2; import com.jogamp.opengl.GL2;
import javax.media.opengl.GL2GL3; import com.jogamp.opengl.GL2ES2;
import javax.media.opengl.GLContext; import com.jogamp.opengl.GL2ES3;
import com.jogamp.opengl.GL2GL3;
import com.jogamp.opengl.GLContext;
public class TextureUtil { public class TextureUtil {
@ -124,9 +127,9 @@ public class TextureUtil {
setFormat(Format.RGB32F, GL.GL_RGB32F, GL.GL_RGB, GL.GL_FLOAT, false); setFormat(Format.RGB32F, GL.GL_RGB32F, GL.GL_RGB, GL.GL_FLOAT, false);
// Special RGB formats // 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.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); setFormat(Format.RGB16F_to_RGB9E5, GL2.GL_RGB9_E5, GL.GL_RGB, GL.GL_HALF_FLOAT, false);
// RGBA formats // RGBA formats
@ -346,7 +349,7 @@ public class TextureUtil {
glFmt.format, glFmt.format,
glFmt.dataType, glFmt.dataType,
data); data);
}else if (target == GL.GL_TEXTURE_2D_ARRAY){ }else if (target == GL2ES3.GL_TEXTURE_2D_ARRAY){
// prepare data for 2D array // prepare data for 2D array
// or upload slice // or upload slice
if (index == -1){ if (index == -1){

@ -45,20 +45,20 @@ import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment; import java.awt.GraphicsEnvironment;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.opengl.DebugGL2; import com.jogamp.opengl.DebugGL2;
import javax.media.opengl.DebugGL3; import com.jogamp.opengl.DebugGL3;
import javax.media.opengl.DebugGL3bc; import com.jogamp.opengl.DebugGL3bc;
import javax.media.opengl.DebugGL4; import com.jogamp.opengl.DebugGL4;
import javax.media.opengl.DebugGL4bc; import com.jogamp.opengl.DebugGL4bc;
import javax.media.opengl.DebugGLES1; import com.jogamp.opengl.DebugGLES1;
import javax.media.opengl.DebugGLES2; import com.jogamp.opengl.DebugGLES2;
import javax.media.opengl.GL; import com.jogamp.opengl.GL;
import javax.media.opengl.GLAutoDrawable; import com.jogamp.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities; import com.jogamp.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener; import com.jogamp.opengl.GLEventListener;
import javax.media.opengl.GLProfile; import com.jogamp.opengl.GLProfile;
import javax.media.opengl.GLRunnable; import com.jogamp.opengl.GLRunnable;
import javax.media.opengl.awt.GLCanvas; import com.jogamp.opengl.awt.GLCanvas;
public abstract class JoglAbstractDisplay extends JoglContext implements GLEventListener { public abstract class JoglAbstractDisplay extends JoglContext implements GLEventListener {

@ -35,7 +35,7 @@ package com.jme3.system.jogl;
import com.jme3.system.JmeCanvasContext; import com.jme3.system.JmeCanvasContext;
import java.awt.Canvas; import java.awt.Canvas;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.opengl.GLAutoDrawable; import com.jogamp.opengl.GLAutoDrawable;
public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext { public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext {

@ -47,14 +47,16 @@ import java.nio.IntBuffer;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.opengl.GL; import com.jogamp.opengl.GL;
import javax.media.opengl.GL2GL3; import com.jogamp.opengl.GL2GL3;
import javax.media.opengl.GLContext; import com.jogamp.opengl.GLContext;
public abstract class JoglContext implements JmeContext { public abstract class JoglContext implements JmeContext {
private static final Logger logger = Logger.getLogger(JoglContext.class.getName()); 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 created = new AtomicBoolean(false);
protected AtomicBoolean renderable = new AtomicBoolean(false); protected AtomicBoolean renderable = new AtomicBoolean(false);
protected final Object createdLock = new Object(); protected final Object createdLock = new Object();

@ -45,7 +45,7 @@ import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.opengl.GLAutoDrawable; import com.jogamp.opengl.GLAutoDrawable;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;

@ -44,19 +44,19 @@ import com.jogamp.opengl.util.AnimatorBase;
import com.jogamp.opengl.util.FPSAnimator; import com.jogamp.opengl.util.FPSAnimator;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.opengl.DebugGL2; import com.jogamp.opengl.DebugGL2;
import javax.media.opengl.DebugGL3; import com.jogamp.opengl.DebugGL3;
import javax.media.opengl.DebugGL3bc; import com.jogamp.opengl.DebugGL3bc;
import javax.media.opengl.DebugGL4; import com.jogamp.opengl.DebugGL4;
import javax.media.opengl.DebugGL4bc; import com.jogamp.opengl.DebugGL4bc;
import javax.media.opengl.DebugGLES1; import com.jogamp.opengl.DebugGLES1;
import javax.media.opengl.DebugGLES2; import com.jogamp.opengl.DebugGLES2;
import javax.media.opengl.GL; import com.jogamp.opengl.GL;
import javax.media.opengl.GLAutoDrawable; import com.jogamp.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities; import com.jogamp.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener; import com.jogamp.opengl.GLEventListener;
import javax.media.opengl.GLProfile; import com.jogamp.opengl.GLProfile;
import javax.media.opengl.GLRunnable; import com.jogamp.opengl.GLRunnable;
public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLEventListener { public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLEventListener {

@ -35,7 +35,7 @@ package com.jme3.system.jogl;
import com.jme3.system.JmeCanvasContext; import com.jme3.system.JmeCanvasContext;
import com.jogamp.newt.awt.NewtCanvasAWT; import com.jogamp.newt.awt.NewtCanvasAWT;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.opengl.GLAutoDrawable; import com.jogamp.opengl.GLAutoDrawable;
public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvasContext { public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvasContext {

@ -42,8 +42,8 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.nativewindow.util.Dimension; import com.jogamp.nativewindow.util.Dimension;
import javax.media.opengl.GLAutoDrawable; import com.jogamp.opengl.GLAutoDrawable;
public class JoglNewtDisplay extends JoglNewtAbstractDisplay { public class JoglNewtDisplay extends JoglNewtAbstractDisplay {

@ -41,12 +41,12 @@ import com.jogamp.newt.NewtVersion;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.media.opengl.GL; import com.jogamp.opengl.GL;
import javax.media.opengl.GLCapabilities; import com.jogamp.opengl.GLCapabilities;
import javax.media.opengl.GLContext; import com.jogamp.opengl.GLContext;
import javax.media.opengl.GLDrawableFactory; import com.jogamp.opengl.GLDrawableFactory;
import javax.media.opengl.GLOffscreenAutoDrawable; import com.jogamp.opengl.GLOffscreenAutoDrawable;
import javax.media.opengl.GLProfile; import com.jogamp.opengl.GLProfile;
public class JoglOffscreenBuffer extends JoglContext implements Runnable { public class JoglOffscreenBuffer extends JoglContext implements Runnable {
@ -146,7 +146,7 @@ public class JoglOffscreenBuffer extends JoglContext implements Runnable {
return; return;
} }
new Thread(this, "JOGL Renderer Thread").start(); new Thread(this, THREAD_NAME).start();
if (waitFor) { if (waitFor) {
waitFor(true); waitFor(true);
} }

@ -181,7 +181,9 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
// check input after we synchronize with framerate. // check input after we synchronize with framerate.
// this reduces input lag. // this reduces input lag.
if (renderable.get()){
Display.processMessages(); Display.processMessages();
}
// Subclasses just call GLObjectManager clean up objects here // Subclasses just call GLObjectManager clean up objects here
// it is safe .. for now. // it is safe .. for now.

@ -96,7 +96,7 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
canvas.setFocusable(true); canvas.setFocusable(true);
canvas.setIgnoreRepaint(true); canvas.setIgnoreRepaint(true);
renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread"); renderThread = new Thread(LwjglCanvas.this, THREAD_NAME);
renderThread.start(); renderThread.start();
}else if (needClose.get()){ }else if (needClose.get()){
return; return;
@ -162,7 +162,7 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
if (renderThread == null){ if (renderThread == null){
logger.log(Level.FINE, "MAIN: Creating OGL thread."); 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(); renderThread.start();
} }
// do not do anything. // do not do anything.

@ -67,6 +67,8 @@ public abstract class LwjglContext implements JmeContext {
private static final Logger logger = Logger.getLogger(LwjglContext.class.getName()); 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 created = new AtomicBoolean(false);
protected AtomicBoolean renderable = new AtomicBoolean(false); protected AtomicBoolean renderable = new AtomicBoolean(false);
protected final Object createdLock = new Object(); protected final Object createdLock = new Object();

@ -166,7 +166,7 @@ public class LwjglDisplay extends LwjglAbstractDisplay {
return; return;
} }
new Thread(this, "LWJGL Renderer Thread").start(); new Thread(this, THREAD_NAME).start();
if (waitFor) if (waitFor)
waitFor(true); waitFor(true);
} }

@ -170,7 +170,7 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
return; return;
} }
new Thread(this, "LWJGL Renderer Thread").start(); new Thread(this, THREAD_NAME).start();
if (waitFor) if (waitFor)
waitFor(true); waitFor(true);
} }

@ -55,6 +55,7 @@ import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture.MagFilter; import com.jme3.texture.Texture.MagFilter;
import com.jme3.texture.Texture.MinFilter; import com.jme3.texture.Texture.MinFilter;
import com.jme3.texture.Texture2D; import com.jme3.texture.Texture2D;
import com.jme3.texture.image.ColorSpace;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import de.lessvoid.nifty.render.batch.spi.BatchRenderBackend; import de.lessvoid.nifty.render.batch.spi.BatchRenderBackend;
@ -302,7 +303,7 @@ public class JmeBatchRenderBackend implements BatchRenderBackend {
initialData.rewind(); initialData.rewind();
modifyTexture( modifyTexture(
getTextureAtlas(atlasTextureId), 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, x,
y); y);
} }
@ -338,7 +339,7 @@ public class JmeBatchRenderBackend implements BatchRenderBackend {
} }
initialData.rewind(); 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.setMinFilter(MinFilter.NearestNoMipMaps);
texture.setMagFilter(MagFilter.Nearest); texture.setMagFilter(MagFilter.Nearest);
return texture; return texture;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save