فهرست منبع

Merge remote-tracking branch 'origin/master' into in-pass-shadows

Kirill Vainer 7 سال پیش
والد
کامیت
6fb2d029d2
100فایلهای تغییر یافته به همراه4048 افزوده شده و 3402 حذف شده
  1. 1 0
      .gitignore
  2. 1 1
      CONTRIBUTING.md
  3. 1 1
      README.md
  4. 7 7
      build.gradle
  5. 3 0
      common.gradle
  6. 2 2
      gradle.properties
  7. BIN
      gradle/wrapper/gradle-wrapper.jar
  8. 2 2
      gradle/wrapper/gradle-wrapper.properties
  9. 15 7
      gradlew
  10. 0 6
      gradlew.bat
  11. 1 1
      jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/MainActivity.java
  12. 1 1
      jme3-android/build.gradle
  13. 4 4
      jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java
  14. 2 2
      jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
  15. 5 5
      jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
  16. 1 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java
  17. 7 7
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/CurvesTemporalMesh.java
  18. 3 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DnaBlockData.java
  19. 3 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DynamicArray.java
  20. 3 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java
  21. 23 13
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  22. 9 9
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Vector3d.java
  23. 20 17
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
  24. 2 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java
  25. 67 66
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageLoader.java
  26. 2 2
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java
  27. 26 6
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java
  28. 11 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
  29. 2 2
      jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java
  30. 10 3
      jme3-bullet/src/common/java/com/jme3/bullet/control/AbstractPhysicsControl.java
  31. 1 10
      jme3-bullet/src/common/java/com/jme3/bullet/control/BetterCharacterControl.java
  32. 3 14
      jme3-bullet/src/common/java/com/jme3/bullet/control/CharacterControl.java
  33. 4 11
      jme3-bullet/src/common/java/com/jme3/bullet/control/GhostControl.java
  34. 17 29
      jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java
  35. 3 25
      jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java
  36. 3 48
      jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java
  37. 4 4
      jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java
  38. 3 3
      jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java
  39. 3 3
      jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java
  40. 2 2
      jme3-bullet/src/main/java/com/jme3/bullet/joints/ConeJoint.java
  41. 2 2
      jme3-bullet/src/main/java/com/jme3/bullet/joints/HingeJoint.java
  42. 2 2
      jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsRigidBody.java
  43. 5 5
      jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java
  44. 2 2
      jme3-bullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java
  45. 10 2
      jme3-bullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java
  46. 1 1
      jme3-core/src/main/java/checkers/quals/DefaultQualifiers.java
  47. 369 365
      jme3-core/src/main/java/com/jme3/animation/AnimChannel.java
  48. 2 31
      jme3-core/src/main/java/com/jme3/animation/AnimControl.java
  49. 4 6
      jme3-core/src/main/java/com/jme3/animation/AnimationUtils.java
  50. 11 9
      jme3-core/src/main/java/com/jme3/animation/Bone.java
  51. 60 32
      jme3-core/src/main/java/com/jme3/animation/BoneTrack.java
  52. 49 3
      jme3-core/src/main/java/com/jme3/animation/CompactArray.java
  53. 9 24
      jme3-core/src/main/java/com/jme3/animation/EffectTrack.java
  54. 4 3
      jme3-core/src/main/java/com/jme3/animation/Skeleton.java
  55. 12 50
      jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java
  56. 37 18
      jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java
  57. 4 4
      jme3-core/src/main/java/com/jme3/animation/Track.java
  58. 19 13
      jme3-core/src/main/java/com/jme3/app/DetailedProfilerState.java
  59. 4 3
      jme3-core/src/main/java/com/jme3/app/StatsView.java
  60. 18 18
      jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java
  61. 2 2
      jme3-core/src/main/java/com/jme3/asset/AssetManager.java
  62. 3 2
      jme3-core/src/main/java/com/jme3/asset/ImplHandler.java
  63. 1 2
      jme3-core/src/main/java/com/jme3/asset/cache/WeakRefCloneAssetCache.java
  64. 5 5
      jme3-core/src/main/java/com/jme3/audio/AudioNode.java
  65. 261 76
      jme3-core/src/main/java/com/jme3/audio/openal/AL.java
  66. 72 19
      jme3-core/src/main/java/com/jme3/audio/openal/ALC.java
  67. 738 660
      jme3-core/src/main/java/com/jme3/audio/openal/EFX.java
  68. 715 720
      jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java
  69. 93 89
      jme3-core/src/main/java/com/jme3/cinematic/KeyFrame.java
  70. 3 3
      jme3-core/src/main/java/com/jme3/cinematic/MotionPath.java
  71. 2 2
      jme3-core/src/main/java/com/jme3/cinematic/MotionPathListener.java
  72. 32 3
      jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java
  73. 146 0
      jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java
  74. 478 491
      jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java
  75. 230 229
      jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java
  76. 2 2
      jme3-core/src/main/java/com/jme3/collision/SweepSphere.java
  77. 5 5
      jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java
  78. 2 2
      jme3-core/src/main/java/com/jme3/effect/influencers/ParticleInfluencer.java
  79. 2 2
      jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshConvexHullShape.java
  80. 2 2
      jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java
  81. 3 3
      jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java
  82. 6 4
      jme3-core/src/main/java/com/jme3/effect/shapes/EmitterSphereShape.java
  83. 5 1
      jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java
  84. 43 33
      jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java
  85. 2 2
      jme3-core/src/main/java/com/jme3/environment/generation/IrradianceSphericalHarmonicsGenerator.java
  86. 162 73
      jme3-core/src/main/java/com/jme3/environment/generation/PrefilteredEnvMapFaceGenerator.java
  87. 2 2
      jme3-core/src/main/java/com/jme3/environment/generation/RunnableWithProgress.java
  88. 25 9
      jme3-core/src/main/java/com/jme3/environment/util/CubeMapWrapper.java
  89. 14 8
      jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java
  90. 2 2
      jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java
  91. 6 8
      jme3-core/src/main/java/com/jme3/input/ChaseCamera.java
  92. 14 7
      jme3-core/src/main/java/com/jme3/input/InputManager.java
  93. 2 2
      jme3-core/src/main/java/com/jme3/input/Joystick.java
  94. 10 0
      jme3-core/src/main/java/com/jme3/input/KeyInput.java
  95. 2 5
      jme3-core/src/main/java/com/jme3/light/LightProbe.java
  96. 2 2
      jme3-core/src/main/java/com/jme3/light/LightProbeBlendingStrategy.java
  97. 3 3
      jme3-core/src/main/java/com/jme3/light/PointLight.java
  98. 2 2
      jme3-core/src/main/java/com/jme3/light/SpotLight.java
  99. 45 0
      jme3-core/src/main/java/com/jme3/material/Materials.java
  100. 3 1
      jme3-core/src/main/java/com/jme3/material/ShaderGenerationInfo.java

+ 1 - 0
.gitignore

@@ -2,6 +2,7 @@
 **/.classpath
 **/.settings
 **/.project
+**/out
 /.gradle/
 /.nb-gradle/
 /.idea/

+ 1 - 1
CONTRIBUTING.md

@@ -55,5 +55,5 @@ We generally abide by the standard Java Code Conventions. Besides that, just mak
 
 ## Documentation
 
-- How to edit the wiki - WIP
+- How to edit the [wiki](https://github.com/jMonkeyEngine/wiki).
 - How to edit JavaDocs - WIP

+ 1 - 1
README.md

@@ -3,7 +3,7 @@ jMonkeyEngine
 
 [![Build Status](https://travis-ci.org/jMonkeyEngine/jmonkeyengine.svg?branch=master)](https://travis-ci.org/jMonkeyEngine/jmonkeyengine)
 
-jMonkeyEngine is a 3D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.1.0 is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll release 3.1.x updates until the major 3.2 release arrives.
+jMonkeyEngine is a 3D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge. 3.2.0 is the latest stable version of the jMonkeyEngine 3 SDK, a complete game development suite. We'll release 3.2.x updates until the major 3.3 release arrives.
 
 The engine is used by several commercial game studios and computer-science courses. Here's a taste:
 

+ 7 - 7
build.gradle

@@ -15,7 +15,7 @@ apply from: file('version.gradle')
 subprojects {
     if(!project.name.equals('jme3-android-examples')) {
         apply from: rootProject.file('common.gradle')
-        if (!['jme3-testdata', 'sdk'].contains(project.name)) {
+        if (!project.name.equals('jme3-testdata')) {
             apply from: rootProject.file('bintray.gradle')
         }
     } else {
@@ -66,9 +66,9 @@ task createZipDistribution(type:Zip,dependsOn:["dist","libDist"], description:"P
     archiveName "jME" + jmeFullVersion + ".zip"
     into("/") {
          from {"./dist"}
-    }            
+    }
     into("/sources") {
-        from {"$buildDir/libDist/sources"}     
+        from {"$buildDir/libDist/sources"}
     }
 }
 
@@ -88,14 +88,14 @@ task dist(dependsOn: [':jme3-examples:dist', 'mergedJavadoc']){
 task mergedJavadoc(type: Javadoc, description: 'Creates Javadoc from all the projects.') {
     title = 'jMonkeyEngine3'
     destinationDir = mkdir("dist/javadoc")
-    
+
     options.encoding = 'UTF-8'
 
     // Allows Javadoc to be generated on Java 8 despite doclint errors.
     if (JavaVersion.current().isJava8Compatible()) {
         options.addStringOption('Xdoclint:none', '-quiet')
     }
-    
+
     options.overview = file("javadoc-overview.html")
     // Note: The closures below are executed lazily.
     source subprojects.collect {project ->
@@ -116,7 +116,7 @@ task mergedSource(type: Copy){
 }
 
 task wrapper(type: Wrapper, description: 'Creates and deploys the Gradle wrapper to the current directory.') {
-    gradleVersion = '3.2.1'
+    gradleVersion = '4.1'
 }
 
 ext {
@@ -137,7 +137,7 @@ task configureAndroidNDK {
     if (System.env.ANDROID_NDK != null) {
         ndkBuildPath = System.env.ANDROID_NDK + File.separator + ndkBuildFile
     }
-    
+
     if (new File(ndkBuildPath).exists()) {
         ndkExists = true
         ndkCommandPath = ndkBuildPath

+ 3 - 0
common.gradle

@@ -16,6 +16,9 @@ repositories {
     maven {
         url "http://nifty-gui.sourceforge.net/nifty-maven-repo"
     }
+    flatDir {
+        dirs rootProject.file('lib')
+    }
 }
 
 dependencies {

+ 2 - 2
gradle.properties

@@ -1,7 +1,7 @@
 # Version number used for plugins, only 3 numbers (e.g. 3.1.3)
-jmeVersion = 3.2.0
+jmeVersion = 3.3.0
 # Version used for application and settings folder, no spaces!
-jmeMainVersion = 3.2
+jmeMainVersion = 3.3
 # Version addition pre-alpha-svn, Stable, Beta
 jmeVersionTag = SNAPSHOT
 # Increment this each time jmeVersionTag changes but jmeVersion stays the same

BIN
gradle/wrapper/gradle-wrapper.jar


+ 2 - 2
gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
-#Fri Nov 25 13:05:50 EST 2016
+#Sun Sep 17 22:55:30 EDT 2017
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip

+ 15 - 7
gradlew

@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
 
 ##############################################################################
 ##
@@ -154,11 +154,19 @@ if $cygwin ; then
     esac
 fi
 
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
-    JVM_OPTS=("$@")
+# Escape application args
+save ( ) {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
 }
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=$(save "$@")
 
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"

+ 0 - 6
gradlew.bat

@@ -49,7 +49,6 @@ goto fail
 @rem Get command-line arguments, handling Windows variants
 
 if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
 
 :win9xME_args
 @rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
 if "x%~1" == "x" goto execute
 
 set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
 
 :execute
 @rem Setup the command line

+ 1 - 1
jme3-android-examples/src/main/java/org/jmonkeyengine/jme3androidexamples/MainActivity.java

@@ -214,7 +214,7 @@ public class MainActivity extends AppCompatActivity implements OnItemClickListen
 
     /**
      * User clicked a view on the screen.  Check for the OK and Cancel buttons
-     * and either start the applicaiton or exit.
+     * and either start the application or exit.
      * @param view
      */
     public void onClick(View view) {

+ 1 - 1
jme3-android/build.gradle

@@ -5,5 +5,5 @@ if (!hasProperty('mainClass')) {
 dependencies {
     compile project(':jme3-core')
     compile project(':jme3-plugins')
-    compile files('../lib/android.jar')
+    compileOnly 'android:android'
 }

+ 4 - 4
jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -59,7 +59,7 @@ import java.util.logging.Logger;
 /**
  * AndroidSensorJoyInput converts the Android Sensor system into Joystick events.
  * A single joystick is configured and includes data for all configured sensors
- * as seperate axes of the joystick.
+ * as separate axes of the joystick.
  *
  * Each axis is named according to the static strings in SensorJoystickAxis.
  * Refer to the strings defined in SensorJoystickAxis for a list of supported
@@ -285,8 +285,8 @@ public class AndroidSensorJoyInput implements SensorEventListener {
     }
 
     /**
-     * Calculates the device orientation based off the data recieved from the
-     * Acceleration Sensor and Mangetic Field sensor
+     * Calculates the device orientation based off the data received from the
+     * Acceleration Sensor and Magnetic Field sensor
      * Values are returned relative to the Earth.
      *
      * From the Android Doc

+ 2 - 2
jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -136,7 +136,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         // stops at the setFormat call without a crash.
         // We look at the user setting for alpha bits and set the surfaceview
         // PixelFormat to either Opaque, Transparent, or Translucent.
-        // ConfigChooser will do it's best to honor the alpha requested by the user
+        // ConfigChooser will do its best to honor the alpha requested by the user
         // For best rendering performance, use Opaque (alpha bits = 0).
         int curAlphaBits = settings.getAlphaBits();
         logger.log(Level.FINE, "curAlphaBits: {0}", curAlphaBits);

+ 5 - 5
jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -69,7 +69,7 @@ public class BlenderKey extends ModelKey {
      */
     protected String                   usedWorld;
     /**
-     * User's default material that is set fo objects that have no material definition in blender. The default value is
+     * User's default material that is set for objects that have no material definition in blender. The default value is
      * null. If the value is null the importer will use its own default material (gray color - like in blender).
      */
     protected Material                 defaultMaterial;
@@ -476,9 +476,9 @@ public class BlenderKey extends ModelKey {
     }
 
     /**
-     * This mehtod sets the name of the WORLD data block taht should be used during file loading. By default the name is
+     * This method sets the name of the WORLD data block that should be used during file loading. By default the name is
      * not set. If no name is set or the given name does not occur in the file - the first WORLD data block will be used
-     * during loading (assumin any exists in the file).
+     * during loading (assuming any exists in the file).
      * @param usedWorld
      *            the name of the WORLD block used during loading
      */
@@ -487,7 +487,7 @@ public class BlenderKey extends ModelKey {
     }
 
     /**
-     * This mehtod returns the name of the WORLD data block taht should be used during file loading.
+     * This method returns the name of the WORLD data block that should be used during file loading.
      * @return the name of the WORLD block used during loading
      */
     public String getUsedWorld() {

+ 1 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/SimulationNode.java

@@ -237,7 +237,7 @@ public class SimulationNode {
                         }
                     }
 
-                    // ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
+                    // ... add virtual tracks if necessary, for bones that were altered but had no tracks before ...
                     for (Long boneOMA : alteredOmas) {
                         BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
                         int boneIndex = skeleton.getBoneIndex(boneContext.getBone());

+ 7 - 7
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/curves/CurvesTemporalMesh.java

@@ -37,7 +37,7 @@ import com.jme3.util.BufferUtils;
 
 /**
  * A temporal mesh for curves and surfaces. It works in similar way as TemporalMesh for meshes.
- * It prepares all neccessary lines and faces and allows to apply modifiers just like in regular temporal mesh.
+ * It prepares all necessary lines and faces and allows to apply modifiers just like in regular temporal mesh.
  * 
  * @author Marcin Roguski (Kaelthas)
  */
@@ -210,7 +210,7 @@ public class CurvesTemporalMesh extends TemporalMesh {
     }
 
     /**
-     * The method computes the value of a point at the certain relational distance from its beggining.
+     * The method computes the value of a point at the certain relational distance from its beginning.
      * @param alongRatio
      *            the relative distance along the curve; should be a value between 0 and 1 inclusive;
      *            if the value exceeds the boundaries it is truncated to them
@@ -369,7 +369,7 @@ public class CurvesTemporalMesh extends TemporalMesh {
     }
 
     /**
-     * The method loads the bevel object that sould be applied to curve. It can either be another curve or a generated one
+     * The method loads the bevel object that should be applied to curve. It can either be another curve or a generated one
      * based on the bevel generating parameters in blender.
      * @param curveStructure
      *            the structure with the curve's data (the curve being loaded, NOT the bevel curve)
@@ -707,7 +707,7 @@ public class CurvesTemporalMesh extends TemporalMesh {
 
     /**
      * the method applies scale for the given bevel points. The points table is
-     * being modified so expect ypur result there.
+     * being modified so expect your result there.
      * 
      * @param points
      *            the bevel points
@@ -726,7 +726,7 @@ public class CurvesTemporalMesh extends TemporalMesh {
 
     /**
      * A helper class that represents a single bezier line. It consists of Edge's and allows to
-     * get a subline of a lentgh of the line.
+     * get a subline of a length of the line.
      * 
      * @author Marcin Roguski (Kaelthas)
      */
@@ -776,7 +776,7 @@ public class CurvesTemporalMesh extends TemporalMesh {
             }
             if (cyclic) {
                 // if the first vertex is repeated at the end the distance will be = 0 so it won't affect the result, and if it is not repeated
-                // then it is neccessary to add the length between the last and the first vertex
+                // then it is necessary to add the length between the last and the first vertex
                 length += vertices[vertices.length - 1].distance(vertices[0]);
             }
         }
@@ -834,7 +834,7 @@ public class CurvesTemporalMesh extends TemporalMesh {
         }
 
         /**
-         * The method computes the value of a point at the certain relational distance from its beggining.
+         * The method computes the value of a point at the certain relational distance from its beginning.
          * @param alongRatio
          *            the relative distance along the curve; should be a value between 0 and 1 inclusive;
          *            if the value exceeds the boundaries it is truncated to them

+ 3 - 3
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DnaBlockData.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -85,7 +85,7 @@ public class DnaBlockData {
             names[i] = inputStream.readString();
         }
 
-        // reding types
+        // reading types
         inputStream.alignPosition(4);
         identifier = inputStream.readByte() << 24 | inputStream.readByte() << 16 | inputStream.readByte() << 8 | inputStream.readByte();
         if (identifier != TYPE_ID) {
@@ -181,7 +181,7 @@ public class DnaBlockData {
     /**
      * This method converts the given identifier code to string.
      * @param code
-     *            the code taht is to be converted
+     *            the code that is to be converted
      * @return the string value of the identifier
      */
     private String toString(int code) {

+ 3 - 3
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/DynamicArray.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,7 +32,7 @@
 package com.jme3.scene.plugins.blender.file;
 
 /**
- * An array that can be dynamically modified/
+ * An array that can be dynamically modified
  * @author Marcin Roguski
  * @param <T>
  *            the type of stored data in the array
@@ -42,7 +42,7 @@ public class DynamicArray<T> implements Cloneable {
     /** An array object that holds the required data. */
     private T[]   array;
     /**
-     * This table holds the sizes of dimetions of the dynamic table. It's length specifies the table dimension or a
+     * This table holds the sizes of dimensions of the dynamic table. Its length specifies the table dimension or a
      * pointer level. For example: if tableSizes.length == 3 then it either specifies a dynamic table of fixed lengths:
      * dynTable[a][b][c], where a,b,c are stored in the tableSizes table.
      */

+ 3 - 3
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/file/Structure.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -169,7 +169,7 @@ public class Structure implements Cloneable {
     }
 
     /**
-     * This methos should be used on structures that are of a 'ListBase' type. It creates a List of structures that are
+     * This method should be used on structures that are of a 'ListBase' type. It creates a List of structures that are
      * held by this structure within the blend file.
      * @return a list of filled structures
      * @throws BlenderFileException
@@ -232,7 +232,7 @@ public class Structure implements Cloneable {
     }
 
     /**
-     * This method returns the address of the structure. The strucutre should be filled with data otherwise an exception
+     * This method returns the address of the structure. The structure should be filled with data otherwise an exception
      * is thrown.
      * @return the address of the feature stored in this structure
      */

+ 23 - 13
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java

@@ -1,6 +1,7 @@
 package com.jme3.scene.plugins.blender.materials;
 
 import java.io.IOException;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -106,7 +107,7 @@ public final class MaterialContext implements Savable {
             boolean transparent = false;
             if (diffuseColor != null) {
                 transparent = diffuseColor.a < 1.0f;
-                if (loadedTextures.size() > 0) {// texutre covers the material color
+                if (loadedTextures.size() > 0) {// texture covers the material color
                     diffuseColor.set(1, 1, 1, 1);
                 }
             }
@@ -163,6 +164,12 @@ public final class MaterialContext implements Savable {
 
             material.setColor("Ambient", new ColorRGBA(ambientFactor, ambientFactor, ambientFactor, 1f));
         }
+        
+        // initializing unused "user-defined UV coords" to all available
+        Map<String, List<Vector2f>> unusedUserDefinedUVCoords = Collections.emptyMap();
+        if(userDefinedUVCoordinates != null && !userDefinedUVCoordinates.isEmpty()) {
+            unusedUserDefinedUVCoords = new HashMap<>(userDefinedUVCoordinates);
+        }
 
         // applying textures
         int textureIndex = 0;
@@ -175,16 +182,19 @@ public final class MaterialContext implements Savable {
                     String usedUserUVSet = combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
 
                     this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture());
-                    List<Vector2f> uvs = combinedTexture.getResultUVS();
-                    if(uvs != null && uvs.size() > 0) {
-                        VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]);
-                        uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
-                        geometry.getMesh().setBuffer(uvCoordsBuffer);
-                    }//uvs might be null if the user assigned non existing UV coordinates group name to the mesh (this should be fixed in blender file)
                     
-                    if(usedUserUVSet != null) {
-                    	userDefinedUVCoordinates = new HashMap<>(userDefinedUVCoordinates);
-                    	userDefinedUVCoordinates.remove(usedUserUVSet);
+                    if(usedUserUVSet == null || unusedUserDefinedUVCoords.containsKey(usedUserUVSet)) {
+                        List<Vector2f> uvs = combinedTexture.getResultUVS();
+                        if(uvs != null && uvs.size() > 0) {
+                            VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]);
+                            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
+                            geometry.getMesh().setBuffer(uvCoordsBuffer);
+                        }//uvs might be null if the user assigned non existing UV coordinates group name to the mesh (this should be fixed in blender file)
+
+                        // Remove used "user-defined UV coords" from the unused collection
+                        if(usedUserUVSet != null) {
+                	       unusedUserDefinedUVCoords.remove(usedUserUVSet);
+                        }
                     }
                 } else {
                     LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length);
@@ -192,12 +202,12 @@ public final class MaterialContext implements Savable {
             }
         }
 
-        if (userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
+        if (unusedUserDefinedUVCoords != null && unusedUserDefinedUVCoords.size() > 0) {
             LOGGER.fine("Storing unused, user defined UV coordinates sets.");
-            if (userDefinedUVCoordinates.size() > TextureHelper.TEXCOORD_TYPES.length) {
+            if (unusedUserDefinedUVCoords.size() > TextureHelper.TEXCOORD_TYPES.length) {
                 LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different UV coordinates for the mesh. JME supports only {0} UV coordinates buffers.", TextureHelper.TEXCOORD_TYPES.length);
             }
-            for (Entry<String, List<Vector2f>> entry : userDefinedUVCoordinates.entrySet()) {
+            for (Entry<String, List<Vector2f>> entry : unusedUserDefinedUVCoords.entrySet()) {
                 if (textureIndex < TextureHelper.TEXCOORD_TYPES.length) {
                     List<Vector2f> uvs = entry.getValue();
                     VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]);

+ 9 - 9
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/math/Vector3d.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -324,7 +324,7 @@ public final class Vector3d implements Savable, Cloneable, Serializable {
      *            the vector to take the cross product of with this.
      * @param result
      *            the vector to store the cross product result.
-     * @return result, after recieving the cross product vector.
+     * @return result, after receiving the cross product vector.
      */
     public Vector3d cross(Vector3d v, Vector3d result) {
         return this.cross(v.x, v.y, v.z, result);
@@ -342,7 +342,7 @@ public final class Vector3d implements Savable, Cloneable, Serializable {
      *            z component of the vector to take the cross product of with this.
      * @param result
      *            the vector to store the cross product result.
-     * @return result, after recieving the cross product vector.
+     * @return result, after receiving the cross product vector.
      */
     public Vector3d cross(double otherX, double otherY, double otherZ, Vector3d result) {
         if (result == null) {
@@ -485,12 +485,12 @@ public final class Vector3d implements Savable, Cloneable, Serializable {
     }
 
     /**
-     * <code>multLocal</code> multiplies a provided vector to this vector
+     * <code>multLocal</code> multiplies a provided vector by this vector
      * internally, and returns a handle to this vector for easy chaining of
      * calls. If the provided vector is null, null is returned.
      *
      * @param vec
-     *            the vector to mult to this vector.
+     *            the vector to multiply by this vector.
      * @return this
      */
     public Vector3d multLocal(Vector3d vec) {
@@ -522,7 +522,7 @@ public final class Vector3d implements Savable, Cloneable, Serializable {
     }
 
     /**
-     * <code>multLocal</code> multiplies a provided vector to this vector
+     * <code>multLocal</code> multiplies a provided vector by this vector
      * internally, and returns a handle to this vector for easy chaining of
      * calls. If the provided vector is null, null is returned.
      *
@@ -539,7 +539,7 @@ public final class Vector3d implements Savable, Cloneable, Serializable {
     }
 
     /**
-     * <code>multLocal</code> multiplies a provided vector to this vector
+     * <code>multLocal</code> multiplies a provided vector by this vector
      * internally, and returns a handle to this vector for easy chaining of
      * calls. If the provided vector is null, null is returned.
      *
@@ -657,7 +657,7 @@ public final class Vector3d implements Savable, Cloneable, Serializable {
     }
 
     /**
-     * <code>subtractLocal</code> subtracts a provided vector to this vector
+     * <code>subtractLocal</code> subtracts a provided vector from this vector
      * internally, and returns a handle to this vector for easy chaining of
      * calls. If the provided vector is null, null is returned.
      *
@@ -825,7 +825,7 @@ public final class Vector3d implements Savable, Cloneable, Serializable {
 
     /**
      * <code>hashCode</code> returns a unique code for this vector object based
-     * on it's values. If two vectors are logically equivalent, they will return
+     * on its values. If two vectors are logically equivalent, they will return
      * the same hash code value.
      * @return the hash code value of this vector.
      */

+ 20 - 17
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/MeshHelper.java

@@ -291,27 +291,30 @@ public class MeshHelper extends AbstractBlenderHelper {
         	Structure defbase = (Structure) parent.getFieldValue("defbase");
             List<String> groupNames = new ArrayList<String>();
             List<Structure> defs = defbase.evaluateListBase();
-            for (Structure def : defs) {
-                groupNames.add(def.getFieldValue("name").toString());
-            }
+            
+            if(!defs.isEmpty()) {
+                for (Structure def : defs) {
+                    groupNames.add(def.getFieldValue("name").toString());
+                }
 
-            Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
-            if (pDvert.isNotNull()) {// assigning weights and bone indices
-                List<Structure> dverts = pDvert.fetchData();
-                for (Structure dvert : dverts) {
-                    Map<String, Float> weightsForVertex = new HashMap<String, Float>();
-                    Pointer pDW = (Pointer) dvert.getFieldValue("dw");
-                    if (pDW.isNotNull()) {
-                        List<Structure> dw = pDW.fetchData();
-                        for (Structure deformWeight : dw) {
-                            int groupIndex = ((Number) deformWeight.getFieldValue("def_nr")).intValue();
-                            float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
-                            String groupName = groupNames.get(groupIndex);
+                Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
+                if (pDvert.isNotNull()) {// assigning weights and bone indices
+                    List<Structure> dverts = pDvert.fetchData();
+                    for (Structure dvert : dverts) {
+                        Map<String, Float> weightsForVertex = new HashMap<String, Float>();
+                        Pointer pDW = (Pointer) dvert.getFieldValue("dw");
+                        if (pDW.isNotNull()) {
+                            List<Structure> dw = pDW.fetchData();
+                            for (Structure deformWeight : dw) {
+                                int groupIndex = ((Number) deformWeight.getFieldValue("def_nr")).intValue();
+                                float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
+                                String groupName = groupNames.get(groupIndex);
 
-                            weightsForVertex.put(groupName, weight);
+                                weightsForVertex.put(groupName, weight);
+                            }
                         }
+                        result.add(weightsForVertex);
                     }
-                    result.add(weightsForVertex);
                 }
             }
         }

+ 2 - 2
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java

@@ -47,7 +47,7 @@ public class SubdivisionSurfaceModifier extends Modifier {
     private Set<Integer>        verticesOnOriginalEdges = new HashSet<Integer>();
 
     /**
-     * Constructor loads all neccessary modifier data.
+     * Constructor loads all necessary modifier data.
      * @param modifierStructure
      *            the modifier structure
      * @param blenderContext
@@ -193,7 +193,7 @@ public class SubdivisionSurfaceModifier extends Modifier {
                 // moving the vertex
                 v.addLocal(t);
 
-                // applying crease weight if neccessary
+                // applying crease weight if necessary
                 CreasePoint creasePoint = creasePoints.get(i);
                 if (creasePoint.getTarget() != null && creasePoint.getWeight() != 0) {
                     t = creasePoint.getTarget().subtractLocal(v).multLocal(creasePoint.getWeight());

+ 67 - 66
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageLoader.java

@@ -31,12 +31,14 @@
  */
 package com.jme3.scene.plugins.blender.textures;
 
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.TextureKey;
 import com.jme3.scene.plugins.blender.file.BlenderInputStream;
 import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
 import com.jme3.texture.plugins.AWTLoader;
-import com.jme3.texture.plugins.DDSLoader;
-import com.jme3.texture.plugins.TGALoader;
-import java.io.InputStream;
+import com.jme3.texture.plugins.HDRLoader;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
@@ -47,11 +49,29 @@ import java.util.logging.Logger;
  */
 /* package */class ImageLoader extends AWTLoader {
     private static final Logger LOGGER    = Logger.getLogger(ImageLoader.class.getName());
-
-    protected DDSLoader         ddsLoader = new DDSLoader();                              // DirectX image loader
+    private static final Logger hdrLogger = Logger.getLogger(HDRLoader.class.getName()); // Used to silence HDR Errors
+    
+    /**
+     * List of Blender-Supported Texture Extensions (we have to guess them, so
+     * the AssetLoader can find them. Not good, but better than nothing.
+     * Source: https://docs.blender.org/manual/en/dev/data_system/files/media/image_formats.html
+     */
+    private static final String[] extensions = new String[]
+        { /* Windows Bitmap */".bmp",
+          /* Iris */ ".sgi", ".rgb", ".bw",
+          /* PNG */ ".png",
+          /* JPEG */ ".jpg", ".jpeg",
+          /* JPEG 2000 */ ".jp2", ".j2c",
+          /* Targa */".tga",
+          /* Cineon & DPX */".cin", ".dpx",
+          /* OpenEXR */ ".exr",
+          /* Radiance HDR */ ".hdr",
+          /* TIFF */ ".tif", ".tiff",
+          /* DDS (Direct X) */ ".dds" };
 
     /**
-     * This method loads the image from the blender file itself. It tries each loader to load the image.
+     * This method loads a image which is packed into the blender file.
+     * It makes use of all the registered AssetLoaders
      * 
      * @param inputStream
      *            blender input stream
@@ -60,76 +80,57 @@ import java.util.logging.Logger;
      * @param flipY
      *            if the image should be flipped (does not work with DirectX image)
      * @return loaded image or null if it could not be loaded
+     * @deprecated This method has only been left in for API compability.
+     * Use loadTexture instead
      */
-    public Image loadImage(BlenderInputStream inputStream, int startPosition, boolean flipY) {
-        // loading using AWT loader
-        inputStream.setPosition(startPosition);
-        Image result = this.loadImage(inputStream, ImageType.AWT, flipY);
-        // loading using TGA loader
-        if (result == null) {
-            inputStream.setPosition(startPosition);
-            result = this.loadImage(inputStream, ImageType.TGA, flipY);
-        }
-        // loading using DDS loader
-        if (result == null) {
-            inputStream.setPosition(startPosition);
-            result = this.loadImage(inputStream, ImageType.DDS, flipY);
+    public Image loadImage(AssetManager assetManager, BlenderInputStream inputStream, int startPosition, boolean flipY) {
+        Texture tex = loadTexture(assetManager, inputStream, startPosition, flipY);
+        
+        if (tex == null) {
+            return null;
+        } else {
+            return tex.getImage();
         }
-
-        if (result == null) {
-            LOGGER.warning("Image could not be loaded by none of available loaders!");
-        }
-
-        return result;
     }
-
+    
     /**
-     * This method loads an image of a specified type from the given input stream.
+     * This method loads a texture which is packed into the blender file.
+     * It makes use of all the registered AssetLoaders
      * 
      * @param inputStream
-     *            the input stream we read the image from
-     * @param imageType
-     *            the type of the image {@link ImageType}
+     *            blender input stream
+     * @param startPosition
+     *            position in the stream where the image data starts
      * @param flipY
      *            if the image should be flipped (does not work with DirectX image)
-     * @return loaded image or null if it could not be loaded
+     * @return loaded texture or null if it could not be loaded
      */
-    public Image loadImage(InputStream inputStream, ImageType imageType, boolean flipY) {
-        Image result = null;
-        switch (imageType) {
-            case AWT:
-                try {
-                    result = this.load(inputStream, flipY);
-                } catch (Exception e) {
-                    LOGGER.warning("Unable to load image using AWT loader!");
-                }
-                break;
-            case DDS:
-                try {
-                    result = ddsLoader.load(inputStream);
-                } catch (Exception e) {
-                    LOGGER.warning("Unable to load image using DDS loader!");
-                }
-                break;
-            case TGA:
-                try {
-                    result = TGALoader.load(inputStream, flipY);
-                } catch (Exception e) {
-                    LOGGER.warning("Unable to load image using TGA loader!");
-                }
-                break;
-            default:
-                throw new IllegalStateException("Unknown image type: " + imageType);
+    public Texture loadTexture(AssetManager assetManager, BlenderInputStream inputStream, int startPosition, boolean flipY) {
+        inputStream.setPosition(startPosition);
+        TextureKey tKey;
+        Texture result = null;
+        
+        hdrLogger.setLevel(Level.SEVERE); // When we bruteforce try HDR on a non hdr file, it prints unreadable chars
+        
+        for (String ext: extensions) {
+            tKey = new TextureKey("dummy" + ext, flipY);
+            try {
+                result = assetManager.loadAssetFromStream(tKey, inputStream);
+            } catch (Exception e) {
+                continue;
+            }
+            
+            if (result != null) {
+                break; // Could locate a possible asset
+            }
+        }
+        
+        if (result == null) {
+            LOGGER.warning("Texture could not be loaded by any of the available loaders!\n"
+                    + "Since the file has been packed into the blender file, there is no"
+                    + "way for us to tell you which texture it was.");
         }
+        
         return result;
     }
-
-    /**
-     * Image types that can be loaded. AWT: png, jpg, jped or bmp TGA: tga DDS: DirectX image files
-     * 
-     * @author Marcin Roguski (Kaelthas)
-     */
-    private static enum ImageType {
-        AWT, TGA, DDS;
-    }
 }

+ 2 - 2
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/ImageUtils.java

@@ -206,7 +206,7 @@ public final class ImageUtils {
                 N.z = 1;
                 N.divideLocal(den);
 
-                // setting thge pixel in the result image
+                // setting the pixel in the result image
                 bumpMap.setRGB(x, y, ImageUtils.vectorToColor(N.x, N.y, N.z));
             }
         }
@@ -422,7 +422,7 @@ public final class ImageUtils {
      *            pixel's X coordinate
      * @param y
      *            pixel's Y coordinate
-     * @return height reprezented by the given texture in the specified location
+     * @return height represented by the given texture in the specified location
      */
     private static int getHeight(BufferedImage image, int x, int y) {
         if (x < 0) {

+ 26 - 6
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/TextureHelper.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -72,6 +72,7 @@ import com.jme3.texture.Texture.WrapMode;
 import com.jme3.texture.Texture2D;
 import com.jme3.texture.image.ColorSpace;
 import com.jme3.util.BufferUtils;
+import com.jme3.util.PlaceholderAssets;
 
 /**
  * A class that is used in texture calculations.
@@ -251,7 +252,11 @@ public class TextureHelper extends AbstractBlenderHelper {
                     blenderContext.getInputStream().setPosition(dataFileBlock.getBlockPosition());
 
                     // Should the texture be flipped? It works for sinbad ..
-                    result = new Texture2D(new ImageLoader().loadImage(blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true));
+                    result = new ImageLoader().loadTexture(blenderContext.getAssetManager(), blenderContext.getInputStream(), dataFileBlock.getBlockPosition(), true);
+                    if (result == null) {
+                        result = new Texture2D(PlaceholderAssets.getPlaceholderImage(blenderContext.getAssetManager()));
+                        LOGGER.fine("ImageLoader returned null. It probably failed to load the packed texture, using placeholder asset");
+                    }
                 }
             }
         //} else {
@@ -310,7 +315,7 @@ public class TextureHelper extends AbstractBlenderHelper {
      * @param pos
      *            the relative position (value of range <0, 1> (both inclusive))
      * @param size
-     *            the size of the line the pixel lies on (width, heigth or
+     *            the size of the line the pixel lies on (width, height or
      *            depth)
      * @return the integer index of the pixel on the line of the specified width
      */
@@ -429,10 +434,10 @@ public class TextureHelper extends AbstractBlenderHelper {
     }
 
     /**
-     * This method loads the textre from outside the blend file using the
+     * This method loads the texture from outside the blend file using the
      * AssetManager that the blend file was loaded with. It returns a texture
      * with a full assetKey that references the original texture so it later
-     * doesn't need to ba packed when the model data is serialized. It searches
+     * doesn't need to be packed when the model data is serialized. It searches
      * the AssetManager for the full path if the model file is a relative path
      * and will attempt to truncate the path if it is an absolute file path
      * until the path can be found in the AssetManager. If the texture can not
@@ -614,8 +619,15 @@ public class TextureHelper extends AbstractBlenderHelper {
                     int texflag = ((Number) textureData.mtex.getFieldValue("texflag")).intValue();
                     boolean negateTexture = (texflag & 0x04) != 0;
 
+                    boolean colorSet = false;
                     for (int i = 0; i < mappings.length; ++i) {
                         if ((mappings[i] & mapto.intValue()) != 0) {
+                            if(mappings[i] == MaterialContext.MTEX_COL) {
+                                colorSet = true;
+                            } else if(colorSet && mappings[i] == MaterialContext.MTEX_ALPHA) {
+                                continue;
+                            }
+                            
                             CombinedTexture combinedTexture = new CombinedTexture(mappings[i], !skyTexture);
                             int blendType = ((Number) textureData.mtex.getFieldValue("blendtype")).intValue();
                             float[] color = new float[] { ((Number) textureData.mtex.getFieldValue("r")).floatValue(), ((Number) textureData.mtex.getFieldValue("g")).floatValue(), ((Number) textureData.mtex.getFieldValue("b")).floatValue() };
@@ -646,8 +658,16 @@ public class TextureHelper extends AbstractBlenderHelper {
         Map<Number, List<TextureData>> result = new HashMap<Number, List<TextureData>>();
         for (TextureData data : textures) {
             Number mapto = (Number) data.mtex.getFieldValue("mapto");
+            
+            boolean colorSet = false;
             for (int i = 0; i < mappings.length; ++i) {
                 if ((mappings[i] & mapto.intValue()) != 0) {
+                    if(mappings[i] == MaterialContext.MTEX_COL) {
+                        colorSet = true;
+                    } else if(colorSet && mappings[i] == MaterialContext.MTEX_ALPHA) {
+                        continue;
+                    }
+                    
                     List<TextureData> datas = result.get(mappings[i]);
                     if (datas == null) {
                         datas = new ArrayList<TextureData>();
@@ -668,4 +688,4 @@ public class TextureHelper extends AbstractBlenderHelper {
         /** The name of the user's UV coordinates that are used for this texture. */
         public String    uvCoordinatesName;
     }
-}
+}

+ 11 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java

@@ -163,9 +163,19 @@ public class TextureBlenderAWT extends AbstractTextureBlender {
      *            the blender context
      */
     protected void blendPixel(float[] result, float[] materialColor, float[] pixelColor, BlenderContext blenderContext) {
+        // We calculate first the importance of the texture (colFactor * texAlphaValue)
         float blendFactor = this.blendFactor * pixelColor[3];
-        float oneMinusFactor = 1.0f - blendFactor, col;
+        // Then, we get the object material factor ((1 - texture importance) * matAlphaValue) 
+        float oneMinusFactor = (1f - blendFactor) * materialColor[3];
+        // Finally, we can get the final blendFactor, which is 1 - the material factor.
+        blendFactor = 1f - oneMinusFactor;
+        
+        // --- Compact method ---
+        // float blendFactor = this.blendFactor * (1f - ((1f - pixelColor[3]) * materialColor[3]));
+        // float oneMinusFactor = 1f - blendFactor;
 
+        float col;
+        
         switch (blendType) {
             case MTEX_BLEND:
                 result[0] = blendFactor * pixelColor[0] + oneMinusFactor * materialColor[0];

+ 2 - 2
jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,7 @@ public interface PhysicsCollisionGroupListener {
     /**
      * Called when two physics objects of the registered group are about to collide, <i>called from physics thread</i>.<br>
      * This is only called when the collision will happen based on the collisionGroup and collideWithGroups
-     * settings in the PhysicsCollisionObject. That is the case when <b>one</b> of the partys has the
+     * settings in the PhysicsCollisionObject. That is the case when <b>one</b> of the parties has the
      * collisionGroup of the other in its collideWithGroups set.<br>
      * @param nodeA CollisionObject #1
      * @param nodeB CollisionObject #2

+ 10 - 3
jme3-bullet/src/common/java/com/jme3/bullet/control/AbstractPhysicsControl.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -41,6 +41,7 @@ import com.jme3.math.Vector3f;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
 import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
 import com.jme3.util.clone.Cloner;
 import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
@@ -162,8 +163,14 @@ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeClone
         }
 
     }
-    
-    @Override   
+
+    @Deprecated
+    @Override
+    public Control cloneForSpatial(Spatial spatial) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override  
     public void cloneFields( Cloner cloner, Object original ) { 
         this.spatial = cloner.clone(spatial);
         createSpatialData(this.spatial);

+ 1 - 10
jme3-bullet/src/common/java/com/jme3/bullet/control/BetterCharacterControl.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -48,9 +48,7 @@ import com.jme3.math.Vector3f;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
 import com.jme3.scene.Spatial;
-import com.jme3.scene.control.Control;
 import com.jme3.util.TempVars;
-import com.jme3.util.clone.Cloner;
 import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 import java.util.List;
@@ -665,13 +663,6 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph
         rigidBody.setUserObject(null);
     }
 
-    @Override
-    public Control cloneForSpatial(Spatial spatial) {
-        BetterCharacterControl control = new BetterCharacterControl(radius, height, mass);
-        control.setJumpForce(jumpForce);
-        return control;
-    }
-
     @Override
     public Object jmeClone() {
         BetterCharacterControl control = new BetterCharacterControl(radius, height, mass);

+ 3 - 14
jme3-bullet/src/common/java/com/jme3/bullet/control/CharacterControl.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -89,21 +89,10 @@ public class CharacterControl extends PhysicsCharacter implements PhysicsControl
         return spatial.getWorldTranslation();
     }
 
+    @Deprecated
     @Override
     public Control cloneForSpatial(Spatial spatial) {
-        CharacterControl control = new CharacterControl(collisionShape, stepHeight);
-        control.setCcdMotionThreshold(getCcdMotionThreshold());
-        control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
-        control.setCollideWithGroups(getCollideWithGroups());
-        control.setCollisionGroup(getCollisionGroup());
-        control.setFallSpeed(getFallSpeed());
-        control.setGravity(getGravity());
-        control.setJumpSpeed(getJumpSpeed());
-        control.setMaxSlope(getMaxSlope());
-        control.setPhysicsLocation(getPhysicsLocation());
-        control.setUpAxis(getUpAxis());
-        control.setApplyPhysicsLocal(isApplyPhysicsLocal());
-        return control;
+        throw new UnsupportedOperationException();
     }
 
     @Override

+ 4 - 11
jme3-bullet/src/common/java/com/jme3/bullet/control/GhostControl.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -95,20 +95,13 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl,
         return spatial.getWorldRotation();
     }
 
+    @Deprecated
     @Override
     public Control cloneForSpatial(Spatial spatial) {
-        GhostControl control = new GhostControl(collisionShape);
-        control.setCcdMotionThreshold(getCcdMotionThreshold());
-        control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
-        control.setCollideWithGroups(getCollideWithGroups());
-        control.setCollisionGroup(getCollisionGroup());
-        control.setPhysicsLocation(getPhysicsLocation());
-        control.setPhysicsRotation(getPhysicsRotationMatrix());
-        control.setApplyPhysicsLocal(isApplyPhysicsLocal());
-        return control;
+        throw new UnsupportedOperationException();
     }
 
-    @Override   
+    @Override
     public Object jmeClone() {
         GhostControl control = new GhostControl(collisionShape);
         control.setCcdMotionThreshold(getCcdMotionThreshold());

+ 17 - 29
jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -59,9 +59,7 @@ import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
-import com.jme3.scene.control.Control;
 import com.jme3.util.TempVars;
-import com.jme3.util.clone.Cloner;
 import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 import java.util.*;
@@ -73,7 +71,7 @@ import java.util.logging.Logger;
  * use this control you need a model with an AnimControl and a
  * SkeletonControl.<br> This should be the case if you imported an animated
  * model from Ogre or blender.<br> Note enabling/disabling the control
- * add/removes it from the physic space<br> <p> This control creates collision
+ * add/removes it from the physics space<br> <p> This control creates collision
  * shapes for each bones of the skeleton when you call
  * spatial.addControl(ragdollControl). <ul> <li>The shape is HullCollision shape
  * based on the vertices associated with each bone and based on a tweakable
@@ -84,11 +82,11 @@ import java.util.logging.Logger;
  * </ul> </p> <p> There are 2 modes for this control : <ul> <li><strong>The
  * kinematic modes :</strong><br> this is the default behavior, this means that
  * the collision shapes of the body are able to interact with physics enabled
- * objects. in this mode physic shapes follow the moovements of the animated
+ * objects. in this mode physics shapes follow the motion of the animated
  * skeleton (for example animated by a key framed animation) this mode is
  * enabled by calling setKinematicMode(); </li> <li><strong>The ragdoll modes
  * :</strong><br> To enable this behavior, you need to call setRagdollMode()
- * method. In this mode the charater is entirely controled by physics, so it
+ * method. In this mode the character is entirely controlled by physics, so it
  * will fall under the gravity and move if any force is applied to it. </li>
  * </ul> </p>
  *
@@ -171,7 +169,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
     }
 
     /**
-     * contruct a KinematicRagdollControl
+     * construct a KinematicRagdollControl
      */
     public KinematicRagdollControl() {
         baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(0.1f)), 1);
@@ -201,7 +199,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
         if(mode == Mode.IK){
             ikUpdate(tpf);
         } else if (mode == mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) {
-            //if the ragdoll has the control of the skeleton, we update each bone with its position in physic world space.
+            //if the ragdoll has the control of the skeleton, we update each bone with its position in physics world space.
             ragDollUpdate(tpf);
         } else {
             kinematicUpdate(tpf);
@@ -217,12 +215,12 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
 
             Vector3f position = vars.vect1;
 
-            //retrieving bone position in physic world space
+            //retrieving bone position in physics world space
             Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
             //transforming this position with inverse transforms of the model
             targetModel.getWorldTransform().transformInverseVector(p, position);
 
-            //retrieving bone rotation in physic world space
+            //retrieving bone rotation in physics world space
             Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat();
 
             //multiplying this rotation by the initialWorld rotation of the bone, 
@@ -255,7 +253,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
                     link.bone.setUserTransformsInModelSpace(position, tmpRot1);
                 } else {
                     //boneList is not empty, this means some bones of the skeleton might not be associated with a collision shape.
-                    //So we update them recusively
+                    //So we update them recursively
                     RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList);
                 }
             }
@@ -264,7 +262,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
     }
 
     protected void kinematicUpdate(float tpf) {
-        //the ragdoll does not have the controll, so the keyframed animation updates the physic position of the physic bonces
+        //the ragdoll does not have control, so the keyframed animation updates the physics position of the physics bonces
         TempVars vars = TempVars.get();
         Quaternion tmpRot1 = vars.quat1;
         Quaternion tmpRot2 = vars.quat2;
@@ -420,7 +418,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
         targetModel.getWorldRotation().mult(tmpRot1, tmpRot1);
         tmpRot1.normalizeLocal();
 
-        //updating physic location/rotation of the physic bone
+        //updating physics location/rotation of the physics bone
         link.rigidBody.setPhysicsLocation(position);
         link.rigidBody.setPhysicsRotation(tmpRot1);
 
@@ -707,9 +705,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
 
     /**
      * Enable or disable the ragdoll behaviour. if ragdollEnabled is true, the
-     * character motion will only be powerd by physics else, the characted will
+     * character motion will only be powered by physics else, the character will
      * be animated by the keyframe animation, but will be able to physically
-     * interact with its physic environnement
+     * interact with its physics environment
      *
      * @param ragdollEnabled
      */
@@ -789,9 +787,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
     }
 
     /**
-     * Set the control into Kinematic mode In theis mode, the collision shapes
+     * Set the control into Kinematic mode In this mode, the collision shapes
      * follow the movements of the skeleton, and can interact with physical
-     * environement
+     * environment
      */
     public void setKinematicMode() {
         if (mode != Mode.Kinematic) {
@@ -820,7 +818,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
     }
     
     /**
-     * retruns the mode of this control
+     * returns the mode of this control
      *
      * @return
      */
@@ -903,7 +901,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
     }
 
     /**
-     * For internal use only specific render for the ragdoll(if debugging)
+     * For internal use only specific render for the ragdoll (if debugging)
      *
      * @param rm
      * @param vp
@@ -912,16 +910,6 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
     public void render(RenderManager rm, ViewPort vp) {
     }
 
-    @Override
-    public Control cloneForSpatial(Spatial spatial) {
-        KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold);
-        control.setMode(mode);
-        control.setRootMass(rootMass);
-        control.setWeightThreshold(weightThreshold);
-        control.setApplyPhysicsLocal(applyLocal);
-        return control;
-    }
-   
     @Override   
     public Object jmeClone() {
         KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold);        

+ 3 - 25
jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -53,7 +53,6 @@ import com.jme3.scene.shape.Box;
 import com.jme3.scene.shape.Sphere;
 import com.jme3.util.clone.Cloner;
 import com.jme3.util.clone.JmeCloneable;
-
 import java.io.IOException;
 
 /**
@@ -92,31 +91,10 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl
         super(shape, mass);
     }
 
+    @Deprecated
     @Override
     public Control cloneForSpatial(Spatial spatial) {
-        RigidBodyControl control = new RigidBodyControl(collisionShape, mass);
-        control.setAngularFactor(getAngularFactor());
-        control.setAngularSleepingThreshold(getAngularSleepingThreshold());
-        control.setCcdMotionThreshold(getCcdMotionThreshold());
-        control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
-        control.setCollideWithGroups(getCollideWithGroups());
-        control.setCollisionGroup(getCollisionGroup());
-        control.setDamping(getLinearDamping(), getAngularDamping());
-        control.setFriction(getFriction());
-        control.setGravity(getGravity());
-        control.setKinematic(isKinematic());
-        control.setKinematicSpatial(isKinematicSpatial());
-        control.setLinearSleepingThreshold(getLinearSleepingThreshold());
-        control.setPhysicsLocation(getPhysicsLocation(null));
-        control.setPhysicsRotation(getPhysicsRotationMatrix(null));
-        control.setRestitution(getRestitution());
-
-        if (mass > 0) {
-            control.setAngularVelocity(getAngularVelocity());
-            control.setLinearVelocity(getLinearVelocity());
-        }
-        control.setApplyPhysicsLocal(isApplyPhysicsLocal());
-        return control;
+        throw new UnsupportedOperationException();
     }
 
     @Override   

+ 3 - 48
jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -43,7 +43,6 @@ import com.jme3.math.Quaternion;
 import com.jme3.math.Vector3f;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
-import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.control.Control;
 import com.jme3.util.clone.Cloner;
@@ -108,54 +107,10 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm
         return spatial.getWorldRotation();
     }
 
+    @Deprecated
     @Override
     public Control cloneForSpatial(Spatial spatial) {
-        VehicleControl control = new VehicleControl(collisionShape, mass);
-        control.setAngularFactor(getAngularFactor());
-        control.setAngularSleepingThreshold(getAngularSleepingThreshold());
-        control.setAngularVelocity(getAngularVelocity());
-        control.setCcdMotionThreshold(getCcdMotionThreshold());
-        control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
-        control.setCollideWithGroups(getCollideWithGroups());
-        control.setCollisionGroup(getCollisionGroup());
-        control.setDamping(getLinearDamping(), getAngularDamping());
-        control.setFriction(getFriction());
-        control.setGravity(getGravity());
-        control.setKinematic(isKinematic());
-        control.setLinearSleepingThreshold(getLinearSleepingThreshold());
-        control.setLinearVelocity(getLinearVelocity());
-        control.setPhysicsLocation(getPhysicsLocation());
-        control.setPhysicsRotation(getPhysicsRotationMatrix());
-        control.setRestitution(getRestitution());
-
-        control.setFrictionSlip(getFrictionSlip());
-        control.setMaxSuspensionTravelCm(getMaxSuspensionTravelCm());
-        control.setSuspensionStiffness(getSuspensionStiffness());
-        control.setSuspensionCompression(tuning.suspensionCompression);
-        control.setSuspensionDamping(tuning.suspensionDamping);
-        control.setMaxSuspensionForce(getMaxSuspensionForce());
-
-        for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) {
-            VehicleWheel wheel = it.next();
-            VehicleWheel newWheel = control.addWheel(wheel.getLocation(), wheel.getDirection(), wheel.getAxle(), wheel.getRestLength(), wheel.getRadius(), wheel.isFrontWheel());
-            newWheel.setFrictionSlip(wheel.getFrictionSlip());
-            newWheel.setMaxSuspensionTravelCm(wheel.getMaxSuspensionTravelCm());
-            newWheel.setSuspensionStiffness(wheel.getSuspensionStiffness());
-            newWheel.setWheelsDampingCompression(wheel.getWheelsDampingCompression());
-            newWheel.setWheelsDampingRelaxation(wheel.getWheelsDampingRelaxation());
-            newWheel.setMaxSuspensionForce(wheel.getMaxSuspensionForce());
-
-            //TODO: bad way finding children!
-            if (spatial instanceof Node) {
-                Node node = (Node) spatial;
-                Spatial wheelSpat = node.getChild(wheel.getWheelSpatial().getName());
-                if (wheelSpat != null) {
-                    newWheel.setWheelSpatial(wheelSpat);
-                }
-            }
-        }
-        control.setApplyPhysicsLocal(isApplyPhysicsLocal());
-        return control;
+        throw new UnsupportedOperationException();
     }
 
     @Override   

+ 4 - 4
jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -123,7 +123,7 @@ public class RagdollUtils {
 
     /**
      * Create a hull collision shape from linked vertices to this bone.
-     * Vertices have to be previoulsly gathered in a map using buildPointMap method
+     * Vertices have to be previously gathered in a map using buildPointMap method
      * 
      * @param pointsMap
      * @param boneIndices
@@ -160,7 +160,7 @@ public class RagdollUtils {
         return new HullCollisionShape(p);
     }
 
-    //retruns the list of bone indices of the given bone and its child(if they are not in the boneList)
+    //returns the list of bone indices of the given bone and its child (if they are not in the boneList)
     public static List<Integer> getBoneIndices(Bone bone, Skeleton skeleton, Set<String> boneList) {
         List<Integer> list = new LinkedList<Integer>();
         if (boneList.isEmpty()) {
@@ -266,7 +266,7 @@ public class RagdollUtils {
 
     /**
      * Updates a bone position and rotation.
-     * if the child bones are not in the bone list this means, they are not associated with a physic shape.
+     * if the child bones are not in the bone list this means, they are not associated with a physics shape.
      * So they have to be updated
      * @param bone the bone
      * @param pos the position

+ 3 - 3
jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -235,7 +235,7 @@ public class PhysicsSpace {
 //                    collides = (bp1.collisionFilterGroup & bp.collisionFilterMask) != 0;
 //                }
 //                if (collides) {
-//                    assert (bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject && bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject);
+//                    assert (bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject && bp1.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject);
 //                    com.bulletphysics.collision.dispatch.CollisionObject colOb = (com.bulletphysics.collision.dispatch.CollisionObject) bp.clientObject;
 //                    com.bulletphysics.collision.dispatch.CollisionObject colOb1 = (com.bulletphysics.collision.dispatch.CollisionObject) bp1.clientObject;
 //                    assert (colOb.getUserPointer() != null && colOb1.getUserPointer() != null);
@@ -954,7 +954,7 @@ public class PhysicsSpace {
      * determinism in physics. For example a maximum number of 2 can compensate
      * for framerates as low as 30fps when the physics has the default accuracy
      * of 60 fps. Note that setting this value too high can make the physics
-     * drive down its own fps in case its overloaded.
+     * drive down its own fps in case it's overloaded.
      *
      * @param steps The maximum number of extra steps, default is 4.
      */

+ 3 - 3
jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -96,7 +96,7 @@ public abstract class PhysicsCollisionObject implements Savable {
      * Sets the collision group number for this physics object. <br>
      * The groups are integer bit masks and some pre-made variables are available in CollisionObject.
      * All physics objects are by default in COLLISION_GROUP_01.<br>
-     * Two object will collide when <b>one</b> of the partys has the
+     * Two object will collide when <b>one</b> of the parties has the
      * collisionGroup of the other in its collideWithGroups set.
      * @param collisionGroup the collisionGroup to set
      */
@@ -109,7 +109,7 @@ public abstract class PhysicsCollisionObject implements Savable {
 
     /**
      * Add a group that this object will collide with.<br>
-     * Two object will collide when <b>one</b> of the partys has the
+     * Two object will collide when <b>one</b> of the parties has the
      * collisionGroup of the other in its collideWithGroups set.<br>
      * @param collisionGroup
      */

+ 2 - 2
jme3-bullet/src/main/java/com/jme3/bullet/joints/ConeJoint.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -44,7 +44,7 @@ import java.util.logging.Logger;
 
 /**
  * <i>From bullet manual:</i><br>
- * To create ragdolls, the conve twist constraint is very useful for limbs like the upper arm.
+ * To create ragdolls, the cone twist constraint is very useful for limbs like the upper arm.
  * It is a special point to point constraint that adds cone and twist axis limits.
  * The x-axis serves as twist axis.
  * @author normenhansen

+ 2 - 2
jme3-bullet/src/main/java/com/jme3/bullet/joints/HingeJoint.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -121,7 +121,7 @@ public class HingeJoint extends PhysicsJoint {
      * @param high the high limit in radians.
      * @param _softness the factor at which the velocity error correction starts operating,i.e a softness of 0.9 means that the vel. corr starts at 90% of the limit range.
      * @param _biasFactor the magnitude of the position correction. It tells you how strictly the position error (drift ) is corrected.
-     * @param _relaxationFactor the rate at which velocity errors are corrected. This can be seen as the strength of the limits. A low value will make the the limits more spongy.
+     * @param _relaxationFactor the rate at which velocity errors are corrected. This can be seen as the strength of the limits. A low value will make the limits more spongy.
      */
     public void setLimit(float low, float high, float _softness, float _biasFactor, float _relaxationFactor) {
         biasFactor = _biasFactor;

+ 2 - 2
jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsRigidBody.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -265,7 +265,7 @@ public class PhysicsRigidBody extends PhysicsCollisionObject {
 //    }
     /**
      * Sets the node to kinematic mode. in this mode the node is not affected by physics
-     * but affects other physics objects. Iits kinetic force is calculated by the amount
+     * but affects other physics objects. Its kinetic force is calculated by the amount
      * of movement it is exposed to and its weight.
      * @param kinematic
      */

+ 5 - 5
jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -283,8 +283,8 @@ public class PhysicsVehicle extends PhysicsRigidBody {
     }
 
     /**
-     * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot
-     * handle the weight of your vehcile.
+     * This value caps the maximum suspension force, raise this above the default 6000 if your suspension cannot
+     * handle the weight of your vehicle.
      * @param maxSuspensionForce
      */
     public void setMaxSuspensionForce(float maxSuspensionForce) {
@@ -292,8 +292,8 @@ public class PhysicsVehicle extends PhysicsRigidBody {
     }
 
     /**
-     * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot
-     * handle the weight of your vehcile.
+     * This value caps the maximum suspension force, raise this above the default 6000 if your suspension cannot
+     * handle the weight of your vehicle.
      * @param wheel
      * @param maxSuspensionForce
      */

+ 2 - 2
jme3-bullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -242,7 +242,7 @@ public class VehicleWheel implements Savable {
 
     /**
      * The maximum suspension force, raise this above the default 6000 if your suspension cannot
-     * handle the weight of your vehcile.
+     * handle the weight of your vehicle.
      * @param maxSuspensionTravelCm
      */
     public void setMaxSuspensionForce(float maxSuspensionForce) {

+ 10 - 2
jme3-bullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2017 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,7 @@ import com.jme3.bullet.collision.shapes.CollisionShape;
 import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
 import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape;
 import com.jme3.math.Matrix3f;
+import com.jme3.math.Vector3f;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.Node;
@@ -111,9 +112,16 @@ public class DebugShapeFactory {
 
     public static Mesh getDebugMesh(CollisionShape shape) {
         Mesh mesh = new Mesh();
-        mesh = new Mesh();
         DebugMeshCallback callback = new DebugMeshCallback();
+        /*
+         * Populate the mesh based on an unscaled shape;
+         * the shape's scale will be applied later, to the geometry.
+         */
+        Vector3f savedScale = shape.getScale().clone();
+        shape.setScale(Vector3f.UNIT_XYZ);
         getVertices(shape.getObjectId(), callback);
+        shape.setScale(savedScale);
+
         mesh.setBuffer(Type.Position, 3, callback.getVertices());
         mesh.getFloatBuffer(Type.Position).clear();
         return mesh;

+ 1 - 1
jme3-core/src/main/java/checkers/quals/DefaultQualifiers.java

@@ -15,7 +15,7 @@ import java.lang.annotation.Target;
  * annotations of the same name at a single location.
  *
  * Example:
- * <!-- &nbsp; is a hack that prevents @ from being the first charater on the line, which confuses Javadoc -->
+ * <!-- &nbsp; is a hack that prevents @ from being the first character on the line, which confuses Javadoc -->
  * <code><pre>
  * &nbsp; @DefaultQualifiers({
  * &nbsp;     @DefaultQualifier("NonNull"),

+ 369 - 365
jme3-core/src/main/java/com/jme3/animation/AnimChannel.java

@@ -1,365 +1,369 @@
-/*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.animation;
-
-import com.jme3.math.FastMath;
-import com.jme3.util.TempVars;
-import java.util.BitSet;
-
-/**
- * <code>AnimChannel</code> provides controls, such as play, pause,
- * fast forward, etc, for an animation. The animation
- * channel may influence the entire model or specific bones of the model's
- * skeleton. A single model may have multiple animation channels influencing
- * various parts of its body. For example, a character model may have an
- * animation channel for its feet, and another for its torso, and
- * the animations for each channel are controlled independently.
- * 
- * @author Kirill Vainer
- */
-public final class AnimChannel {
-
-    private static final float DEFAULT_BLEND_TIME = 0.15f;
-    
-    private AnimControl control;
-
-    private BitSet affectedBones;
-
-    private Animation animation;
-    private Animation blendFrom;
-    private float time;
-    private float speed;
-    private float timeBlendFrom;
-    private float blendTime;
-    private float speedBlendFrom;
-    private boolean notified=false;
-
-    private LoopMode loopMode, loopModeBlendFrom;
-    
-    private float blendAmount = 1f;
-    private float blendRate   = 0;
-    
-    AnimChannel(AnimControl control){
-        this.control = control;
-    }
-
-    /**
-     * Returns the parent control of this AnimChannel.
-     * 
-     * @return the parent control of this AnimChannel.
-     * @see AnimControl
-     */
-    public AnimControl getControl() {
-        return control;
-    }
-    
-    /**
-     * @return The name of the currently playing animation, or null if
-     * none is assigned.
-     *
-     * @see AnimChannel#setAnim(java.lang.String) 
-     */
-    public String getAnimationName() {
-        return animation != null ? animation.getName() : null;
-    }
-
-    /**
-     * @return The loop mode currently set for the animation. The loop mode
-     * determines what will happen to the animation once it finishes
-     * playing.
-     * 
-     * For more information, see the LoopMode enum class.
-     * @see LoopMode
-     * @see AnimChannel#setLoopMode(com.jme3.animation.LoopMode)
-     */
-    public LoopMode getLoopMode() {
-        return loopMode;
-    }
-
-    /**
-     * @param loopMode Set the loop mode for the channel. The loop mode
-     * determines what will happen to the animation once it finishes
-     * playing.
-     *
-     * For more information, see the LoopMode enum class.
-     * @see LoopMode
-     */
-    public void setLoopMode(LoopMode loopMode) {
-        this.loopMode = loopMode;
-    }
-
-    /**
-     * @return The speed that is assigned to the animation channel. The speed
-     * is a scale value starting from 0.0, at 1.0 the animation will play
-     * at its default speed.
-     *
-     * @see AnimChannel#setSpeed(float)
-     */
-    public float getSpeed() {
-        return speed;
-    }
-
-    /**
-     * @param speed Set the speed of the animation channel. The speed
-     * is a scale value starting from 0.0, at 1.0 the animation will play
-     * at its default speed.
-     */
-    public void setSpeed(float speed) {
-        this.speed = speed;
-        if(blendTime>0){
-            this.speedBlendFrom = speed;
-            blendTime = Math.min(blendTime, animation.getLength() / speed);  
-            blendRate = 1/ blendTime;
-        }
-    }
-
-    /**
-     * @return The time of the currently playing animation. The time
-     * starts at 0 and continues on until getAnimMaxTime().
-     *
-     * @see AnimChannel#setTime(float)
-     */
-    public float getTime() {
-        return time;
-    }
-
-    /**
-     * @param time Set the time of the currently playing animation, the time
-     * is clamped from 0 to {@link #getAnimMaxTime()}. 
-     */
-    public void setTime(float time) {
-        this.time = FastMath.clamp(time, 0, getAnimMaxTime());
-    }
-
-    /**
-     * @return The length of the currently playing animation, or zero
-     * if no animation is playing.
-     *
-     * @see AnimChannel#getTime()
-     */
-    public float getAnimMaxTime(){
-        return animation != null ? animation.getLength() : 0f;
-    }
-
-    /**
-     * Set the current animation that is played by this AnimChannel.
-     * <p>
-     * This resets the time to zero, and optionally blends the animation
-     * over <code>blendTime</code> seconds with the currently playing animation.
-     * Notice that this method will reset the control's speed to 1.0.
-     *
-     * @param name The name of the animation to play
-     * @param blendTime The blend time over which to blend the new animation
-     * with the old one. If zero, then no blending will occur and the new
-     * animation will be applied instantly.
-     */
-    public void setAnim(String name, float blendTime){
-        if (name == null)
-            throw new IllegalArgumentException("name cannot be null");
-
-        if (blendTime < 0f)
-            throw new IllegalArgumentException("blendTime cannot be less than zero");
-
-        Animation anim = control.animationMap.get(name);
-        if (anim == null)
-            throw new IllegalArgumentException("Cannot find animation named: '"+name+"'");
-
-        control.notifyAnimChange(this, name);
-
-        if (animation != null && blendTime > 0f){
-            this.blendTime = blendTime;
-            // activate blending
-            blendTime = Math.min(blendTime, anim.getLength() / speed);            
-            blendFrom = animation;
-            timeBlendFrom = time;
-            speedBlendFrom = speed;
-            loopModeBlendFrom = loopMode;
-            blendAmount = 0f;
-            blendRate   = 1f / blendTime;
-        }else{
-            blendFrom = null;
-        }
-
-        animation = anim;
-        time = 0;
-        speed = 1f;
-        loopMode = LoopMode.Loop;
-        notified = false;
-    }
-
-    /**
-     * Set the current animation that is played by this AnimChannel.
-     * <p>
-     * See {@link #setAnim(java.lang.String, float)}.
-     * The blendTime argument by default is 150 milliseconds.
-     * 
-     * @param name The name of the animation to play
-     */
-    public void setAnim(String name){
-        setAnim(name, DEFAULT_BLEND_TIME);
-    }
-
-    /**
-     * Add all the bones of the model's skeleton to be
-     * influenced by this animation channel.
-     */
-    public void addAllBones() {
-        affectedBones = null;
-    }
-
-    /**
-     * Add a single bone to be influenced by this animation channel.
-     */
-    public void addBone(String name) {
-        addBone(control.getSkeleton().getBone(name));
-    }
-
-    /**
-     * Add a single bone to be influenced by this animation channel.
-     */
-    public void addBone(Bone bone) {
-        int boneIndex = control.getSkeleton().getBoneIndex(bone);
-        if(affectedBones == null) {
-            affectedBones = new BitSet(control.getSkeleton().getBoneCount());
-        }
-        affectedBones.set(boneIndex);
-    }
-
-    /**
-     * Add bones to be influenced by this animation channel starting from the
-     * given bone name and going toward the root bone.
-     */
-    public void addToRootBone(String name) {
-        addToRootBone(control.getSkeleton().getBone(name));
-    }
-
-    /**
-     * Add bones to be influenced by this animation channel starting from the
-     * given bone and going toward the root bone.
-     */
-    public void addToRootBone(Bone bone) {
-        addBone(bone);
-        while (bone.getParent() != null) {
-            bone = bone.getParent();
-            addBone(bone);
-        }
-    }
-
-    /**
-     * Add bones to be influenced by this animation channel, starting
-     * from the given named bone and going toward its children.
-     */
-    public void addFromRootBone(String name) {
-        addFromRootBone(control.getSkeleton().getBone(name));
-    }
-
-    /**
-     * Add bones to be influenced by this animation channel, starting
-     * from the given bone and going toward its children.
-     */
-    public void addFromRootBone(Bone bone) {
-        addBone(bone);
-        if (bone.getChildren() == null)
-            return;
-        for (Bone childBone : bone.getChildren()) {
-            addBone(childBone);
-            addFromRootBone(childBone);
-        }
-    }
-
-    BitSet getAffectedBones(){
-        return affectedBones;
-    }
-    
-    public void reset(boolean rewind){
-        if(rewind){
-            setTime(0);        
-            if(control.getSkeleton()!=null){
-                control.getSkeleton().resetAndUpdate();
-            }else{
-                TempVars vars = TempVars.get();
-                update(0, vars);
-                vars.release();    
-            }
-        }
-        animation = null;
-        notified = false;
-    }
-
-    void update(float tpf, TempVars vars) {
-        if (animation == null)
-            return;
-
-        if (blendFrom != null && blendAmount != 1.0f){
-            // The blendFrom anim is set, the actual animation
-            // playing will be set 
-//            blendFrom.setTime(timeBlendFrom, 1f, control, this, vars);
-            blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars);
-            
-            timeBlendFrom += tpf * speedBlendFrom;
-            timeBlendFrom = AnimationUtils.clampWrapTime(timeBlendFrom,
-                                          blendFrom.getLength(),
-                                          loopModeBlendFrom);
-            if (timeBlendFrom < 0){
-                timeBlendFrom = -timeBlendFrom;
-                speedBlendFrom = -speedBlendFrom;
-            }
-
-            blendAmount += tpf * blendRate;
-            if (blendAmount > 1f){
-                blendAmount = 1f;
-                blendFrom = null;
-            }
-        }
-        
-        animation.setTime(time, blendAmount, control, this, vars);
-        time += tpf * speed;      
-        if (animation.getLength() > 0){
-            if (!notified && (time >= animation.getLength() || time < 0)) {
-                if (loopMode == LoopMode.DontLoop) {
-                    // Note that this flag has to be set before calling the notify
-                    // since the notify may start a new animation and then unset
-                    // the flag.
-                    notified = true;
-                }
-                control.notifyAnimCycleDone(this, animation.getName());
-            } 
-        }
-        time = AnimationUtils.clampWrapTime(time, animation.getLength(), loopMode);
-        if (time < 0){
-            // Negative time indicates that speed should be inverted
-            // (for cycle loop mode only)
-            time = -time;
-            speed = -speed;
-        }
-    }
-}
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.animation;
+
+import com.jme3.math.FastMath;
+import com.jme3.util.TempVars;
+import java.util.BitSet;
+
+/**
+ * <code>AnimChannel</code> provides controls, such as play, pause,
+ * fast forward, etc, for an animation. The animation
+ * channel may influence the entire model or specific bones of the model's
+ * skeleton. A single model may have multiple animation channels influencing
+ * various parts of its body. For example, a character model may have an
+ * animation channel for its feet, and another for its torso, and
+ * the animations for each channel are controlled independently.
+ * 
+ * @author Kirill Vainer
+ */
+public final class AnimChannel {
+
+    private static final float DEFAULT_BLEND_TIME = 0.15f;
+    
+    private AnimControl control;
+
+    private BitSet affectedBones;
+
+    private Animation animation;
+    private Animation blendFrom;
+    private float time;
+    private float speed;
+    private float timeBlendFrom;
+    private float blendTime;
+    private float speedBlendFrom;
+    private boolean notified=false;
+
+    private LoopMode loopMode, loopModeBlendFrom;
+    
+    private float blendAmount = 1f;
+    private float blendRate   = 0;
+    
+    public AnimChannel(){
+        
+    }
+    
+    public AnimChannel(AnimControl control){
+        this.control = control;
+    }
+
+    /**
+     * Returns the parent control of this AnimChannel.
+     * 
+     * @return the parent control of this AnimChannel.
+     * @see AnimControl
+     */
+    public AnimControl getControl() {
+        return control;
+    }
+    
+    /**
+     * @return The name of the currently playing animation, or null if
+     * none is assigned.
+     *
+     * @see AnimChannel#setAnim(java.lang.String) 
+     */
+    public String getAnimationName() {
+        return animation != null ? animation.getName() : null;
+    }
+
+    /**
+     * @return The loop mode currently set for the animation. The loop mode
+     * determines what will happen to the animation once it finishes
+     * playing.
+     * 
+     * For more information, see the LoopMode enum class.
+     * @see LoopMode
+     * @see AnimChannel#setLoopMode(com.jme3.animation.LoopMode)
+     */
+    public LoopMode getLoopMode() {
+        return loopMode;
+    }
+
+    /**
+     * @param loopMode Set the loop mode for the channel. The loop mode
+     * determines what will happen to the animation once it finishes
+     * playing.
+     *
+     * For more information, see the LoopMode enum class.
+     * @see LoopMode
+     */
+    public void setLoopMode(LoopMode loopMode) {
+        this.loopMode = loopMode;
+    }
+
+    /**
+     * @return The speed that is assigned to the animation channel. The speed
+     * is a scale value starting from 0.0, at 1.0 the animation will play
+     * at its default speed.
+     *
+     * @see AnimChannel#setSpeed(float)
+     */
+    public float getSpeed() {
+        return speed;
+    }
+
+    /**
+     * @param speed Set the speed of the animation channel. The speed
+     * is a scale value starting from 0.0, at 1.0 the animation will play
+     * at its default speed.
+     */
+    public void setSpeed(float speed) {
+        this.speed = speed;
+        if(blendTime>0){
+            this.speedBlendFrom = speed;
+            blendTime = Math.min(blendTime, animation.getLength() / speed);  
+            blendRate = 1/ blendTime;
+        }
+    }
+
+    /**
+     * @return The time of the currently playing animation. The time
+     * starts at 0 and continues on until getAnimMaxTime().
+     *
+     * @see AnimChannel#setTime(float)
+     */
+    public float getTime() {
+        return time;
+    }
+
+    /**
+     * @param time Set the time of the currently playing animation, the time
+     * is clamped from 0 to {@link #getAnimMaxTime()}. 
+     */
+    public void setTime(float time) {
+        this.time = FastMath.clamp(time, 0, getAnimMaxTime());
+    }
+
+    /**
+     * @return The length of the currently playing animation, or zero
+     * if no animation is playing.
+     *
+     * @see AnimChannel#getTime()
+     */
+    public float getAnimMaxTime(){
+        return animation != null ? animation.getLength() : 0f;
+    }
+
+    /**
+     * Set the current animation that is played by this AnimChannel.
+     * <p>
+     * This resets the time to zero, and optionally blends the animation
+     * over <code>blendTime</code> seconds with the currently playing animation.
+     * Notice that this method will reset the control's speed to 1.0.
+     *
+     * @param name The name of the animation to play
+     * @param blendTime The blend time over which to blend the new animation
+     * with the old one. If zero, then no blending will occur and the new
+     * animation will be applied instantly.
+     */
+    public void setAnim(String name, float blendTime){
+        if (name == null)
+            throw new IllegalArgumentException("name cannot be null");
+
+        if (blendTime < 0f)
+            throw new IllegalArgumentException("blendTime cannot be less than zero");
+
+        Animation anim = control.animationMap.get(name);
+        if (anim == null)
+            throw new IllegalArgumentException("Cannot find animation named: '"+name+"'");
+
+        control.notifyAnimChange(this, name);
+
+        if (animation != null && blendTime > 0f){
+            this.blendTime = blendTime;
+            // activate blending
+            blendTime = Math.min(blendTime, anim.getLength() / speed);            
+            blendFrom = animation;
+            timeBlendFrom = time;
+            speedBlendFrom = speed;
+            loopModeBlendFrom = loopMode;
+            blendAmount = 0f;
+            blendRate   = 1f / blendTime;
+        }else{
+            blendFrom = null;
+        }
+
+        animation = anim;
+        time = 0;
+        speed = 1f;
+        loopMode = LoopMode.Loop;
+        notified = false;
+    }
+
+    /**
+     * Set the current animation that is played by this AnimChannel.
+     * <p>
+     * See {@link #setAnim(java.lang.String, float)}.
+     * The blendTime argument by default is 150 milliseconds.
+     * 
+     * @param name The name of the animation to play
+     */
+    public void setAnim(String name){
+        setAnim(name, DEFAULT_BLEND_TIME);
+    }
+
+    /**
+     * Add all the bones of the model's skeleton to be
+     * influenced by this animation channel.
+     */
+    public void addAllBones() {
+        affectedBones = null;
+    }
+
+    /**
+     * Add a single bone to be influenced by this animation channel.
+     */
+    public void addBone(String name) {
+        addBone(control.getSkeleton().getBone(name));
+    }
+
+    /**
+     * Add a single bone to be influenced by this animation channel.
+     */
+    public void addBone(Bone bone) {
+        int boneIndex = control.getSkeleton().getBoneIndex(bone);
+        if(affectedBones == null) {
+            affectedBones = new BitSet(control.getSkeleton().getBoneCount());
+        }
+        affectedBones.set(boneIndex);
+    }
+
+    /**
+     * Add bones to be influenced by this animation channel starting from the
+     * given bone name and going toward the root bone.
+     */
+    public void addToRootBone(String name) {
+        addToRootBone(control.getSkeleton().getBone(name));
+    }
+
+    /**
+     * Add bones to be influenced by this animation channel starting from the
+     * given bone and going toward the root bone.
+     */
+    public void addToRootBone(Bone bone) {
+        addBone(bone);
+        while (bone.getParent() != null) {
+            bone = bone.getParent();
+            addBone(bone);
+        }
+    }
+
+    /**
+     * Add bones to be influenced by this animation channel, starting
+     * from the given named bone and going toward its children.
+     */
+    public void addFromRootBone(String name) {
+        addFromRootBone(control.getSkeleton().getBone(name));
+    }
+
+    /**
+     * Add bones to be influenced by this animation channel, starting
+     * from the given bone and going toward its children.
+     */
+    public void addFromRootBone(Bone bone) {
+        addBone(bone);
+        if (bone.getChildren() == null)
+            return;
+        for (Bone childBone : bone.getChildren()) {
+            addBone(childBone);
+            addFromRootBone(childBone);
+        }
+    }
+
+    BitSet getAffectedBones(){
+        return affectedBones;
+    }
+    
+    public void reset(boolean rewind){
+        if(rewind){
+            setTime(0);        
+            if(control.getSkeleton()!=null){
+                control.getSkeleton().resetAndUpdate();
+            }else{
+                TempVars vars = TempVars.get();
+                update(0, vars);
+                vars.release();    
+            }
+        }
+        animation = null;
+        notified = false;
+    }
+
+    void update(float tpf, TempVars vars) {
+        if (animation == null)
+            return;
+
+        if (blendFrom != null && blendAmount != 1.0f){
+            // The blendFrom anim is set, the actual animation
+            // playing will be set 
+//            blendFrom.setTime(timeBlendFrom, 1f, control, this, vars);
+            blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars);
+            
+            timeBlendFrom += tpf * speedBlendFrom;
+            timeBlendFrom = AnimationUtils.clampWrapTime(timeBlendFrom,
+                                          blendFrom.getLength(),
+                                          loopModeBlendFrom);
+            if (timeBlendFrom < 0){
+                timeBlendFrom = -timeBlendFrom;
+                speedBlendFrom = -speedBlendFrom;
+            }
+
+            blendAmount += tpf * blendRate;
+            if (blendAmount > 1f){
+                blendAmount = 1f;
+                blendFrom = null;
+            }
+        }
+        
+        animation.setTime(time, blendAmount, control, this, vars);
+        time += tpf * speed;      
+        if (animation.getLength() > 0){
+            if (!notified && (time >= animation.getLength() || time < 0)) {
+                if (loopMode == LoopMode.DontLoop) {
+                    // Note that this flag has to be set before calling the notify
+                    // since the notify may start a new animation and then unset
+                    // the flag.
+                    notified = true;
+                }
+                control.notifyAnimCycleDone(this, animation.getName());
+            } 
+        }
+        time = AnimationUtils.clampWrapTime(time, animation.getLength(), loopMode);
+        if (time < 0){
+            // Negative time indicates that speed should be inverted
+            // (for cycle loop mode only)
+            time = -time;
+            speed = -speed;
+        }
+    }
+}

+ 2 - 31
jme3-core/src/main/java/com/jme3/animation/AnimControl.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,19 +34,16 @@ package com.jme3.animation;
 import com.jme3.export.*;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
-import com.jme3.scene.Mesh;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.control.AbstractControl;
-import com.jme3.scene.control.Control;
+import com.jme3.util.TempVars;
 import com.jme3.util.clone.Cloner;
 import com.jme3.util.clone.JmeCloneable;
-import com.jme3.util.TempVars;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Map.Entry;
 
 /**
  * <code>AnimControl</code> is a Spatial control that allows manipulation
@@ -108,32 +105,6 @@ public final class AnimControl extends AbstractControl implements Cloneable, Jme
     public AnimControl() {
     }
 
-    /**
-     * Internal use only.
-     */
-    @Override
-    public Control cloneForSpatial(Spatial spatial) {
-        try {
-            AnimControl clone = (AnimControl) super.clone();
-            clone.spatial = spatial;
-            clone.channels = new ArrayList<AnimChannel>();
-            clone.listeners = new ArrayList<AnimEventListener>();
-
-            if (skeleton != null) {
-                clone.skeleton = new Skeleton(skeleton);
-            }
-
-            // animationMap is cloned, but only ClonableTracks will be cloned as they need a reference to a cloned spatial
-            for (Entry<String, Animation> animEntry : animationMap.entrySet()) {
-                clone.animationMap.put(animEntry.getKey(), animEntry.getValue().cloneForSpatial(spatial));
-            }
-            
-            return clone;
-        } catch (CloneNotSupportedException ex) {
-            throw new AssertionError();
-        }
-    }
-
     @Override   
     public Object jmeClone() {
         AnimControl clone = (AnimControl) super.jmeClone();

+ 4 - 6
jme3-core/src/main/java/com/jme3/animation/AnimationUtils.java

@@ -31,17 +31,15 @@
  */
 package com.jme3.animation;
 
-import static com.jme3.animation.LoopMode.Cycle;
-import static com.jme3.animation.LoopMode.DontLoop;
-import static com.jme3.animation.LoopMode.Loop;
-
 /**
  *
  * @author Nehon
  */
 public class AnimationUtils {
     
-    
+    public AnimationUtils(){
+        
+    }
     /**
      * Clamps the time according to duration and loopMode
      * @param time
@@ -50,7 +48,7 @@ public class AnimationUtils {
      * @return 
      */
      public static float clampWrapTime(float time, float duration, LoopMode loopMode){
-        if (time == 0) {
+         if (time == 0 || duration == 0) {
             return 0; // prevent division by 0 errors
         }        
         switch (loopMode) {

+ 11 - 9
jme3-core/src/main/java/com/jme3/animation/Bone.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2017 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,15 +32,15 @@
 package com.jme3.animation;
 
 import com.jme3.export.*;
+import com.jme3.material.MatParamOverride;
 import com.jme3.math.*;
-import com.jme3.scene.Geometry;
-import com.jme3.scene.Mesh;
-import com.jme3.scene.Node;
-import com.jme3.scene.Spatial;
+import com.jme3.scene.*;
+import com.jme3.shader.VarType;
 import com.jme3.util.SafeArrayList;
 import com.jme3.util.TempVars;
-import com.jme3.util.clone.JmeCloneable;
 import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
+
 import java.io.IOException;
 import java.util.ArrayList;
 
@@ -550,7 +550,7 @@ public final class Bone implements Savable, JmeCloneable {
     }
 
     /**
-     * Updates world transforms for this bone and it's children.
+     * Updates world transforms for this bone and its children.
      */
     public final void update() {
         this.updateModelTransforms();
@@ -590,7 +590,7 @@ public final class Bone implements Savable, JmeCloneable {
     }
 
     /**
-     * Reset the bone and it's children to bind pose.
+     * Reset the bone and its children to bind pose.
      */
     final void reset() {
         if (!userControl) {
@@ -677,7 +677,7 @@ public final class Bone implements Savable, JmeCloneable {
         modelPos.set(translation);
         modelRot.set(rotation);
         
-        //if there is an attached Node we need to set it's local transforms too.
+        //if there is an attached Node we need to set its local transforms too.
         if(attachNode != null){
             attachNode.setLocalTranslation(translation);
             attachNode.setLocalRotation(rotation);
@@ -723,6 +723,8 @@ public final class Bone implements Savable, JmeCloneable {
         if (attachNode == null) {
             attachNode = new Node(name + "_attachnode");
             attachNode.setUserData("AttachedBone", this);
+            //We don't want the node to have a numBone set by a parent node so we force it to null
+            attachNode.addMatParamOverride(new MatParamOverride(VarType.Int, "NumberOfBones", null));
         }
 
         return attachNode;

+ 60 - 32
jme3-core/src/main/java/com/jme3/animation/BoneTrack.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -35,6 +35,8 @@ import com.jme3.export.*;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector3f;
 import com.jme3.util.TempVars;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 import java.util.BitSet;
 
@@ -43,10 +45,10 @@ import java.util.BitSet;
  * 
  * @author Kirill Vainer
  */
-public final class BoneTrack implements Track {
+public final class BoneTrack implements JmeCloneable, Track {
 
     /**
-     * Bone index in the skeleton which this track effects.
+     * Bone index in the skeleton which this track affects.
      */
     private int targetBoneIndex;
     
@@ -138,16 +140,23 @@ public final class BoneTrack implements Track {
 
     /**
      * Set the translations and rotations for this bone track
-     * @param times a float array with the time of each frame
-     * @param translations the translation of the bone for each frame
-     * @param rotations the rotation of the bone for each frame
+     *
+     * @param times the time of each frame, measured from the start of the track
+     * (not null, length&gt;0)
+     * @param translations the translation of the bone for each frame (not null,
+     * same length as times)
+     * @param rotations the rotation of the bone for each frame (not null, same
+     * length as times)
      */
     public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations) {
         if (times.length == 0) {
             throw new RuntimeException("BoneTrack with no keyframes!");
         }
 
-        assert times.length == translations.length && times.length == rotations.length;
+        assert translations != null;
+        assert times.length == translations.length;
+        assert rotations != null;
+        assert times.length == rotations.length;
 
         this.times = times;
         this.translations = new CompactVector3Array();
@@ -160,15 +169,19 @@ public final class BoneTrack implements Track {
 
     /**
      * Set the translations, rotations and scales for this bone track
-     * @param times a float array with the time of each frame
-     * @param translations the translation of the bone for each frame
-     * @param rotations the rotation of the bone for each frame
-     * @param scales the scale of the bone for each frame
+     *
+     * @param times the time of each frame, measured from the start of the track
+     * (not null, length&gt;0)
+     * @param translations the translation of the bone for each frame (not null,
+     * same length as times)
+     * @param rotations the rotation of the bone for each frame (not null, same
+     * length as times)
+     * @param scales the scale of the bone for each frame (ignored if null)
      */
     public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) {
         this.setKeyframes(times, translations, rotations);
-        assert times.length == scales.length;
         if (scales != null) {
+            assert times.length == scales.length;
             this.scales = new CompactVector3Array();
             this.scales.add(scales);
             this.scales.freeze();
@@ -261,33 +274,48 @@ public final class BoneTrack implements Track {
     public float[] getKeyFrameTimes() {
         return times;
     }
-    
+
     /**
-     * This method creates a clone of the current object.
-     * @return a clone of the current object
+     * Create a deep clone of this track.
+     *
+     * @return a new track
      */
     @Override
     public BoneTrack clone() {
-        int tablesLength = times.length;
-
-        float[] times = this.times.clone();
-        Vector3f[] sourceTranslations = this.getTranslations();
-        Quaternion[] sourceRotations = this.getRotations();
-        Vector3f[] sourceScales = this.getScales();
+        return Cloner.deepClone(this);
+    }
 
-        Vector3f[] translations = new Vector3f[tablesLength];
-        Quaternion[] rotations = new Quaternion[tablesLength];
-        Vector3f[] scales = new Vector3f[tablesLength];
-        for (int i = 0; i < tablesLength; ++i) {
-            translations[i] = sourceTranslations[i].clone();
-            rotations[i] = sourceRotations[i].clone();
-            scales[i] = sourceScales != null ? sourceScales[i].clone() : new Vector3f(1.0f, 1.0f, 1.0f);
+    /**
+     * Create a shallow clone for the JME cloner.
+     *
+     * @return a new track
+     */
+    @Override
+    public BoneTrack jmeClone() {
+        try {
+            return (BoneTrack) super.clone();
+        } catch (CloneNotSupportedException exception) {
+            throw new RuntimeException("Can't clone track", exception);
         }
-        
-        // Need to use the constructor here because of the final fields used in this class
-        return new BoneTrack(targetBoneIndex, times, translations, rotations, scales);
     }
-    
+
+    /**
+     * Callback from {@link com.jme3.util.clone.Cloner} to convert this
+     * shallow-cloned track into a deep-cloned one, using the specified cloner
+     * to resolve copied fields.
+     *
+     * @param cloner the cloner currently cloning this control (not null)
+     * @param original the track from which this track was shallow-cloned
+     * (unused)
+     */
+    @Override
+    public void cloneFields(Cloner cloner, Object original) {
+        translations = cloner.clone(translations);
+        rotations = cloner.clone(rotations);
+        scales = cloner.clone(scales);
+        times = cloner.clone(times);
+    }
+
     @Override
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);

+ 49 - 3
jme3-core/src/main/java/com/jme3/animation/CompactArray.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,6 +31,8 @@
  */
 package com.jme3.animation;
 
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.lang.reflect.Array;
 import java.util.HashMap;
 import java.util.Map;
@@ -40,7 +42,7 @@ import java.util.Map;
  * @author Lim, YongHoon
  * @param <T>
  */
-public abstract class CompactArray<T> {
+public abstract class CompactArray<T> implements JmeCloneable {
 
     private Map<T, Integer> indexPool = new HashMap<T, Integer>();
     protected int[] index;
@@ -68,6 +70,7 @@ public abstract class CompactArray<T> {
      * They are serialized automatically when get() method is called.
      * @param objArray
      */
+    @SuppressWarnings("unchecked")
     public void add(T... objArray) {
         if (objArray == null || objArray.length == 0) {
             return;
@@ -186,10 +189,11 @@ public abstract class CompactArray<T> {
     }
 
     /**
-     * retrun an array of indices for the given objects
+     * Return an array of indices for the given objects
      * @param objArray
      * @return 
      */
+    @SuppressWarnings("unchecked")
     public final int[] getIndex(T... objArray) {
         int[] index = new int[objArray.length];
         for (int i = 0; i < index.length; i++) {
@@ -228,6 +232,7 @@ public abstract class CompactArray<T> {
      * decompress and return object array
      * @return decompress and return object array
      */
+    @SuppressWarnings("unchecked")
     public final T[] toObjectArray() {
         try {
             T[] compactArr = (T[]) Array.newInstance(getElementClass(), getSerializedSize() / getTupleSize());
@@ -247,6 +252,47 @@ public abstract class CompactArray<T> {
         }
     }
 
+    /**
+     * Create a deep clone of this array.
+     *
+     * @return a new array
+     * @throws java.lang.CloneNotSupportedException
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        return Cloner.deepClone(this);
+    }
+
+    /**
+     * Create a shallow clone for the JME cloner.
+     *
+     * @return a new array
+     */
+    @Override
+    public Object jmeClone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException exception) {
+            throw new RuntimeException("Can't clone array", exception);
+        }
+    }
+
+    /**
+     * Callback from {@link com.jme3.util.clone.Cloner} to convert this
+     * shallow-cloned array into a deep-cloned one, using the specified cloner
+     * to resolve copied fields.
+     *
+     * @param cloner the cloner currently cloning this control (not null)
+     * @param original the array from which this array was shallow-cloned
+     * (unused)
+     */
+    @Override
+    public void cloneFields(Cloner cloner, Object original) {
+        indexPool = cloner.clone(indexPool);
+        index = cloner.clone(index);
+        array = cloner.clone(array);
+    }
+
     /**
      * serialize object
      * @param compactIndex compacted object index

+ 9 - 24
jme3-core/src/main/java/com/jme3/animation/EffectTrack.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -42,18 +42,16 @@ import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.Spatial.CullHint;
 import com.jme3.scene.control.AbstractControl;
-import com.jme3.scene.control.Control;
 import com.jme3.util.TempVars;
 import com.jme3.util.clone.Cloner;
-import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 /**
  * EffectTrack is a track to add to an existing animation, to emit particles
- * during animations for example : exhausts, dust raised by foot steps, shock
- * waves, lightnings etc...
+ * during animations for example: exhaust, dust raised by footsteps, shock
+ * waves, lightning, etc...
  *
  * usage is
  * <pre>
@@ -62,9 +60,9 @@ import java.util.logging.Logger;
  * control.getAnim("TheAnim").addTrack(track);
  * </pre>
  *
- * if the emitter has emits 0 particles per seconds emmitAllPArticles will be
- * called on it at time 0 + startOffset. if it he it has more it will start
- * emit normally at time 0 + startOffset.
+ * if the emitter emits 0 particles per second, emitAllPArticles will be
+ * called on it at time 0 + startOffset. if it has more it will start
+ * emitting normally at time 0 + startOffset.
  *
  *
  * @author Nehon
@@ -132,19 +130,6 @@ public class EffectTrack implements ClonableTrack {
         @Override
         protected void controlRender(RenderManager rm, ViewPort vp) {
         }
-
-        @Override
-        public Control cloneForSpatial(Spatial spatial) {
-
-            KillParticleControl c = new KillParticleControl();
-            //this control should be removed as it shouldn't have been persisted in the first place
-            //In the quest to find the less hackish solution to achieve this,
-            //making it remove itself from the spatial in the first update loop when loaded was the less bad.
-            c.remove = true;
-            c.setSpatial(spatial);
-            return c;
-
-        }
     };
 
     //Anim listener that stops the Emmitter when the animation is finished or changed.
@@ -213,7 +198,7 @@ public class EffectTrack implements ClonableTrack {
             control.addListener(new OnEndListener());
             initialized = true;
         }
-        //checking fo time to trigger the effect
+        //checking for time to trigger the effect
         if (!emitted && time >= startOffset) {
             emitted = true;
             emitter.setCullHint(CullHint.Dynamic);
@@ -430,7 +415,7 @@ public class EffectTrack implements ClonableTrack {
      */
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule out = ex.getCapsule(this);
-        //reseting the particle emission rate on the emitter before saving.
+        //reset the particle emission rate on the emitter before saving.
         emitter.setParticlesPerSec(particlesPerSeconds);
         out.write(emitter, "emitter", null);
         out.write(particlesPerSeconds, "particlesPerSeconds", 0);
@@ -449,7 +434,7 @@ public class EffectTrack implements ClonableTrack {
     public void read(JmeImporter im) throws IOException {
         InputCapsule in = im.getCapsule(this);
         this.particlesPerSeconds = in.readFloat("particlesPerSeconds", 0);
-        //reading the emitter even if the track will then reference its cloned counter part if it's loaded with the assetManager.
+        //reading the emitter even if the track will then reference its cloned counterpart if it's loaded with the assetManager.
         //This also avoid null pointer exception if the model is not loaded via the AssetManager.
         emitter = (ParticleEmitter) in.readSavable("emitter", null);
         emitter.setParticlesPerSec(0);

+ 4 - 3
jme3-core/src/main/java/com/jme3/animation/Skeleton.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -53,7 +53,7 @@ public final class Skeleton implements Savable, JmeCloneable {
     private Bone[] boneList;
     
     /**
-     * Contains the skinning matrices, multiplying it by a vertex effected by a bone
+     * Contains the skinning matrices, multiplying it by a vertex affected by a bone
      * will cause it to go to the animated position.
      */
     private transient Matrix4f[] skinningMatrixes;
@@ -169,7 +169,7 @@ public final class Skeleton implements Savable, JmeCloneable {
     }
 
     /**
-     * Saves the current skeleton state as it's binding pose.
+     * Saves the current skeleton state as its binding pose.
      */
     public void setBindingPose() {
         for (int i = rootBones.length - 1; i >= 0; i--) {
@@ -304,6 +304,7 @@ public final class Skeleton implements Savable, JmeCloneable {
         createSkinningMatrices();
 
         for (Bone rootBone : rootBones) {
+            rootBone.reset();
             rootBone.update();
             rootBone.setBindingPose();
         }

+ 12 - 50
jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2017 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -35,21 +35,18 @@ import com.jme3.export.*;
 import com.jme3.material.MatParamOverride;
 import com.jme3.math.FastMath;
 import com.jme3.math.Matrix4f;
-import com.jme3.renderer.RenderManager;
-import com.jme3.renderer.RendererException;
-import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.*;
 import com.jme3.scene.*;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.control.AbstractControl;
-import com.jme3.scene.control.Control;
 import com.jme3.scene.mesh.IndexBuffer;
 import com.jme3.shader.VarType;
-import com.jme3.util.*;
+import com.jme3.util.SafeArrayList;
+import com.jme3.util.TempVars;
 import com.jme3.util.clone.Cloner;
 import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 import java.nio.Buffer;
-import java.nio.ByteBuffer;
 import java.nio.FloatBuffer;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -145,6 +142,12 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
     }
 
     private boolean testHardwareSupported(RenderManager rm) {
+
+        //Only 255 bones max supported with hardware skinning
+        if (skeleton.getBoneCount() > 255) {
+            return false;
+        }
+
         switchToHardware();
         
         try {
@@ -158,7 +161,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
     /**
      * Specifies if hardware skinning is preferred. If it is preferred and
-     * supported by GPU, it shall be enabled, if its not preferred, or not
+     * supported by GPU, it shall be enabled, if it's not preferred, or not
      * supported by GPU, then it shall be disabled.
      * 
      * @param preferred
@@ -326,7 +329,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
                 bpb.clear();
                 bnb.clear();
 
-                //reseting bind tangents if there is a bind tangent buffer
+                //reset bind tangents if there is a bind tangent buffer
                 VertexBuffer bindTangents = mesh.getBuffer(Type.BindPoseTangent);
                 if (bindTangents != null) {
                     VertexBuffer tangents = mesh.getBuffer(Type.Tangent);
@@ -344,47 +347,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         }
     }
 
-    @Override
-    public Control cloneForSpatial(Spatial spatial) {
-        Node clonedNode = (Node) spatial;
-        SkeletonControl clone = new SkeletonControl();
-
-        AnimControl ctrl = spatial.getControl(AnimControl.class);
-        if (ctrl != null) {
-            // AnimControl is responsible for cloning the skeleton, not
-            // SkeletonControl.
-            clone.skeleton = ctrl.getSkeleton();
-        } else {
-            // If there's no AnimControl, create the clone ourselves.
-            clone.skeleton = new Skeleton(skeleton);
-        }
-        clone.hwSkinningDesired = this.hwSkinningDesired;
-        clone.hwSkinningEnabled = this.hwSkinningEnabled;
-        clone.hwSkinningSupported = this.hwSkinningSupported;
-        clone.hwSkinningTested = this.hwSkinningTested;
-        
-        clone.setSpatial(clonedNode);
-
-        // Fix attachments for the cloned node
-        for (int i = 0; i < clonedNode.getQuantity(); i++) {
-            // go through attachment nodes, apply them to correct bone
-            Spatial child = clonedNode.getChild(i);
-            if (child instanceof Node) {
-                Node clonedAttachNode = (Node) child;
-                Bone originalBone = (Bone) clonedAttachNode.getUserData("AttachedBone");
-
-                if (originalBone != null) {
-                    Bone clonedBone = clone.skeleton.getBone(originalBone.getName());
-
-                    clonedAttachNode.setUserData("AttachedBone", clonedBone);
-                    clonedBone.setAttachmentsNode(clonedAttachNode);
-                }
-            }
-        }
-
-        return clone;
-    }
-
     @Override   
     public Object jmeClone() {
         return super.jmeClone();

+ 37 - 18
jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -41,16 +41,14 @@ import com.jme3.scene.Spatial;
 import com.jme3.util.TempVars;
 import com.jme3.util.clone.Cloner;
 import com.jme3.util.clone.JmeCloneable;
-
 import java.io.IOException;
-import java.util.Arrays;
 
 /**
  * This class represents the track for spatial animation.
  * 
  * @author Marcin Roguski (Kaelthas)
  */
-public class SpatialTrack implements Track, JmeCloneable {
+public class SpatialTrack implements JmeCloneable, Track {
     
     /** 
      * Translations of the track. 
@@ -250,9 +248,16 @@ public class SpatialTrack implements Track, JmeCloneable {
             return times == null ? 0 : times[times.length - 1] - times[0];
     }
 
+    /**
+     * Create a clone with the same track spatial.
+     *
+     * @return a new track
+     */
     @Override
-    public Track clone() {
-        return (Track) jmeClone();
+    public SpatialTrack clone() {
+        Cloner cloner = new Cloner();
+        cloner.setClonedValue(trackSpatial, trackSpatial);
+        return cloner.clone(this);
     }
 
     @Override
@@ -268,24 +273,38 @@ public class SpatialTrack implements Track, JmeCloneable {
         return trackSpatial;
     }
 
+    /**
+     * Create a shallow clone for the JME cloner.
+     *
+     * @return a new track
+     */
     @Override
-    public Object jmeClone() {
-        int tablesLength = times.length;
-
-        float[] timesCopy = this.times.clone();
-        Vector3f[] translationsCopy = this.getTranslations() == null ? null : Arrays.copyOf(this.getTranslations(), tablesLength);
-        Quaternion[] rotationsCopy = this.getRotations() == null ? null : Arrays.copyOf(this.getRotations(), tablesLength);
-        Vector3f[] scalesCopy = this.getScales() == null ? null : Arrays.copyOf(this.getScales(), tablesLength);
-
-        //need to use the constructor here because of the final fields used in this class
-        return new SpatialTrack(timesCopy, translationsCopy, rotationsCopy, scalesCopy);
+    public SpatialTrack jmeClone() {
+        try {
+            return (SpatialTrack) super.clone();
+        } catch (CloneNotSupportedException exception) {
+            throw new RuntimeException("Can't clone track", exception);
+        }
     }
 
+    /**
+     * Callback from {@link com.jme3.util.clone.Cloner} to convert this
+     * shallow-cloned track into a deep-cloned one, using the specified cloner
+     * to resolve copied fields.
+     *
+     * @param cloner the cloner currently cloning this control (not null)
+     * @param original the track from which this track was shallow-cloned
+     * (unused)
+     */
     @Override
     public void cloneFields(Cloner cloner, Object original) {
-        this.trackSpatial = cloner.clone(((SpatialTrack) original).trackSpatial);
+        translations = cloner.clone(translations);
+        rotations = cloner.clone(rotations);
+        scales = cloner.clone(scales);
+        trackSpatial = cloner.clone(trackSpatial);
+        times = cloner.clone(times);
     }
-	
+
     @Override
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);

+ 4 - 4
jme3-core/src/main/java/com/jme3/animation/Track.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -44,9 +44,9 @@ public interface Track extends Savable, Cloneable {
      * given parameters.
      * 
      * @param time The time in the animation
-     * @param weight The weight from 0 to 1 on how much to apply the track 
-     * @param control The control which the track should effect
-     * @param channel The channel which the track should effect
+     * @param weight The weight from 0 to 1 on how much to apply the track
+     * @param control The control which the track should affect
+     * @param channel The channel which the track should affect
      */
     public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars);
 

+ 19 - 13
jme3-core/src/main/java/com/jme3/app/DetailedProfilerState.java

@@ -59,6 +59,8 @@ public class DetailedProfilerState extends BaseAppState {
     private ColorRGBA dimmedOrange = ColorRGBA.Orange.mult(0.7f);
     private ColorRGBA dimmedRed = ColorRGBA.Red.mult(0.7f);
 
+    private ProfilerInputListener inputListener = new ProfilerInputListener();
+
     public DetailedProfilerState() {
 
     }
@@ -119,23 +121,17 @@ public class DetailedProfilerState extends BaseAppState {
         if (inputManager != null) {
             inputManager.addMapping(TOGGLE_KEY, new KeyTrigger(KeyInput.KEY_F6));
             inputManager.addMapping(CLICK_KEY, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
-            inputManager.addListener(new ActionListener() {
-                @Override
-                public void onAction(String name, boolean isPressed, float tpf) {
-                    if (name.equals(TOGGLE_KEY) && isPressed) {
-                        setEnabled(!isEnabled());
-                    }
-                    if (isEnabled() && name.equals(CLICK_KEY) && isPressed) {
-                        handleClick(inputManager.getCursorPosition());
-                    }
-                }
-            }, TOGGLE_KEY, CLICK_KEY);
+            inputManager.addListener(inputListener, TOGGLE_KEY, CLICK_KEY);
         }
     }
 
     @Override
     protected void cleanup(Application app) {
-
+        ui.detachAllChildren();
+        InputManager manager = getApplication().getInputManager();
+        manager.deleteMapping(TOGGLE_KEY);
+        manager.deleteMapping(CLICK_KEY);
+        manager.removeListener(inputListener);
     }
 
     @Override
@@ -441,8 +437,18 @@ public class DetailedProfilerState extends BaseAppState {
         public String toString() {
             return label.getText() + " - " + df.format(getMsFromNs(cpuValue)) + "ms / " + df.format(getMsFromNs(gpuValue)) + "ms";
         }
+    }
 
-
+    private class ProfilerInputListener implements ActionListener {
+        @Override
+        public void onAction(String name, boolean isPressed, float tpf) {
+            if (name.equals(TOGGLE_KEY) && isPressed) {
+                setEnabled(!isEnabled());
+            }
+            if (isEnabled() && name.equals(CLICK_KEY) && isPressed) {
+                handleClick(getApplication().getInputManager().getCursorPosition());
+            }
+        }
     }
 }
 

+ 4 - 3
jme3-core/src/main/java/com/jme3/app/StatsView.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -117,11 +117,12 @@ public class StatsView extends Node implements Control, JmeCloneable {
         //statistics.clearFrame();
     }
 
+    @Deprecated
     @Override
     public Control cloneForSpatial(Spatial spatial) {
-        return (Control) spatial;
+        throw new UnsupportedOperationException();
     }
-
+    
     @Override
     public StatsView jmeClone() {
         throw new UnsupportedOperationException("Not yet implemented.");

+ 18 - 18
jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -80,10 +80,10 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
 
     /**
      * This constructor allows you to specify the output file path of the screenshot.
-     * Include the seperator at the end of the path.
-     * Use an emptry string to use the application folder. Use NULL to use the system
+     * Include the separator at the end of the path.
+     * Use an empty string to use the application folder. Use NULL to use the system
      * default storage folder.
-     * @param filePath The screenshot file path to use. Include the seperator at the end of the path.
+     * @param filePath The screenshot file path to use. Include the separator at the end of the path.
      */
     public ScreenshotAppState(String filePath) {
         this.filePath = filePath;
@@ -91,11 +91,11 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
 
     /**
      * This constructor allows you to specify the output file path of the screenshot.
-     * Include the seperator at the end of the path.
-     * Use an emptry string to use the application folder. Use NULL to use the system
+     * Include the separator at the end of the path.
+     * Use an empty string to use the application folder. Use NULL to use the system
      * default storage folder.
-     * @param filePath The screenshot file path to use. Include the seperator at the end of the path.
-     * @param fileName The name of the file to save the screeshot as.
+     * @param filePath The screenshot file path to use. Include the separator at the end of the path.
+     * @param fileName The name of the file to save the screenshot as.
      */
     public ScreenshotAppState(String filePath, String fileName) {
         this.filePath = filePath;
@@ -105,10 +105,10 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
     /**
      * This constructor allows you to specify the output file path of the screenshot and
      * a base index for the shot index.
-     * Include the seperator at the end of the path.
-     * Use an emptry string to use the application folder. Use NULL to use the system
+     * Include the separator at the end of the path.
+     * Use an empty string to use the application folder. Use NULL to use the system
      * default storage folder.
-     * @param filePath The screenshot file path to use. Include the seperator at the end of the path.
+     * @param filePath The screenshot file path to use. Include the separator at the end of the path.
      * @param shotIndex The base index for screen shots.  The first screen shot will have
      *                  shotIndex + 1 appended, the next shotIndex + 2, and so on.
      */
@@ -120,11 +120,11 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
     /**
      * This constructor allows you to specify the output file path of the screenshot and
      * a base index for the shot index.
-     * Include the seperator at the end of the path.
-     * Use an emptry string to use the application folder. Use NULL to use the system
+     * Include the separator at the end of the path.
+     * Use an empty string to use the application folder. Use NULL to use the system
      * default storage folder.
-     * @param filePath The screenshot file path to use. Include the seperator at the end of the path.
-     * @param fileName The name of the file to save the screeshot as.
+     * @param filePath The screenshot file path to use. Include the separator at the end of the path.
+     * @param fileName The name of the file to save the screenshot as.
      * @param shotIndex The base index for screen shots.  The first screen shot will have
      *                  shotIndex + 1 appended, the next shotIndex + 2, and so on.
      */
@@ -136,10 +136,10 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen
     
     /**
      * Set the file path to store the screenshot.
-     * Include the seperator at the end of the path.
-     * Use an emptry string to use the application folder. Use NULL to use the system
+     * Include the separator at the end of the path.
+     * Use an empty string to use the application folder. Use NULL to use the system
      * default storage folder.
-     * @param filePath File path to use to store the screenshot. Include the seperator at the end of the path.
+     * @param filePath File path to use to store the screenshot. Include the separator at the end of the path.
      */
     public void setFilePath(String filePath) {
         this.filePath = filePath;

+ 2 - 2
jme3-core/src/main/java/com/jme3/asset/AssetManager.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -217,7 +217,7 @@ public interface AssetManager {
      * Load an asset from a key, the asset will be located
      * by one of the {@link AssetLocator} implementations provided in the
      * {@link AssetManager#registerLocator(java.lang.String, java.lang.Class) }
-     * call. If located successfully, it will be loaded via the the appropriate
+     * call. If located successfully, it will be loaded via the appropriate
      * {@link AssetLoader} implementation based on the file's extension, as
      * specified in the call 
      * {@link AssetManager#registerLoader(java.lang.Class, java.lang.String[]) }.

+ 3 - 2
jme3-core/src/main/java/com/jme3/asset/ImplHandler.java

@@ -32,6 +32,7 @@
 package com.jme3.asset;
 
 import com.jme3.asset.cache.AssetCache;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -279,9 +280,9 @@ final class ImplHandler {
         // Synchronized access must be used for any ops on classToLoaderMap
         // Find the loader ImplThreadLocal for this class
         synchronized (classToLoaderMap){
-            ImplThreadLocal local = classToLoaderMap.get(loaderType);
             // Remove it from the class->loader map
-            classToLoaderMap.remove(loaderType);
+            ImplThreadLocal local = classToLoaderMap.remove(loaderType);
+            if (local == null) return;
             // Remove it from the extension->loader map
             for (String extension : local.getExtensions()){
                 extensionToLoaderMap.remove(extension);

+ 1 - 2
jme3-core/src/main/java/com/jme3/asset/cache/WeakRefCloneAssetCache.java

@@ -112,11 +112,10 @@ public class WeakRefCloneAssetCache implements AssetCache {
             // might not even have this asset anymore, it is OK.
             if (smartCache.remove(key) != null){
                 removedAssets ++;
-                //System.out.println("WeakRefAssetCache: The asset " + ref.assetKey + " was purged from the cache");
             }
         }
         if (removedAssets >= 1) {
-            logger.log(Level.FINE, "WeakRefAssetCache: {0} assets were purged from the cache.", removedAssets);
+            logger.log(Level.FINE, "WeakRefCloneAssetCache: {0} assets were purged from the cache.", removedAssets);
         }
     }
     

+ 5 - 5
jme3-core/src/main/java/com/jme3/audio/AudioNode.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012, 2016 jMonkeyEngine
+ * Copyright (c) 2009-2012, 2016, 2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -221,7 +221,7 @@ public class AudioNode extends Node implements AudioSource {
     /**
      * Start playing an instance of this audio. This method can be used
      * to play the same <code>AudioNode</code> multiple times. Note
-     * that changes to the parameters of this AudioNode will not effect the
+     * that changes to the parameters of this AudioNode will not affect the
      * instances already playing.
      */
     public void playInstance(){
@@ -278,8 +278,8 @@ public class AudioNode extends Node implements AudioSource {
      * the dry filter will only influence the "dry" portion of the audio,
      * e.g. not the reverberated parts of the AudioNode playing.
      *
-     * See the relevent documentation for the {@link Filter} to determine
-     * the effect.
+     * See the relevant documentation for the {@link Filter} to determine the
+     * effect.
      *
      * @param dryFilter The filter to set, or null to disable dry filter.
      */
@@ -692,7 +692,7 @@ public class AudioNode extends Node implements AudioSource {
 
     /**
      * Set the audio node as positional.
-     * The position, velocity, and distance parameters effect positional
+     * The position, velocity, and distance parameters affect positional
      * audio nodes. Set to false if the audio node should play in "headspace".
      *
      * @param positional True if the audio node should be positional, otherwise

+ 261 - 76
jme3-core/src/main/java/com/jme3/audio/openal/AL.java

@@ -12,37 +12,37 @@ public interface AL {
     /**
      * Boolean False.
      */
-    static final int AL_FALSE = 0;
+    public static final int AL_FALSE = 0;
 
     /**
      * Boolean True.
      */
-    static final int AL_TRUE = 1;
+    public static final int AL_TRUE = 1;
 
     /* "no distance model" or "no buffer" */
-    static final int AL_NONE = 0;
+    public static final int AL_NONE = 0;
 
     /**
      * Indicate Source has relative coordinates.
      */
-    static final int AL_SOURCE_RELATIVE = 0x202;
+    public static final int AL_SOURCE_RELATIVE = 0x202;
 
     /**
      * Directional source, inner cone angle, in degrees. Range: [0-360] Default:
      * 360
      */
-    static final int AL_CONE_INNER_ANGLE = 0x1001;
+    public static final int AL_CONE_INNER_ANGLE = 0x1001;
 
     /**
      * Directional source, outer cone angle, in degrees. Range: [0-360] Default:
      * 360
      */
-    static final int AL_CONE_OUTER_ANGLE = 0x1002;
+    public static final int AL_CONE_OUTER_ANGLE = 0x1002;
 
     /**
      * Specify the pitch to be applied at source. Range: [0.5-2.0] Default: 1.0
      */
-    static final int AL_PITCH = 0x1003;
+    public static final int AL_PITCH = 0x1003;
 
     /**
      * Specify the current location in three dimensional space. OpenAL, like
@@ -52,84 +52,84 @@ public interface AL {
      * coordinate system, flip the sign on the Z coordinate. Listener position
      * is always in the world coordinate system.
      */
-    static final int AL_POSITION = 0x1004;
+    public static final int AL_POSITION = 0x1004;
 
     /**
      * Specify the current direction.
      */
-    static final int AL_DIRECTION = 0x1005;
+    public static final int AL_DIRECTION = 0x1005;
 
     /**
      * Specify the current velocity in three dimensional space.
      */
-    static final int AL_VELOCITY = 0x1006;
+    public static final int AL_VELOCITY = 0x1006;
 
     /**
      * Indicate whether source is looping. Type: ALboolean? Range: [AL_TRUE,
      * AL_FALSE] Default: FALSE.
      */
-    static final int AL_LOOPING = 0x1007;
+    public static final int AL_LOOPING = 0x1007;
 
     /**
      * Indicate the buffer to provide sound samples. Type: ALuint. Range: any
      * valid Buffer id.
      */
-    static final int AL_BUFFER = 0x1009;
+    public static final int AL_BUFFER = 0x1009;
 
     /**
      * Indicate the gain (volume amplification) applied. Type: ALfloat. Range:
      * ]0.0- ] A value of 1.0 means un-attenuated/unchanged. Each division by 2
-     * equals an attenuation of -6dB. Each multiplicaton with 2 equals an
+     * equals an attenuation of -6dB. Each multiplication by 2 equals an
      * amplification of +6dB. A value of 0.0 is meaningless with respect to a
      * logarithmic scale; it is interpreted as zero volume - the channel is
      * effectively disabled.
      */
-    static final int AL_GAIN = 0x100A;
+    public static final int AL_GAIN = 0x100A;
 
     /*
      * Indicate minimum source attenuation
      * Type: ALfloat
      * Range:  [0.0 - 1.0]
      *
-     * Logarthmic
+     * Logarithmic
      */
-    static final int AL_MIN_GAIN = 0x100D;
+    public static final int AL_MIN_GAIN = 0x100D;
 
     /**
      * Indicate maximum source attenuation Type: ALfloat Range: [0.0 - 1.0]
      *
-     * Logarthmic
+     * Logarithmic
      */
-    static final int AL_MAX_GAIN = 0x100E;
+    public static final int AL_MAX_GAIN = 0x100E;
 
     /**
      * Indicate listener orientation.
      *
      * at/up
      */
-    static final int AL_ORIENTATION = 0x100F;
+    public static final int AL_ORIENTATION = 0x100F;
 
     /**
      * Source state information.
      */
-    static final int AL_SOURCE_STATE = 0x1010;
-    static final int AL_INITIAL = 0x1011;
-    static final int AL_PLAYING = 0x1012;
-    static final int AL_PAUSED = 0x1013;
-    static final int AL_STOPPED = 0x1014;
+    public static final int AL_SOURCE_STATE = 0x1010;
+    public static final int AL_INITIAL = 0x1011;
+    public static final int AL_PLAYING = 0x1012;
+    public static final int AL_PAUSED = 0x1013;
+    public static final int AL_STOPPED = 0x1014;
 
     /**
      * Buffer Queue params
      */
-    static final int AL_BUFFERS_QUEUED = 0x1015;
-    static final int AL_BUFFERS_PROCESSED = 0x1016;
+    public static final int AL_BUFFERS_QUEUED = 0x1015;
+    public static final int AL_BUFFERS_PROCESSED = 0x1016;
 
     /**
      * Source buffer position information
      */
-    static final int AL_SEC_OFFSET = 0x1024;
-    static final int AL_SAMPLE_OFFSET = 0x1025;
-    static final int AL_BYTE_OFFSET = 0x1026;
+    public static final int AL_SEC_OFFSET = 0x1024;
+    public static final int AL_SAMPLE_OFFSET = 0x1025;
+    public static final int AL_BYTE_OFFSET = 0x1026;
 
     /*
      * Source type (Static, Streaming or undetermined)
@@ -137,38 +137,38 @@ public interface AL {
      * Source is Streaming if one or more Buffers have been attached using alSourceQueueBuffers
      * Source is undetermined when it has the NULL buffer attached
      */
-    static final int AL_SOURCE_TYPE = 0x1027;
-    static final int AL_STATIC = 0x1028;
-    static final int AL_STREAMING = 0x1029;
-    static final int AL_UNDETERMINED = 0x1030;
+    public static final int AL_SOURCE_TYPE = 0x1027;
+    public static final int AL_STATIC = 0x1028;
+    public static final int AL_STREAMING = 0x1029;
+    public static final int AL_UNDETERMINED = 0x1030;
 
     /**
      * Sound samples: format specifier.
      */
-    static final int AL_FORMAT_MONO8 = 0x1100;
-    static final int AL_FORMAT_MONO16 = 0x1101;
-    static final int AL_FORMAT_STEREO8 = 0x1102;
-    static final int AL_FORMAT_STEREO16 = 0x1103;
+    public static final int AL_FORMAT_MONO8 = 0x1100;
+    public static final int AL_FORMAT_MONO16 = 0x1101;
+    public static final int AL_FORMAT_STEREO8 = 0x1102;
+    public static final int AL_FORMAT_STEREO16 = 0x1103;
 
     /**
      * source specific reference distance Type: ALfloat Range: 0.0 - +inf
      *
      * At 0.0, no distance attenuation occurs. Default is 1.0.
      */
-    static final int AL_REFERENCE_DISTANCE = 0x1020;
+    public static final int AL_REFERENCE_DISTANCE = 0x1020;
 
     /**
      * source specific rolloff factor Type: ALfloat Range: 0.0 - +inf
      *
      */
-    static final int AL_ROLLOFF_FACTOR = 0x1021;
+    public static final int AL_ROLLOFF_FACTOR = 0x1021;
 
     /**
      * Directional source, outer cone gain.
      *
      * Default: 0.0 Range: [0.0 - 1.0] Logarithmic
      */
-    static final int AL_CONE_OUTER_GAIN = 0x1022;
+    public static final int AL_CONE_OUTER_GAIN = 0x1022;
 
     /**
      * Indicate distance above which sources are not attenuated using the
@@ -176,64 +176,64 @@ public interface AL {
      *
      * Default: +inf Type: ALfloat Range: 0.0 - +inf
      */
-    static final int AL_MAX_DISTANCE = 0x1023;
+    public static final int AL_MAX_DISTANCE = 0x1023;
 
     /**
      * Sound samples: frequency, in units of Hertz [Hz]. This is the number of
      * samples per second. Half of the sample frequency marks the maximum
      * significant frequency component.
      */
-    static final int AL_FREQUENCY = 0x2001;
-    static final int AL_BITS = 0x2002;
-    static final int AL_CHANNELS = 0x2003;
-    static final int AL_SIZE = 0x2004;
+    public static final int AL_FREQUENCY = 0x2001;
+    public static final int AL_BITS = 0x2002;
+    public static final int AL_CHANNELS = 0x2003;
+    public static final int AL_SIZE = 0x2004;
 
     /**
      * Buffer state.
      *
      * Not supported for public use (yet).
      */
-    static final int AL_UNUSED = 0x2010;
-    static final int AL_PENDING = 0x2011;
-    static final int AL_PROCESSED = 0x2012;
+    public static final int AL_UNUSED = 0x2010;
+    public static final int AL_PENDING = 0x2011;
+    public static final int AL_PROCESSED = 0x2012;
 
     /**
      * Errors: No Error.
      */
-    static final int AL_NO_ERROR = 0;
+    public static final int AL_NO_ERROR = 0;
 
     /**
-     * Invalid Name paramater passed to AL call.
+     * Invalid Name parameter passed to AL call.
      */
-    static final int AL_INVALID_NAME = 0xA001;
+    public static final int AL_INVALID_NAME = 0xA001;
 
     /**
      * Invalid parameter passed to AL call.
      */
-    static final int AL_INVALID_ENUM = 0xA002;
+    public static final int AL_INVALID_ENUM = 0xA002;
 
     /**
      * Invalid enum parameter value.
      */
-    static final int AL_INVALID_VALUE = 0xA003;
+    public static final int AL_INVALID_VALUE = 0xA003;
 
     /**
      * Illegal call.
      */
-    static final int AL_INVALID_OPERATION = 0xA004;
+    public static final int AL_INVALID_OPERATION = 0xA004;
 
     /**
      * No mojo.
      */
-    static final int AL_OUT_OF_MEMORY = 0xA005;
+    public static final int AL_OUT_OF_MEMORY = 0xA005;
 
     /**
      * Context strings: Vendor Name.
      */
-    static final int AL_VENDOR = 0xB001;
-    static final int AL_VERSION = 0xB002;
-    static final int AL_RENDERER = 0xB003;
-    static final int AL_EXTENSIONS = 0xB004;
+    public static final int AL_VENDOR = 0xB001;
+    public static final int AL_VERSION = 0xB002;
+    public static final int AL_RENDERER = 0xB003;
+    public static final int AL_EXTENSIONS = 0xB004;
 
     /**
      * Global tweakage.
@@ -241,56 +241,241 @@ public interface AL {
     /**
      * Doppler scale. Default 1.0
      */
-    static final int AL_DOPPLER_FACTOR = 0xC000;
+    public static final int AL_DOPPLER_FACTOR = 0xC000;
 
     /**
      * Tweaks speed of propagation.
      */
-    static final int AL_DOPPLER_VELOCITY = 0xC001;
+    public static final int AL_DOPPLER_VELOCITY = 0xC001;
 
     /**
      * Speed of Sound in units per second
      */
-    static final int AL_SPEED_OF_SOUND = 0xC003;
+    public static final int AL_SPEED_OF_SOUND = 0xC003;
 
     /**
      * Distance models
      *
      * used in conjunction with DistanceModel
      *
-     * implicit: NONE, which disances distance attenuation.
-     */
-    static final int AL_DISTANCE_MODEL = 0xD000;
-    static final int AL_INVERSE_DISTANCE = 0xD001;
-    static final int AL_INVERSE_DISTANCE_CLAMPED = 0xD002;
-    static final int AL_LINEAR_DISTANCE = 0xD003;
-    static final int AL_LINEAR_DISTANCE_CLAMPED = 0xD004;
-    static final int AL_EXPONENT_DISTANCE = 0xD005;
-    static final int AL_EXPONENT_DISTANCE_CLAMPED = 0xD006;
-//
-///* Listener parameter value ranges and defaults. */
-//#define AL_MIN_METERS_PER_UNIT                   FLT_MIN
-//#define AL_MAX_METERS_PER_UNIT                   FLT_MAX
-//#define AL_DEFAULT_METERS_PER_UNIT               (1.0f)
+     * implicit: NONE, which disables distance attenuation.
+     */
+    public static final int AL_DISTANCE_MODEL = 0xD000;
+    public static final int AL_INVERSE_DISTANCE = 0xD001;
+    public static final int AL_INVERSE_DISTANCE_CLAMPED = 0xD002;
+    public static final int AL_LINEAR_DISTANCE = 0xD003;
+    public static final int AL_LINEAR_DISTANCE_CLAMPED = 0xD004;
+    public static final int AL_EXPONENT_DISTANCE = 0xD005;
+    public static final int AL_EXPONENT_DISTANCE_CLAMPED = 0xD006;
+
+    //
+    ///* Listener parameter value ranges and defaults. */
+    //#define AL_MIN_METERS_PER_UNIT                   FLT_MIN
+    //#define AL_MAX_METERS_PER_UNIT                   FLT_MAX
+    //#define AL_DEFAULT_METERS_PER_UNIT               (1.0f)
 
     public String alGetString(int parameter);
+
+    /**
+     * Requests a number of source names.
+     *
+     * @return the number of source names.
+     */
     public int alGenSources();
+
+    /**
+     * Obtains error information.
+     * <p>
+     * <p>Each detectable error is assigned a numeric code. When an error is detected by AL, a flag is set and the error code is recorded. Further errors, if they
+     * occur, do not affect this recorded code. When alGetError is called, the code is returned and the flag is cleared, so that a further error will again
+     * record its code. If a call to alGetError returns AL_NO_ERROR then there has been no detectable error since the last call to alGetError (or since the AL
+     * was initialized).</p>
+     * <p>
+     * <p>Error codes can be mapped to strings. The alGetString function returns a pointer to a constant (literal) string that is identical to the identifier used
+     * for the enumeration value, as defined in the specification.</p>
+     */
     public int alGetError();
+
+    /**
+     * Requests the deletion of a number of sources.
+     *
+     * @param numSources the number of sources.
+     * @param sources    the sources to delete.
+     */
     public void alDeleteSources(int numSources, IntBuffer sources);
+
+    /**
+     * Requests a number of buffer names.
+     *
+     * @param numBuffers the number of buffers.
+     * @param buffers    the buffer that will receive the buffer names.
+     */
     public void alGenBuffers(int numBuffers, IntBuffer buffers);
+
+    /**
+     * Requests the deletion of a number of buffers.
+     *
+     * @param numBuffers the number of buffers.
+     * @param buffers    the buffers to delete.
+     */
     public void alDeleteBuffers(int numBuffers, IntBuffer buffers);
+
+    /**
+     * Sets the source state to AL_STOPPED.
+     * <p>
+     * <p>alSourceStop applied to an AL_INITIAL source is a legal NOP. alSourceStop applied to a AL_PLAYING source will change its state to AL_STOPPED. The source
+     * is exempt from processing, its current state is preserved. alSourceStop applied to a AL_PAUSED source will change its state to AL_STOPPED, with the same
+     * consequences as on a AL_PLAYING source. alSourceStop applied to a AL_STOPPED source is a legal NOP.</p>
+     *
+     * @param source the source to stop.
+     */
     public void alSourceStop(int source);
+
+    /**
+     * Integer version of {@link #alSourcef Sourcef}.
+     *
+     * @param source the source to modify.
+     * @param param  the parameter to modify.
+     * @param value  the parameter value.
+     */
     public void alSourcei(int source, int param, int value);
+
+    /**
+     * Sets the sample data of the specified buffer.
+     * <p>
+     * <p>The data specified is copied to an internal software, or if possible, hardware buffer. The implementation is free to apply decompression, conversion,
+     * resampling, and filtering as needed.</p>
+     * <p>
+     * <p>8-bit data is expressed as an unsigned value over the range 0 to 255, 128 being an audio output level of zero.</p>
+     * <p>
+     * <p>16-bit data is expressed as a signed value over the range -32768 to 32767, 0 being an audio output level of zero. Byte order for 16-bit values is
+     * determined by the native format of the CPU.</p>
+     * <p>
+     * <p>Stereo data is expressed in an interleaved format, left channel sample followed by the right channel sample.</p>
+     * <p>
+     * <p>Buffers containing audio data with more than one channel will be played without 3D spatialization features – these formats are normally used for
+     * background music.</p>
+     *
+     * @param buffer    the buffer to modify.
+     * @param format    the data format. One of:<br><table><tr><td>{@link #AL_FORMAT_MONO8 FORMAT_MONO8}</td><td>{@link #AL_FORMAT_MONO16 FORMAT_MONO16}</td><td>{@link #AL_FORMAT_STEREO8 FORMAT_STEREO8}</td><td>{@link #AL_FORMAT_STEREO16 FORMAT_STEREO16}</td></tr></table>
+     * @param data      the sample data.
+     * @param frequency the data frequency.
+     */
     public void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency);
+
+    /**
+     * Sets the source state to AL_PLAYING.
+     * <p>
+     * <p>alSourcePlay applied to an AL_INITIAL source will promote the source to AL_PLAYING, thus the data found in the buffer will be fed into the processing,
+     * starting at the beginning. alSourcePlay applied to a AL_PLAYING source will restart the source from the beginning. It will not affect the configuration,
+     * and will leave the source in AL_PLAYING state, but reset the sampling offset to the beginning. alSourcePlay applied to a AL_PAUSED source will resume
+     * processing using the source state as preserved at the alSourcePause operation. alSourcePlay applied to a AL_STOPPED source will propagate it to
+     * AL_INITIAL then to AL_PLAYING immediately.</p>
+     *
+     * @param source the source to play.
+     */
     public void alSourcePlay(int source);
+
+    /**
+     * Sets the source state to AL_PAUSED.
+     * <p>
+     * <p>alSourcePause applied to an AL_INITIAL source is a legal NOP. alSourcePause applied to a AL_PLAYING source will change its state to AL_PAUSED. The
+     * source is exempt from processing, its current state is preserved. alSourcePause applied to a AL_PAUSED source is a legal NOP. alSourcePause applied to a
+     * AL_STOPPED source is a legal NOP.</p>
+     *
+     * @param source the source to pause.
+     */
     public void alSourcePause(int source);
+
+    /**
+     * Sets the float value of a source parameter.
+     *
+     * @param source the source to modify.
+     * @param param  the parameter to modify. One of:<br><table><tr><td>{@link #AL_CONE_INNER_ANGLE CONE_INNER_ANGLE}</td><td>{@link #AL_CONE_OUTER_ANGLE CONE_OUTER_ANGLE}</td><td>{@link #AL_PITCH PITCH}</td><td>{@link #AL_DIRECTION DIRECTION}</td><td>{@link #AL_LOOPING LOOPING}</td><td>{@link #AL_BUFFER BUFFER}</td><td>{@link #AL_SOURCE_STATE SOURCE_STATE}</td></tr><tr><td>{@link #AL_CONE_OUTER_GAIN CONE_OUTER_GAIN}</td><td>{@link #AL_SOURCE_TYPE SOURCE_TYPE}</td><td>{@link #AL_POSITION POSITION}</td><td>{@link #AL_VELOCITY VELOCITY}</td><td>{@link #AL_GAIN GAIN}</td><td>{@link #AL_REFERENCE_DISTANCE REFERENCE_DISTANCE}</td><td>{@link #AL_ROLLOFF_FACTOR ROLLOFF_FACTOR}</td></tr><tr><td>{@link #AL_MAX_DISTANCE MAX_DISTANCE}</td></tr></table>
+     * @param value  the parameter value.
+     */
     public void alSourcef(int source, int param, float value);
+
+    /**
+     * Sets the 3 dimensional values of a source parameter.
+     *
+     * @param source the source to modify.
+     * @param param  the parameter to modify. One of:<br><table><tr><td>{@link #AL_CONE_INNER_ANGLE CONE_INNER_ANGLE}</td><td>{@link #AL_CONE_OUTER_ANGLE CONE_OUTER_ANGLE}</td><td>{@link #AL_PITCH PITCH}</td><td>{@link #AL_DIRECTION DIRECTION}</td><td>{@link #AL_LOOPING LOOPING}</td><td>{@link #AL_BUFFER BUFFER}</td><td>{@link #AL_SOURCE_STATE SOURCE_STATE}</td></tr><tr><td>{@link #AL_CONE_OUTER_GAIN CONE_OUTER_GAIN}</td><td>{@link #AL_SOURCE_TYPE SOURCE_TYPE}</td><td>{@link #AL_POSITION POSITION}</td><td>{@link #AL_VELOCITY VELOCITY}</td><td>{@link #AL_GAIN GAIN}</td><td>{@link #AL_REFERENCE_DISTANCE REFERENCE_DISTANCE}</td><td>{@link #AL_ROLLOFF_FACTOR ROLLOFF_FACTOR}</td></tr><tr><td>{@link #AL_MAX_DISTANCE MAX_DISTANCE}</td></tr></table>
+     * @param value1 the first parameter value.
+     * @param value2 the second parameter value.
+     * @param value3 the third parameter value.
+     */
     public void alSource3f(int source, int param, float value1, float value2, float value3);
+
+    /**
+     * Returns the integer value of the specified source parameter.
+     *
+     * @param source the source to query.
+     * @param param  the parameter to query. One of:<br><table><tr><td>{@link #AL_CONE_INNER_ANGLE CONE_INNER_ANGLE}</td><td>{@link #AL_CONE_OUTER_ANGLE CONE_OUTER_ANGLE}</td><td>{@link #AL_PITCH PITCH}</td><td>{@link #AL_DIRECTION DIRECTION}</td><td>{@link #AL_LOOPING LOOPING}</td><td>{@link #AL_BUFFER BUFFER}</td><td>{@link #AL_SOURCE_STATE SOURCE_STATE}</td></tr><tr><td>{@link #AL_CONE_OUTER_GAIN CONE_OUTER_GAIN}</td><td>{@link #AL_SOURCE_TYPE SOURCE_TYPE}</td><td>{@link #AL_POSITION POSITION}</td><td>{@link #AL_VELOCITY VELOCITY}</td><td>{@link #AL_GAIN GAIN}</td><td>{@link #AL_REFERENCE_DISTANCE REFERENCE_DISTANCE}</td><td>{@link #AL_ROLLOFF_FACTOR ROLLOFF_FACTOR}</td></tr><tr><td>{@link #AL_MAX_DISTANCE MAX_DISTANCE}</td></tr></table>
+     */
     public int alGetSourcei(int source, int param);
+
+    /**
+     * Removes a number of buffer entries that have finished processing, in the order of apperance, from the queue of the specified source.
+     * <p>
+     * <p>Once a queue entry for a buffer has been appended to a queue and is pending processing, it should not be changed. Removal of a given queue entry is not
+     * possible unless either the source is stopped (in which case then entire queue is considered processed), or if the queue entry has already been processed
+     * (AL_PLAYING or AL_PAUSED source). A playing source will enter the AL_STOPPED state if it completes playback of the last buffer in its queue (the same
+     * behavior as when a single buffer has been attached to a source and has finished playback).</p>
+     *
+     * @param source     the target source
+     * @param numBuffers the names count.
+     * @param buffers    the buffer names
+     */
     public void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers);
+
+    /**
+     * Queues up one or multiple buffer names to the specified source.
+     * <p>
+     * <p>The buffers will be queued in the sequence in which they appear in the array. This command is legal on a source in any playback state (to allow for
+     * streaming, queuing has to be possible on a AL_PLAYING source). All buffers in a queue must have the same format and attributes, with the exception of
+     * the {@code NULL} buffer (i.e., 0) which can always be queued.</p>
+     *
+     * @param source     the target source.
+     * @param numBuffers the names count.
+     * @param buffers    the buffer names.
+     */
     public void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers);
+
+    /**
+     * Pointer version of {@link #alListenerf Listenerf}.
+     *
+     * @param param the parameter to modify.
+     * @param data  the parameter values.
+     */
     public void alListener(int param, FloatBuffer data);
+
+    /**
+     * Sets the float value of a listener parameter.
+     *
+     * @param param the parameter to modify. One of:<br><table><tr><td>{@link #AL_ORIENTATION ORIENTATION}</td><td>{@link #AL_POSITION POSITION}</td><td>{@link #AL_VELOCITY VELOCITY}</td><td>{@link #AL_GAIN GAIN}</td></tr></table>
+     * @param value the parameter value.
+     */
     public void alListenerf(int param, float value);
+
+    /**
+     * Sets the 3 dimensional float values of a listener parameter.
+     *
+     * @param param  the parameter to modify. One of:<br><table><tr><td>{@link #AL_ORIENTATION ORIENTATION}</td><td>{@link #AL_POSITION POSITION}</td><td>{@link #AL_VELOCITY VELOCITY}</td><td>{@link #AL_GAIN GAIN}</td></tr></table>
+     * @param value1 the first value.
+     * @param value2 the second value.
+     * @param value3 the third value.
+     */
     public void alListener3f(int param, float value1, float value2, float value3);
+
+    /**
+     * Sets the 3 dimensional integer values of a source parameter.
+     *
+     * @param source the source to modify.
+     * @param param  the parameter to modify.
+     * @param value1 the first value.
+     * @param value2 the second value.
+     * @param value3 the third value.
+     */
     public void alSource3i(int source, int param, int value1, int value2, int value3);
 }

+ 72 - 19
jme3-core/src/main/java/com/jme3/audio/openal/ALC.java

@@ -7,67 +7,120 @@ public interface ALC {
     /**
      * No error
      */
-    static final int ALC_NO_ERROR = 0;
+    public static final int ALC_NO_ERROR = 0;
 
     /**
      * No device
      */
-    static final int ALC_INVALID_DEVICE = 0xA001;
+    public static final int ALC_INVALID_DEVICE = 0xA001;
 
     /**
      * invalid context ID
      */
-    static final int ALC_INVALID_CONTEXT = 0xA002;
+    public static final int ALC_INVALID_CONTEXT = 0xA002;
 
     /**
      * bad enum
      */
-    static final int ALC_INVALID_ENUM = 0xA003;
+    public static final int ALC_INVALID_ENUM = 0xA003;
 
     /**
      * bad value
      */
-    static final int ALC_INVALID_VALUE = 0xA004;
+    public static final int ALC_INVALID_VALUE = 0xA004;
 
     /**
      * Out of memory.
      */
-    static final int ALC_OUT_OF_MEMORY = 0xA005;
+    public static final int ALC_OUT_OF_MEMORY = 0xA005;
 
     /**
      * The Specifier string for default device
      */
-    static final int ALC_DEFAULT_DEVICE_SPECIFIER = 0x1004;
-    static final int ALC_DEVICE_SPECIFIER = 0x1005;
-    static final int ALC_EXTENSIONS = 0x1006;
+    public static final int ALC_DEFAULT_DEVICE_SPECIFIER = 0x1004;
+    public static final int ALC_DEVICE_SPECIFIER = 0x1005;
+    public static final int ALC_EXTENSIONS = 0x1006;
 
-    static final int ALC_MAJOR_VERSION = 0x1000;
-    static final int ALC_MINOR_VERSION = 0x1001;
+    public static final int ALC_MAJOR_VERSION = 0x1000;
+    public static final int ALC_MINOR_VERSION = 0x1001;
 
-    static final int ALC_ATTRIBUTES_SIZE = 0x1002;
-    static final int ALC_ALL_ATTRIBUTES = 0x1003;
+    public static final int ALC_ATTRIBUTES_SIZE = 0x1002;
+    public static final int ALC_ALL_ATTRIBUTES = 0x1003;
 
     /**
      * Capture extension
      */
-    static final int ALC_CAPTURE_DEVICE_SPECIFIER = 0x310;
-    static final int ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER = 0x311;
-    static final int ALC_CAPTURE_SAMPLES = 0x312;
+    public static final int ALC_CAPTURE_DEVICE_SPECIFIER = 0x310;
+    public static final int ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER = 0x311;
+    public static final int ALC_CAPTURE_SAMPLES = 0x312;
 
     /**
      * ALC_ENUMERATE_ALL_EXT enums
      */
-    static final int ALC_DEFAULT_ALL_DEVICES_SPECIFIER = 0x1012;
-    static final int ALC_ALL_DEVICES_SPECIFIER = 0x1013;
+    public static final int ALC_DEFAULT_ALL_DEVICES_SPECIFIER = 0x1012;
+    public static final int ALC_ALL_DEVICES_SPECIFIER = 0x1013;
 
     //public static ALCCapabilities createCapabilities(long device);
-    
+
+    /**
+     * Creates an AL context.
+     */
     public void createALC();
+
+    /**
+     * Destroys an AL context.
+     */
     public void destroyALC();
+
+    /**
+     * Checks of creating an AL context.
+     *
+     * @return true if an AL context is created.
+     */
     public boolean isCreated();
+
+    /**
+     * Obtains string value(s) from ALC.
+     *
+     * @param parameter the information to query. One of:<br><table><tr><td>{@link #ALC_DEFAULT_DEVICE_SPECIFIER DEFAULT_DEVICE_SPECIFIER}</td><td>{@link #ALC_DEVICE_SPECIFIER DEVICE_SPECIFIER}</td><td>{@link #ALC_EXTENSIONS EXTENSIONS}</td></tr><tr><td>{@link #ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER CAPTURE_DEFAULT_DEVICE_SPECIFIER}</td><td>{@link #ALC_CAPTURE_DEVICE_SPECIFIER CAPTURE_DEVICE_SPECIFIER}</td></tr></table>
+     */
     public String alcGetString(int parameter);
+
+    /**
+     * Verifies that a given extension is available for the current context and the device it is associated with.
+     * <p>
+     * <p>Invalid and unsupported string tokens return ALC_FALSE. A {@code NULL} deviceHandle is acceptable. {@code extName} is not case sensitive – the implementation
+     * will convert the name to all upper-case internally (and will express extension names in upper-case).</p>
+     *
+     * @param extension the extension name.
+     */
     public boolean alcIsExtensionPresent(String extension);
+
+    /**
+     * Obtains integer value(s) from ALC.
+     *
+     * @param param  the information to query. One of:<br><table><tr><td>{@link #ALC_MAJOR_VERSION MAJOR_VERSION}</td><td>{@link #ALC_MINOR_VERSION MINOR_VERSION}</td><td>{@link #ALC_ATTRIBUTES_SIZE ATTRIBUTES_SIZE}</td><td>{@link #ALC_ALL_ATTRIBUTES ALL_ATTRIBUTES}</td><td>{@link #ALC_CAPTURE_SAMPLES CAPTURE_SAMPLES}</td></tr></table>
+     * @param buffer the destination buffer.
+     * @param size   the buffer size.
+     */
     public void alcGetInteger(int param, IntBuffer buffer, int size);
+
+    /**
+     * Pauses a playback device.
+     * <p>
+     * <p>When paused, no contexts associated with the device will be processed or updated. Playing sources will not produce sound, have their offsets
+     * incremented, or process any more buffers, until the device is resumed. Pausing a device that is already paused is a legal no-op.</p>
+     */
     public void alcDevicePauseSOFT();
+
+    /**
+     * Resumes playback of a paused device.
+     * <p>
+     * <p>This will restart processing on the device -- sources will resume playing sound as normal. Resuming playback on a device that is not paused is a legal
+     * no-op.</p>
+     * <p>
+     * <p>These functions are not reference counted. alcDeviceResumeSOFT only needs to be called once to resume playback, regardless of how many times
+     * {@link #alcDevicePauseSOFT DevicePauseSOFT} was called.</p>
+     */
     public void alcDeviceResumeSOFT();
 }

+ 738 - 660
jme3-core/src/main/java/com/jme3/audio/openal/EFX.java

@@ -4,676 +4,754 @@ import java.nio.IntBuffer;
 
 public interface EFX {
 
-    static final String ALC_EXT_EFX_NAME = "ALC_EXT_EFX";
-
-    static final int ALC_EFX_MAJOR_VERSION = 0x20001;
-    static final int ALC_EFX_MINOR_VERSION = 0x20002;
-    static final int ALC_MAX_AUXILIARY_SENDS = 0x20003;
-
-///* Listener properties. */
-//#define AL_METERS_PER_UNIT                       0x20004
-//
-///* Source properties. */
-    static final int AL_DIRECT_FILTER = 0x20005;
-    static final int AL_AUXILIARY_SEND_FILTER = 0x20006;
-//#define AL_AIR_ABSORPTION_FACTOR                 0x20007
-//#define AL_ROOM_ROLLOFF_FACTOR                   0x20008
-//#define AL_CONE_OUTER_GAINHF                     0x20009
-    static final int AL_DIRECT_FILTER_GAINHF_AUTO = 0x2000A;
-//#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO       0x2000B
-//#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO     0x2000C
-//
-//
-///* Effect properties. */
-//
-///* Reverb effect parameters */
-    static final int AL_REVERB_DENSITY = 0x0001;
-    static final int AL_REVERB_DIFFUSION = 0x0002;
-    static final int AL_REVERB_GAIN = 0x0003;
-    static final int AL_REVERB_GAINHF = 0x0004;
-    static final int AL_REVERB_DECAY_TIME = 0x0005;
-    static final int AL_REVERB_DECAY_HFRATIO = 0x0006;
-    static final int AL_REVERB_REFLECTIONS_GAIN = 0x0007;
-    static final int AL_REVERB_REFLECTIONS_DELAY = 0x0008;
-    static final int AL_REVERB_LATE_REVERB_GAIN = 0x0009;
-    static final int AL_REVERB_LATE_REVERB_DELAY = 0x000A;
-    static final int AL_REVERB_AIR_ABSORPTION_GAINHF = 0x000B;
-    static final int AL_REVERB_ROOM_ROLLOFF_FACTOR = 0x000C;
-    static final int AL_REVERB_DECAY_HFLIMIT = 0x000D;
-
-///* EAX Reverb effect parameters */
-//#define AL_EAXREVERB_DENSITY                     0x0001
-//#define AL_EAXREVERB_DIFFUSION                   0x0002
-//#define AL_EAXREVERB_GAIN                        0x0003
-//#define AL_EAXREVERB_GAINHF                      0x0004
-//#define AL_EAXREVERB_GAINLF                      0x0005
-//#define AL_EAXREVERB_DECAY_TIME                  0x0006
-//#define AL_EAXREVERB_DECAY_HFRATIO               0x0007
-//#define AL_EAXREVERB_DECAY_LFRATIO               0x0008
-//#define AL_EAXREVERB_REFLECTIONS_GAIN            0x0009
-//#define AL_EAXREVERB_REFLECTIONS_DELAY           0x000A
-//#define AL_EAXREVERB_REFLECTIONS_PAN             0x000B
-//#define AL_EAXREVERB_LATE_REVERB_GAIN            0x000C
-//#define AL_EAXREVERB_LATE_REVERB_DELAY           0x000D
-//#define AL_EAXREVERB_LATE_REVERB_PAN             0x000E
-//#define AL_EAXREVERB_ECHO_TIME                   0x000F
-//#define AL_EAXREVERB_ECHO_DEPTH                  0x0010
-//#define AL_EAXREVERB_MODULATION_TIME             0x0011
-//#define AL_EAXREVERB_MODULATION_DEPTH            0x0012
-//#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF       0x0013
-//#define AL_EAXREVERB_HFREFERENCE                 0x0014
-//#define AL_EAXREVERB_LFREFERENCE                 0x0015
-//#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR         0x0016
-//#define AL_EAXREVERB_DECAY_HFLIMIT               0x0017
-//
-///* Chorus effect parameters */
-//#define AL_CHORUS_WAVEFORM                       0x0001
-//#define AL_CHORUS_PHASE                          0x0002
-//#define AL_CHORUS_RATE                           0x0003
-//#define AL_CHORUS_DEPTH                          0x0004
-//#define AL_CHORUS_FEEDBACK                       0x0005
-//#define AL_CHORUS_DELAY                          0x0006
-//
-///* Distortion effect parameters */
-//#define AL_DISTORTION_EDGE                       0x0001
-//#define AL_DISTORTION_GAIN                       0x0002
-//#define AL_DISTORTION_LOWPASS_CUTOFF             0x0003
-//#define AL_DISTORTION_EQCENTER                   0x0004
-//#define AL_DISTORTION_EQBANDWIDTH                0x0005
-//
-///* Echo effect parameters */
-//#define AL_ECHO_DELAY                            0x0001
-//#define AL_ECHO_LRDELAY                          0x0002
-//#define AL_ECHO_DAMPING                          0x0003
-//#define AL_ECHO_FEEDBACK                         0x0004
-//#define AL_ECHO_SPREAD                           0x0005
-//
-///* Flanger effect parameters */
-//#define AL_FLANGER_WAVEFORM                      0x0001
-//#define AL_FLANGER_PHASE                         0x0002
-//#define AL_FLANGER_RATE                          0x0003
-//#define AL_FLANGER_DEPTH                         0x0004
-//#define AL_FLANGER_FEEDBACK                      0x0005
-//#define AL_FLANGER_DELAY                         0x0006
-//
-///* Frequency shifter effect parameters */
-//#define AL_FREQUENCY_SHIFTER_FREQUENCY           0x0001
-//#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION      0x0002
-//#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION     0x0003
-//
-///* Vocal morpher effect parameters */
-//#define AL_VOCAL_MORPHER_PHONEMEA                0x0001
-//#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING  0x0002
-//#define AL_VOCAL_MORPHER_PHONEMEB                0x0003
-//#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING  0x0004
-//#define AL_VOCAL_MORPHER_WAVEFORM                0x0005
-//#define AL_VOCAL_MORPHER_RATE                    0x0006
-//
-///* Pitchshifter effect parameters */
-//#define AL_PITCH_SHIFTER_COARSE_TUNE             0x0001
-//#define AL_PITCH_SHIFTER_FINE_TUNE               0x0002
-//
-///* Ringmodulator effect parameters */
-//#define AL_RING_MODULATOR_FREQUENCY              0x0001
-//#define AL_RING_MODULATOR_HIGHPASS_CUTOFF        0x0002
-//#define AL_RING_MODULATOR_WAVEFORM               0x0003
-//
-///* Autowah effect parameters */
-//#define AL_AUTOWAH_ATTACK_TIME                   0x0001
-//#define AL_AUTOWAH_RELEASE_TIME                  0x0002
-//#define AL_AUTOWAH_RESONANCE                     0x0003
-//#define AL_AUTOWAH_PEAK_GAIN                     0x0004
-//
-///* Compressor effect parameters */
-//#define AL_COMPRESSOR_ONOFF                      0x0001
-//
-///* Equalizer effect parameters */
-//#define AL_EQUALIZER_LOW_GAIN                    0x0001
-//#define AL_EQUALIZER_LOW_CUTOFF                  0x0002
-//#define AL_EQUALIZER_MID1_GAIN                   0x0003
-//#define AL_EQUALIZER_MID1_CENTER                 0x0004
-//#define AL_EQUALIZER_MID1_WIDTH                  0x0005
-//#define AL_EQUALIZER_MID2_GAIN                   0x0006
-//#define AL_EQUALIZER_MID2_CENTER                 0x0007
-//#define AL_EQUALIZER_MID2_WIDTH                  0x0008
-//#define AL_EQUALIZER_HIGH_GAIN                   0x0009
-//#define AL_EQUALIZER_HIGH_CUTOFF                 0x000A
-//
-///* Effect type */
-//#define AL_EFFECT_FIRST_PARAMETER                0x0000
-//#define AL_EFFECT_LAST_PARAMETER                 0x8000
-    static final int AL_EFFECT_TYPE = 0x8001;
-//
-///* Effect types, used with the AL_EFFECT_TYPE property */
-//#define AL_EFFECT_NULL                           0x0000
-    static final int AL_EFFECT_REVERB = 0x0001;
-//#define AL_EFFECT_CHORUS                         0x0002
-//#define AL_EFFECT_DISTORTION                     0x0003
-//#define AL_EFFECT_ECHO                           0x0004
-//#define AL_EFFECT_FLANGER                        0x0005
-//#define AL_EFFECT_FREQUENCY_SHIFTER              0x0006
-//#define AL_EFFECT_VOCAL_MORPHER                  0x0007
-//#define AL_EFFECT_PITCH_SHIFTER                  0x0008
-//#define AL_EFFECT_RING_MODULATOR                 0x0009
-//#define AL_EFFECT_AUTOWAH                        0x000A
-//#define AL_EFFECT_COMPRESSOR                     0x000B
-//#define AL_EFFECT_EQUALIZER                      0x000C
-//#define AL_EFFECT_EAXREVERB                      0x8000
-//
-///* Auxiliary Effect Slot properties. */
-    static final int AL_EFFECTSLOT_EFFECT = 0x0001;
-//#define AL_EFFECTSLOT_GAIN                       0x0002
-//#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO        0x0003
-//
-///* NULL Auxiliary Slot ID to disable a source send. */
-//#define AL_EFFECTSLOT_NULL                       0x0000
-//
-//
-///* Filter properties. */
-//
-///* Lowpass filter parameters */
-    static final int AL_LOWPASS_GAIN = 0x0001;
-    static final int AL_LOWPASS_GAINHF = 0x0002;
-//
-///* Highpass filter parameters */
-//#define AL_HIGHPASS_GAIN                         0x0001
-//#define AL_HIGHPASS_GAINLF                       0x0002
-//
-///* Bandpass filter parameters */
-//#define AL_BANDPASS_GAIN                         0x0001
-//#define AL_BANDPASS_GAINLF                       0x0002
-//#define AL_BANDPASS_GAINHF                       0x0003
-//
-///* Filter type */
-//#define AL_FILTER_FIRST_PARAMETER                0x0000
-//#define AL_FILTER_LAST_PARAMETER                 0x8000
-    static final int AL_FILTER_TYPE = 0x8001;
-//
-///* Filter types, used with the AL_FILTER_TYPE property */
-    static final int AL_FILTER_NULL = 0x0000;
-    static final int AL_FILTER_LOWPASS = 0x0001;
-    static final int AL_FILTER_HIGHPASS = 0x0002;
-//#define AL_FILTER_BANDPASS                       0x0003
-//
-///* Filter ranges and defaults. */
-//
-///* Lowpass filter */
-//#define AL_LOWPASS_MIN_GAIN                      (0.0f)
-//#define AL_LOWPASS_MAX_GAIN                      (1.0f)
-//#define AL_LOWPASS_DEFAULT_GAIN                  (1.0f)
-//
-//#define AL_LOWPASS_MIN_GAINHF                    (0.0f)
-//#define AL_LOWPASS_MAX_GAINHF                    (1.0f)
-//#define AL_LOWPASS_DEFAULT_GAINHF                (1.0f)
-//
-///* Highpass filter */
-//#define AL_HIGHPASS_MIN_GAIN                     (0.0f)
-//#define AL_HIGHPASS_MAX_GAIN                     (1.0f)
-//#define AL_HIGHPASS_DEFAULT_GAIN                 (1.0f)
-//
-//#define AL_HIGHPASS_MIN_GAINLF                   (0.0f)
-//#define AL_HIGHPASS_MAX_GAINLF                   (1.0f)
-//#define AL_HIGHPASS_DEFAULT_GAINLF               (1.0f)
-//
-///* Bandpass filter */
-//#define AL_BANDPASS_MIN_GAIN                     (0.0f)
-//#define AL_BANDPASS_MAX_GAIN                     (1.0f)
-//#define AL_BANDPASS_DEFAULT_GAIN                 (1.0f)
-//
-//#define AL_BANDPASS_MIN_GAINHF                   (0.0f)
-//#define AL_BANDPASS_MAX_GAINHF                   (1.0f)
-//#define AL_BANDPASS_DEFAULT_GAINHF               (1.0f)
-//
-//#define AL_BANDPASS_MIN_GAINLF                   (0.0f)
-//#define AL_BANDPASS_MAX_GAINLF                   (1.0f)
-//#define AL_BANDPASS_DEFAULT_GAINLF               (1.0f)
-//
-//
-///* Effect parameter ranges and defaults. */
-//
-///* Standard reverb effect */
-//#define AL_REVERB_MIN_DENSITY                    (0.0f)
-//#define AL_REVERB_MAX_DENSITY                    (1.0f)
-//#define AL_REVERB_DEFAULT_DENSITY                (1.0f)
-//
-//#define AL_REVERB_MIN_DIFFUSION                  (0.0f)
-//#define AL_REVERB_MAX_DIFFUSION                  (1.0f)
-//#define AL_REVERB_DEFAULT_DIFFUSION              (1.0f)
-//
-//#define AL_REVERB_MIN_GAIN                       (0.0f)
-//#define AL_REVERB_MAX_GAIN                       (1.0f)
-//#define AL_REVERB_DEFAULT_GAIN                   (0.32f)
-//
-//#define AL_REVERB_MIN_GAINHF                     (0.0f)
-//#define AL_REVERB_MAX_GAINHF                     (1.0f)
-//#define AL_REVERB_DEFAULT_GAINHF                 (0.89f)
-//
-//#define AL_REVERB_MIN_DECAY_TIME                 (0.1f)
-//#define AL_REVERB_MAX_DECAY_TIME                 (20.0f)
-//#define AL_REVERB_DEFAULT_DECAY_TIME             (1.49f)
-//
-//#define AL_REVERB_MIN_DECAY_HFRATIO              (0.1f)
-//#define AL_REVERB_MAX_DECAY_HFRATIO              (2.0f)
-//#define AL_REVERB_DEFAULT_DECAY_HFRATIO          (0.83f)
-//
-//#define AL_REVERB_MIN_REFLECTIONS_GAIN           (0.0f)
-//#define AL_REVERB_MAX_REFLECTIONS_GAIN           (3.16f)
-//#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN       (0.05f)
-//
-//#define AL_REVERB_MIN_REFLECTIONS_DELAY          (0.0f)
-//#define AL_REVERB_MAX_REFLECTIONS_DELAY          (0.3f)
-//#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY      (0.007f)
-//
-//#define AL_REVERB_MIN_LATE_REVERB_GAIN           (0.0f)
-//#define AL_REVERB_MAX_LATE_REVERB_GAIN           (10.0f)
-//#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN       (1.26f)
-//
-//#define AL_REVERB_MIN_LATE_REVERB_DELAY          (0.0f)
-//#define AL_REVERB_MAX_LATE_REVERB_DELAY          (0.1f)
-//#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY      (0.011f)
-//
-//#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF      (0.892f)
-//#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF      (1.0f)
-//#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF  (0.994f)
-//
-//#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR        (0.0f)
-//#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR        (10.0f)
-//#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR    (0.0f)
-//
-//#define AL_REVERB_MIN_DECAY_HFLIMIT              AL_FALSE
-//#define AL_REVERB_MAX_DECAY_HFLIMIT              AL_TRUE
-//#define AL_REVERB_DEFAULT_DECAY_HFLIMIT          AL_TRUE
-//
-///* EAX reverb effect */
-//#define AL_EAXREVERB_MIN_DENSITY                 (0.0f)
-//#define AL_EAXREVERB_MAX_DENSITY                 (1.0f)
-//#define AL_EAXREVERB_DEFAULT_DENSITY             (1.0f)
-//
-//#define AL_EAXREVERB_MIN_DIFFUSION               (0.0f)
-//#define AL_EAXREVERB_MAX_DIFFUSION               (1.0f)
-//#define AL_EAXREVERB_DEFAULT_DIFFUSION           (1.0f)
-//
-//#define AL_EAXREVERB_MIN_GAIN                    (0.0f)
-//#define AL_EAXREVERB_MAX_GAIN                    (1.0f)
-//#define AL_EAXREVERB_DEFAULT_GAIN                (0.32f)
-//
-//#define AL_EAXREVERB_MIN_GAINHF                  (0.0f)
-//#define AL_EAXREVERB_MAX_GAINHF                  (1.0f)
-//#define AL_EAXREVERB_DEFAULT_GAINHF              (0.89f)
-//
-//#define AL_EAXREVERB_MIN_GAINLF                  (0.0f)
-//#define AL_EAXREVERB_MAX_GAINLF                  (1.0f)
-//#define AL_EAXREVERB_DEFAULT_GAINLF              (1.0f)
-//
-//#define AL_EAXREVERB_MIN_DECAY_TIME              (0.1f)
-//#define AL_EAXREVERB_MAX_DECAY_TIME              (20.0f)
-//#define AL_EAXREVERB_DEFAULT_DECAY_TIME          (1.49f)
-//
-//#define AL_EAXREVERB_MIN_DECAY_HFRATIO           (0.1f)
-//#define AL_EAXREVERB_MAX_DECAY_HFRATIO           (2.0f)
-//#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO       (0.83f)
-//
-//#define AL_EAXREVERB_MIN_DECAY_LFRATIO           (0.1f)
-//#define AL_EAXREVERB_MAX_DECAY_LFRATIO           (2.0f)
-//#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO       (1.0f)
-//
-//#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN        (0.0f)
-//#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN        (3.16f)
-//#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN    (0.05f)
-//
-//#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY       (0.0f)
-//#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY       (0.3f)
-//#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY   (0.007f)
-//
-//#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f)
-//
-//#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN        (0.0f)
-//#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN        (10.0f)
-//#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN    (1.26f)
-//
-//#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY       (0.0f)
-//#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY       (0.1f)
-//#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY   (0.011f)
-//
-//#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f)
-//
-//#define AL_EAXREVERB_MIN_ECHO_TIME               (0.075f)
-//#define AL_EAXREVERB_MAX_ECHO_TIME               (0.25f)
-//#define AL_EAXREVERB_DEFAULT_ECHO_TIME           (0.25f)
-//
-//#define AL_EAXREVERB_MIN_ECHO_DEPTH              (0.0f)
-//#define AL_EAXREVERB_MAX_ECHO_DEPTH              (1.0f)
-//#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH          (0.0f)
-//
-//#define AL_EAXREVERB_MIN_MODULATION_TIME         (0.04f)
-//#define AL_EAXREVERB_MAX_MODULATION_TIME         (4.0f)
-//#define AL_EAXREVERB_DEFAULT_MODULATION_TIME     (0.25f)
-//
-//#define AL_EAXREVERB_MIN_MODULATION_DEPTH        (0.0f)
-//#define AL_EAXREVERB_MAX_MODULATION_DEPTH        (1.0f)
-//#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH    (0.0f)
-//
-//#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF   (0.892f)
-//#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF   (1.0f)
-//#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
-//
-//#define AL_EAXREVERB_MIN_HFREFERENCE             (1000.0f)
-//#define AL_EAXREVERB_MAX_HFREFERENCE             (20000.0f)
-//#define AL_EAXREVERB_DEFAULT_HFREFERENCE         (5000.0f)
-//
-//#define AL_EAXREVERB_MIN_LFREFERENCE             (20.0f)
-//#define AL_EAXREVERB_MAX_LFREFERENCE             (1000.0f)
-//#define AL_EAXREVERB_DEFAULT_LFREFERENCE         (250.0f)
-//
-//#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR     (0.0f)
-//#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR     (10.0f)
-//#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
-//
-//#define AL_EAXREVERB_MIN_DECAY_HFLIMIT           AL_FALSE
-//#define AL_EAXREVERB_MAX_DECAY_HFLIMIT           AL_TRUE
-//#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT       AL_TRUE
-//
-///* Chorus effect */
-//#define AL_CHORUS_WAVEFORM_SINUSOID              (0)
-//#define AL_CHORUS_WAVEFORM_TRIANGLE              (1)
-//
-//#define AL_CHORUS_MIN_WAVEFORM                   (0)
-//#define AL_CHORUS_MAX_WAVEFORM                   (1)
-//#define AL_CHORUS_DEFAULT_WAVEFORM               (1)
-//
-//#define AL_CHORUS_MIN_PHASE                      (-180)
-//#define AL_CHORUS_MAX_PHASE                      (180)
-//#define AL_CHORUS_DEFAULT_PHASE                  (90)
-//
-//#define AL_CHORUS_MIN_RATE                       (0.0f)
-//#define AL_CHORUS_MAX_RATE                       (10.0f)
-//#define AL_CHORUS_DEFAULT_RATE                   (1.1f)
-//
-//#define AL_CHORUS_MIN_DEPTH                      (0.0f)
-//#define AL_CHORUS_MAX_DEPTH                      (1.0f)
-//#define AL_CHORUS_DEFAULT_DEPTH                  (0.1f)
-//
-//#define AL_CHORUS_MIN_FEEDBACK                   (-1.0f)
-//#define AL_CHORUS_MAX_FEEDBACK                   (1.0f)
-//#define AL_CHORUS_DEFAULT_FEEDBACK               (0.25f)
-//
-//#define AL_CHORUS_MIN_DELAY                      (0.0f)
-//#define AL_CHORUS_MAX_DELAY                      (0.016f)
-//#define AL_CHORUS_DEFAULT_DELAY                  (0.016f)
-//
-///* Distortion effect */
-//#define AL_DISTORTION_MIN_EDGE                   (0.0f)
-//#define AL_DISTORTION_MAX_EDGE                   (1.0f)
-//#define AL_DISTORTION_DEFAULT_EDGE               (0.2f)
-//
-//#define AL_DISTORTION_MIN_GAIN                   (0.01f)
-//#define AL_DISTORTION_MAX_GAIN                   (1.0f)
-//#define AL_DISTORTION_DEFAULT_GAIN               (0.05f)
-//
-//#define AL_DISTORTION_MIN_LOWPASS_CUTOFF         (80.0f)
-//#define AL_DISTORTION_MAX_LOWPASS_CUTOFF         (24000.0f)
-//#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF     (8000.0f)
-//
-//#define AL_DISTORTION_MIN_EQCENTER               (80.0f)
-//#define AL_DISTORTION_MAX_EQCENTER               (24000.0f)
-//#define AL_DISTORTION_DEFAULT_EQCENTER           (3600.0f)
-//
-//#define AL_DISTORTION_MIN_EQBANDWIDTH            (80.0f)
-//#define AL_DISTORTION_MAX_EQBANDWIDTH            (24000.0f)
-//#define AL_DISTORTION_DEFAULT_EQBANDWIDTH        (3600.0f)
-//
-///* Echo effect */
-//#define AL_ECHO_MIN_DELAY                        (0.0f)
-//#define AL_ECHO_MAX_DELAY                        (0.207f)
-//#define AL_ECHO_DEFAULT_DELAY                    (0.1f)
-//
-//#define AL_ECHO_MIN_LRDELAY                      (0.0f)
-//#define AL_ECHO_MAX_LRDELAY                      (0.404f)
-//#define AL_ECHO_DEFAULT_LRDELAY                  (0.1f)
-//
-//#define AL_ECHO_MIN_DAMPING                      (0.0f)
-//#define AL_ECHO_MAX_DAMPING                      (0.99f)
-//#define AL_ECHO_DEFAULT_DAMPING                  (0.5f)
-//
-//#define AL_ECHO_MIN_FEEDBACK                     (0.0f)
-//#define AL_ECHO_MAX_FEEDBACK                     (1.0f)
-//#define AL_ECHO_DEFAULT_FEEDBACK                 (0.5f)
-//
-//#define AL_ECHO_MIN_SPREAD                       (-1.0f)
-//#define AL_ECHO_MAX_SPREAD                       (1.0f)
-//#define AL_ECHO_DEFAULT_SPREAD                   (-1.0f)
-//
-///* Flanger effect */
-//#define AL_FLANGER_WAVEFORM_SINUSOID             (0)
-//#define AL_FLANGER_WAVEFORM_TRIANGLE             (1)
-//
-//#define AL_FLANGER_MIN_WAVEFORM                  (0)
-//#define AL_FLANGER_MAX_WAVEFORM                  (1)
-//#define AL_FLANGER_DEFAULT_WAVEFORM              (1)
-//
-//#define AL_FLANGER_MIN_PHASE                     (-180)
-//#define AL_FLANGER_MAX_PHASE                     (180)
-//#define AL_FLANGER_DEFAULT_PHASE                 (0)
-//
-//#define AL_FLANGER_MIN_RATE                      (0.0f)
-//#define AL_FLANGER_MAX_RATE                      (10.0f)
-//#define AL_FLANGER_DEFAULT_RATE                  (0.27f)
-//
-//#define AL_FLANGER_MIN_DEPTH                     (0.0f)
-//#define AL_FLANGER_MAX_DEPTH                     (1.0f)
-//#define AL_FLANGER_DEFAULT_DEPTH                 (1.0f)
-//
-//#define AL_FLANGER_MIN_FEEDBACK                  (-1.0f)
-//#define AL_FLANGER_MAX_FEEDBACK                  (1.0f)
-//#define AL_FLANGER_DEFAULT_FEEDBACK              (-0.5f)
-//
-//#define AL_FLANGER_MIN_DELAY                     (0.0f)
-//#define AL_FLANGER_MAX_DELAY                     (0.004f)
-//#define AL_FLANGER_DEFAULT_DELAY                 (0.002f)
-//
-///* Frequency shifter effect */
-//#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY       (0.0f)
-//#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY       (24000.0f)
-//#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY   (0.0f)
-//
-//#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION  (0)
-//#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION  (2)
-//#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0)
-//
-//#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN      (0)
-//#define AL_FREQUENCY_SHIFTER_DIRECTION_UP        (1)
-//#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF       (2)
-//
-//#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0)
-//#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2)
-//#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0)
-//
-///* Vocal morpher effect */
-//#define AL_VOCAL_MORPHER_MIN_PHONEMEA            (0)
-//#define AL_VOCAL_MORPHER_MAX_PHONEMEA            (29)
-//#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA        (0)
-//
-//#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24)
-//#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24)
-//#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0)
-//
-//#define AL_VOCAL_MORPHER_MIN_PHONEMEB            (0)
-//#define AL_VOCAL_MORPHER_MAX_PHONEMEB            (29)
-//#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB        (10)
-//
-//#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24)
-//#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24)
-//#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0)
-//
-//#define AL_VOCAL_MORPHER_PHONEME_A               (0)
-//#define AL_VOCAL_MORPHER_PHONEME_E               (1)
-//#define AL_VOCAL_MORPHER_PHONEME_I               (2)
-//#define AL_VOCAL_MORPHER_PHONEME_O               (3)
-//#define AL_VOCAL_MORPHER_PHONEME_U               (4)
-//#define AL_VOCAL_MORPHER_PHONEME_AA              (5)
-//#define AL_VOCAL_MORPHER_PHONEME_AE              (6)
-//#define AL_VOCAL_MORPHER_PHONEME_AH              (7)
-//#define AL_VOCAL_MORPHER_PHONEME_AO              (8)
-//#define AL_VOCAL_MORPHER_PHONEME_EH              (9)
-//#define AL_VOCAL_MORPHER_PHONEME_ER              (10)
-//#define AL_VOCAL_MORPHER_PHONEME_IH              (11)
-//#define AL_VOCAL_MORPHER_PHONEME_IY              (12)
-//#define AL_VOCAL_MORPHER_PHONEME_UH              (13)
-//#define AL_VOCAL_MORPHER_PHONEME_UW              (14)
-//#define AL_VOCAL_MORPHER_PHONEME_B               (15)
-//#define AL_VOCAL_MORPHER_PHONEME_D               (16)
-//#define AL_VOCAL_MORPHER_PHONEME_F               (17)
-//#define AL_VOCAL_MORPHER_PHONEME_G               (18)
-//#define AL_VOCAL_MORPHER_PHONEME_J               (19)
-//#define AL_VOCAL_MORPHER_PHONEME_K               (20)
-//#define AL_VOCAL_MORPHER_PHONEME_L               (21)
-//#define AL_VOCAL_MORPHER_PHONEME_M               (22)
-//#define AL_VOCAL_MORPHER_PHONEME_N               (23)
-//#define AL_VOCAL_MORPHER_PHONEME_P               (24)
-//#define AL_VOCAL_MORPHER_PHONEME_R               (25)
-//#define AL_VOCAL_MORPHER_PHONEME_S               (26)
-//#define AL_VOCAL_MORPHER_PHONEME_T               (27)
-//#define AL_VOCAL_MORPHER_PHONEME_V               (28)
-//#define AL_VOCAL_MORPHER_PHONEME_Z               (29)
-//
-//#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID       (0)
-//#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE       (1)
-//#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH       (2)
-//
-//#define AL_VOCAL_MORPHER_MIN_WAVEFORM            (0)
-//#define AL_VOCAL_MORPHER_MAX_WAVEFORM            (2)
-//#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM        (0)
-//
-//#define AL_VOCAL_MORPHER_MIN_RATE                (0.0f)
-//#define AL_VOCAL_MORPHER_MAX_RATE                (10.0f)
-//#define AL_VOCAL_MORPHER_DEFAULT_RATE            (1.41f)
-//
-///* Pitch shifter effect */
-//#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE         (-12)
-//#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE         (12)
-//#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE     (12)
-//
-//#define AL_PITCH_SHIFTER_MIN_FINE_TUNE           (-50)
-//#define AL_PITCH_SHIFTER_MAX_FINE_TUNE           (50)
-//#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE       (0)
-//
-///* Ring modulator effect */
-//#define AL_RING_MODULATOR_MIN_FREQUENCY          (0.0f)
-//#define AL_RING_MODULATOR_MAX_FREQUENCY          (8000.0f)
-//#define AL_RING_MODULATOR_DEFAULT_FREQUENCY      (440.0f)
-//
-//#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF    (0.0f)
-//#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF    (24000.0f)
-//#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f)
-//
-//#define AL_RING_MODULATOR_SINUSOID               (0)
-//#define AL_RING_MODULATOR_SAWTOOTH               (1)
-//#define AL_RING_MODULATOR_SQUARE                 (2)
-//
-//#define AL_RING_MODULATOR_MIN_WAVEFORM           (0)
-//#define AL_RING_MODULATOR_MAX_WAVEFORM           (2)
-//#define AL_RING_MODULATOR_DEFAULT_WAVEFORM       (0)
-//
-///* Autowah effect */
-//#define AL_AUTOWAH_MIN_ATTACK_TIME               (0.0001f)
-//#define AL_AUTOWAH_MAX_ATTACK_TIME               (1.0f)
-//#define AL_AUTOWAH_DEFAULT_ATTACK_TIME           (0.06f)
-//
-//#define AL_AUTOWAH_MIN_RELEASE_TIME              (0.0001f)
-//#define AL_AUTOWAH_MAX_RELEASE_TIME              (1.0f)
-//#define AL_AUTOWAH_DEFAULT_RELEASE_TIME          (0.06f)
-//
-//#define AL_AUTOWAH_MIN_RESONANCE                 (2.0f)
-//#define AL_AUTOWAH_MAX_RESONANCE                 (1000.0f)
-//#define AL_AUTOWAH_DEFAULT_RESONANCE             (1000.0f)
-//
-//#define AL_AUTOWAH_MIN_PEAK_GAIN                 (0.00003f)
-//#define AL_AUTOWAH_MAX_PEAK_GAIN                 (31621.0f)
-//#define AL_AUTOWAH_DEFAULT_PEAK_GAIN             (11.22f)
-//
-///* Compressor effect */
-//#define AL_COMPRESSOR_MIN_ONOFF                  (0)
-//#define AL_COMPRESSOR_MAX_ONOFF                  (1)
-//#define AL_COMPRESSOR_DEFAULT_ONOFF              (1)
-//
-///* Equalizer effect */
-//#define AL_EQUALIZER_MIN_LOW_GAIN                (0.126f)
-//#define AL_EQUALIZER_MAX_LOW_GAIN                (7.943f)
-//#define AL_EQUALIZER_DEFAULT_LOW_GAIN            (1.0f)
-//
-//#define AL_EQUALIZER_MIN_LOW_CUTOFF              (50.0f)
-//#define AL_EQUALIZER_MAX_LOW_CUTOFF              (800.0f)
-//#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF          (200.0f)
-//
-//#define AL_EQUALIZER_MIN_MID1_GAIN               (0.126f)
-//#define AL_EQUALIZER_MAX_MID1_GAIN               (7.943f)
-//#define AL_EQUALIZER_DEFAULT_MID1_GAIN           (1.0f)
-//
-//#define AL_EQUALIZER_MIN_MID1_CENTER             (200.0f)
-//#define AL_EQUALIZER_MAX_MID1_CENTER             (3000.0f)
-//#define AL_EQUALIZER_DEFAULT_MID1_CENTER         (500.0f)
-//
-//#define AL_EQUALIZER_MIN_MID1_WIDTH              (0.01f)
-//#define AL_EQUALIZER_MAX_MID1_WIDTH              (1.0f)
-//#define AL_EQUALIZER_DEFAULT_MID1_WIDTH          (1.0f)
-//
-//#define AL_EQUALIZER_MIN_MID2_GAIN               (0.126f)
-//#define AL_EQUALIZER_MAX_MID2_GAIN               (7.943f)
-//#define AL_EQUALIZER_DEFAULT_MID2_GAIN           (1.0f)
-//
-//#define AL_EQUALIZER_MIN_MID2_CENTER             (1000.0f)
-//#define AL_EQUALIZER_MAX_MID2_CENTER             (8000.0f)
-//#define AL_EQUALIZER_DEFAULT_MID2_CENTER         (3000.0f)
-//
-//#define AL_EQUALIZER_MIN_MID2_WIDTH              (0.01f)
-//#define AL_EQUALIZER_MAX_MID2_WIDTH              (1.0f)
-//#define AL_EQUALIZER_DEFAULT_MID2_WIDTH          (1.0f)
-//
-//#define AL_EQUALIZER_MIN_HIGH_GAIN               (0.126f)
-//#define AL_EQUALIZER_MAX_HIGH_GAIN               (7.943f)
-//#define AL_EQUALIZER_DEFAULT_HIGH_GAIN           (1.0f)
-//
-//#define AL_EQUALIZER_MIN_HIGH_CUTOFF             (4000.0f)
-//#define AL_EQUALIZER_MAX_HIGH_CUTOFF             (16000.0f)
-//#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF         (6000.0f)
-//
-//
-///* Source parameter value ranges and defaults. */
-//#define AL_MIN_AIR_ABSORPTION_FACTOR             (0.0f)
-//#define AL_MAX_AIR_ABSORPTION_FACTOR             (10.0f)
-//#define AL_DEFAULT_AIR_ABSORPTION_FACTOR         (0.0f)
-//
-//#define AL_MIN_ROOM_ROLLOFF_FACTOR               (0.0f)
-//#define AL_MAX_ROOM_ROLLOFF_FACTOR               (10.0f)
-//#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR           (0.0f)
-//
-//#define AL_MIN_CONE_OUTER_GAINHF                 (0.0f)
-//#define AL_MAX_CONE_OUTER_GAINHF                 (1.0f)
-//#define AL_DEFAULT_CONE_OUTER_GAINHF             (1.0f)
-//
-//#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO         AL_FALSE
-//#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO         AL_TRUE
-//#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO     AL_TRUE
-//
-//#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO   AL_FALSE
-//#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO   AL_TRUE
-//#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE
-//
-//#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE
-//#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
-//#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
-//
+    public static final String ALC_EXT_EFX_NAME = "ALC_EXT_EFX";
 
+    public static final int ALC_EFX_MAJOR_VERSION = 0x20001;
+    public static final int ALC_EFX_MINOR_VERSION = 0x20002;
+    public static final int ALC_MAX_AUXILIARY_SENDS = 0x20003;
+
+    /* Listener properties. */
+    //#define AL_METERS_PER_UNIT                       0x20004
+
+    /* Source properties. */
+    public static final int AL_DIRECT_FILTER = 0x20005;
+    public static final int AL_AUXILIARY_SEND_FILTER = 0x20006;
+    //#define AL_AIR_ABSORPTION_FACTOR                 0x20007
+    //#define AL_ROOM_ROLLOFF_FACTOR                   0x20008
+    //#define AL_CONE_OUTER_GAINHF                     0x20009
+
+    public static final int AL_DIRECT_FILTER_GAINHF_AUTO = 0x2000A;
+    //#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO       0x2000B
+    //#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO     0x2000C
+
+    /* Effect properties. */
+
+    /* Reverb effect parameters */
+    public static final int AL_REVERB_DENSITY = 0x0001;
+    public static final int AL_REVERB_DIFFUSION = 0x0002;
+    public static final int AL_REVERB_GAIN = 0x0003;
+    public static final int AL_REVERB_GAINHF = 0x0004;
+    public static final int AL_REVERB_DECAY_TIME = 0x0005;
+    public static final int AL_REVERB_DECAY_HFRATIO = 0x0006;
+    public static final int AL_REVERB_REFLECTIONS_GAIN = 0x0007;
+    public static final int AL_REVERB_REFLECTIONS_DELAY = 0x0008;
+    public static final int AL_REVERB_LATE_REVERB_GAIN = 0x0009;
+    public static final int AL_REVERB_LATE_REVERB_DELAY = 0x000A;
+    public static final int AL_REVERB_AIR_ABSORPTION_GAINHF = 0x000B;
+    public static final int AL_REVERB_ROOM_ROLLOFF_FACTOR = 0x000C;
+    public static final int AL_REVERB_DECAY_HFLIMIT = 0x000D;
+
+    /* EAX Reverb effect parameters */
+    //#define AL_EAXREVERB_DENSITY                     0x0001
+    //#define AL_EAXREVERB_DIFFUSION                   0x0002
+    //#define AL_EAXREVERB_GAIN                        0x0003
+    //#define AL_EAXREVERB_GAINHF                      0x0004
+    //#define AL_EAXREVERB_GAINLF                      0x0005
+    //#define AL_EAXREVERB_DECAY_TIME                  0x0006
+    //#define AL_EAXREVERB_DECAY_HFRATIO               0x0007
+    //#define AL_EAXREVERB_DECAY_LFRATIO               0x0008
+    //#define AL_EAXREVERB_REFLECTIONS_GAIN            0x0009
+    //#define AL_EAXREVERB_REFLECTIONS_DELAY           0x000A
+    //#define AL_EAXREVERB_REFLECTIONS_PAN             0x000B
+    //#define AL_EAXREVERB_LATE_REVERB_GAIN            0x000C
+    //#define AL_EAXREVERB_LATE_REVERB_DELAY           0x000D
+    //#define AL_EAXREVERB_LATE_REVERB_PAN             0x000E
+    //#define AL_EAXREVERB_ECHO_TIME                   0x000F
+    //#define AL_EAXREVERB_ECHO_DEPTH                  0x0010
+    //#define AL_EAXREVERB_MODULATION_TIME             0x0011
+    //#define AL_EAXREVERB_MODULATION_DEPTH            0x0012
+    //#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF       0x0013
+    //#define AL_EAXREVERB_HFREFERENCE                 0x0014
+    //#define AL_EAXREVERB_LFREFERENCE                 0x0015
+    //#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR         0x0016
+    //#define AL_EAXREVERB_DECAY_HFLIMIT               0x0017
+    //
+    ///* Chorus effect parameters */
+    //#define AL_CHORUS_WAVEFORM                       0x0001
+    //#define AL_CHORUS_PHASE                          0x0002
+    //#define AL_CHORUS_RATE                           0x0003
+    //#define AL_CHORUS_DEPTH                          0x0004
+    //#define AL_CHORUS_FEEDBACK                       0x0005
+    //#define AL_CHORUS_DELAY                          0x0006
+    //
+    ///* Distortion effect parameters */
+    //#define AL_DISTORTION_EDGE                       0x0001
+    //#define AL_DISTORTION_GAIN                       0x0002
+    //#define AL_DISTORTION_LOWPASS_CUTOFF             0x0003
+    //#define AL_DISTORTION_EQCENTER                   0x0004
+    //#define AL_DISTORTION_EQBANDWIDTH                0x0005
+    //
+    ///* Echo effect parameters */
+    //#define AL_ECHO_DELAY                            0x0001
+    //#define AL_ECHO_LRDELAY                          0x0002
+    //#define AL_ECHO_DAMPING                          0x0003
+    //#define AL_ECHO_FEEDBACK                         0x0004
+    //#define AL_ECHO_SPREAD                           0x0005
+    //
+    ///* Flanger effect parameters */
+    //#define AL_FLANGER_WAVEFORM                      0x0001
+    //#define AL_FLANGER_PHASE                         0x0002
+    //#define AL_FLANGER_RATE                          0x0003
+    //#define AL_FLANGER_DEPTH                         0x0004
+    //#define AL_FLANGER_FEEDBACK                      0x0005
+    //#define AL_FLANGER_DELAY                         0x0006
+    //
+    ///* Frequency shifter effect parameters */
+    //#define AL_FREQUENCY_SHIFTER_FREQUENCY           0x0001
+    //#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION      0x0002
+    //#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION     0x0003
+    //
+    ///* Vocal morpher effect parameters */
+    //#define AL_VOCAL_MORPHER_PHONEMEA                0x0001
+    //#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING  0x0002
+    //#define AL_VOCAL_MORPHER_PHONEMEB                0x0003
+    //#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING  0x0004
+    //#define AL_VOCAL_MORPHER_WAVEFORM                0x0005
+    //#define AL_VOCAL_MORPHER_RATE                    0x0006
+    //
+    ///* Pitchshifter effect parameters */
+    //#define AL_PITCH_SHIFTER_COARSE_TUNE             0x0001
+    //#define AL_PITCH_SHIFTER_FINE_TUNE               0x0002
+    //
+    ///* Ringmodulator effect parameters */
+    //#define AL_RING_MODULATOR_FREQUENCY              0x0001
+    //#define AL_RING_MODULATOR_HIGHPASS_CUTOFF        0x0002
+    //#define AL_RING_MODULATOR_WAVEFORM               0x0003
+    //
+    ///* Autowah effect parameters */
+    //#define AL_AUTOWAH_ATTACK_TIME                   0x0001
+    //#define AL_AUTOWAH_RELEASE_TIME                  0x0002
+    //#define AL_AUTOWAH_RESONANCE                     0x0003
+    //#define AL_AUTOWAH_PEAK_GAIN                     0x0004
+    //
+    ///* Compressor effect parameters */
+    //#define AL_COMPRESSOR_ONOFF                      0x0001
+    //
+    ///* Equalizer effect parameters */
+    //#define AL_EQUALIZER_LOW_GAIN                    0x0001
+    //#define AL_EQUALIZER_LOW_CUTOFF                  0x0002
+    //#define AL_EQUALIZER_MID1_GAIN                   0x0003
+    //#define AL_EQUALIZER_MID1_CENTER                 0x0004
+    //#define AL_EQUALIZER_MID1_WIDTH                  0x0005
+    //#define AL_EQUALIZER_MID2_GAIN                   0x0006
+    //#define AL_EQUALIZER_MID2_CENTER                 0x0007
+    //#define AL_EQUALIZER_MID2_WIDTH                  0x0008
+    //#define AL_EQUALIZER_HIGH_GAIN                   0x0009
+    //#define AL_EQUALIZER_HIGH_CUTOFF                 0x000A
+
+    /* Effect type */
+    //#define AL_EFFECT_FIRST_PARAMETER                0x0000
+    //#define AL_EFFECT_LAST_PARAMETER                 0x8000
+    public static final int AL_EFFECT_TYPE = 0x8001;
+
+    /* Effect types, used with the AL_EFFECT_TYPE property */
+    //#define AL_EFFECT_NULL                           0x0000
+    public static final int AL_EFFECT_REVERB = 0x0001;
+    //#define AL_EFFECT_CHORUS                         0x0002
+    //#define AL_EFFECT_DISTORTION                     0x0003
+    //#define AL_EFFECT_ECHO                           0x0004
+    //#define AL_EFFECT_FLANGER                        0x0005
+    //#define AL_EFFECT_FREQUENCY_SHIFTER              0x0006
+    //#define AL_EFFECT_VOCAL_MORPHER                  0x0007
+    //#define AL_EFFECT_PITCH_SHIFTER                  0x0008
+    //#define AL_EFFECT_RING_MODULATOR                 0x0009
+    //#define AL_EFFECT_AUTOWAH                        0x000A
+    //#define AL_EFFECT_COMPRESSOR                     0x000B
+    //#define AL_EFFECT_EQUALIZER                      0x000C
+    //#define AL_EFFECT_EAXREVERB                      0x8000
+
+    /* Auxiliary Effect Slot properties. */
+    public static final int AL_EFFECTSLOT_EFFECT = 0x0001;
+    //#define AL_EFFECTSLOT_GAIN                       0x0002
+    //#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO        0x0003
+
+    ///* NULL Auxiliary Slot ID to disable a source send. */
+    //#define AL_EFFECTSLOT_NULL                       0x0000
+
+    ///* Filter properties. */
+
+    /* Lowpass filter parameters */
+    public static final int AL_LOWPASS_GAIN = 0x0001;
+    public static final int AL_LOWPASS_GAINHF = 0x0002;
+
+    ///* Highpass filter parameters */
+    //#define AL_HIGHPASS_GAIN                         0x0001
+    //#define AL_HIGHPASS_GAINLF                       0x0002
+
+    ///* Bandpass filter parameters */
+    //#define AL_BANDPASS_GAIN                         0x0001
+    //#define AL_BANDPASS_GAINLF                       0x0002
+    //#define AL_BANDPASS_GAINHF                       0x0003
+
+    /* Filter type */
+    //#define AL_FILTER_FIRST_PARAMETER                0x0000
+    //#define AL_FILTER_LAST_PARAMETER                 0x8000
+    public static final int AL_FILTER_TYPE = 0x8001;
+
+    /* Filter types, used with the AL_FILTER_TYPE property */
+    public static final int AL_FILTER_NULL = 0x0000;
+    public static final int AL_FILTER_LOWPASS = 0x0001;
+    public static final int AL_FILTER_HIGHPASS = 0x0002;
+    //#define AL_FILTER_BANDPASS                       0x0003
+
+    ///* Filter ranges and defaults. */
+    //
+    ///* Lowpass filter */
+    //#define AL_LOWPASS_MIN_GAIN                      (0.0f)
+    //#define AL_LOWPASS_MAX_GAIN                      (1.0f)
+    //#define AL_LOWPASS_DEFAULT_GAIN                  (1.0f)
+    //
+    //#define AL_LOWPASS_MIN_GAINHF                    (0.0f)
+    //#define AL_LOWPASS_MAX_GAINHF                    (1.0f)
+    //#define AL_LOWPASS_DEFAULT_GAINHF                (1.0f)
+    //
+    ///* Highpass filter */
+    //#define AL_HIGHPASS_MIN_GAIN                     (0.0f)
+    //#define AL_HIGHPASS_MAX_GAIN                     (1.0f)
+    //#define AL_HIGHPASS_DEFAULT_GAIN                 (1.0f)
+    //
+    //#define AL_HIGHPASS_MIN_GAINLF                   (0.0f)
+    //#define AL_HIGHPASS_MAX_GAINLF                   (1.0f)
+    //#define AL_HIGHPASS_DEFAULT_GAINLF               (1.0f)
+    //
+    ///* Bandpass filter */
+    //#define AL_BANDPASS_MIN_GAIN                     (0.0f)
+    //#define AL_BANDPASS_MAX_GAIN                     (1.0f)
+    //#define AL_BANDPASS_DEFAULT_GAIN                 (1.0f)
+    //
+    //#define AL_BANDPASS_MIN_GAINHF                   (0.0f)
+    //#define AL_BANDPASS_MAX_GAINHF                   (1.0f)
+    //#define AL_BANDPASS_DEFAULT_GAINHF               (1.0f)
+    //
+    //#define AL_BANDPASS_MIN_GAINLF                   (0.0f)
+    //#define AL_BANDPASS_MAX_GAINLF                   (1.0f)
+    //#define AL_BANDPASS_DEFAULT_GAINLF               (1.0f)
+    //
+    //
+    ///* Effect parameter ranges and defaults. */
+    //
+    ///* Standard reverb effect */
+    //#define AL_REVERB_MIN_DENSITY                    (0.0f)
+    //#define AL_REVERB_MAX_DENSITY                    (1.0f)
+    //#define AL_REVERB_DEFAULT_DENSITY                (1.0f)
+    //
+    //#define AL_REVERB_MIN_DIFFUSION                  (0.0f)
+    //#define AL_REVERB_MAX_DIFFUSION                  (1.0f)
+    //#define AL_REVERB_DEFAULT_DIFFUSION              (1.0f)
+    //
+    //#define AL_REVERB_MIN_GAIN                       (0.0f)
+    //#define AL_REVERB_MAX_GAIN                       (1.0f)
+    //#define AL_REVERB_DEFAULT_GAIN                   (0.32f)
+    //
+    //#define AL_REVERB_MIN_GAINHF                     (0.0f)
+    //#define AL_REVERB_MAX_GAINHF                     (1.0f)
+    //#define AL_REVERB_DEFAULT_GAINHF                 (0.89f)
+    //
+    //#define AL_REVERB_MIN_DECAY_TIME                 (0.1f)
+    //#define AL_REVERB_MAX_DECAY_TIME                 (20.0f)
+    //#define AL_REVERB_DEFAULT_DECAY_TIME             (1.49f)
+    //
+    //#define AL_REVERB_MIN_DECAY_HFRATIO              (0.1f)
+    //#define AL_REVERB_MAX_DECAY_HFRATIO              (2.0f)
+    //#define AL_REVERB_DEFAULT_DECAY_HFRATIO          (0.83f)
+    //
+    //#define AL_REVERB_MIN_REFLECTIONS_GAIN           (0.0f)
+    //#define AL_REVERB_MAX_REFLECTIONS_GAIN           (3.16f)
+    //#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN       (0.05f)
+    //
+    //#define AL_REVERB_MIN_REFLECTIONS_DELAY          (0.0f)
+    //#define AL_REVERB_MAX_REFLECTIONS_DELAY          (0.3f)
+    //#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY      (0.007f)
+    //
+    //#define AL_REVERB_MIN_LATE_REVERB_GAIN           (0.0f)
+    //#define AL_REVERB_MAX_LATE_REVERB_GAIN           (10.0f)
+    //#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN       (1.26f)
+    //
+    //#define AL_REVERB_MIN_LATE_REVERB_DELAY          (0.0f)
+    //#define AL_REVERB_MAX_LATE_REVERB_DELAY          (0.1f)
+    //#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY      (0.011f)
+    //
+    //#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF      (0.892f)
+    //#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF      (1.0f)
+    //#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF  (0.994f)
+    //
+    //#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR        (0.0f)
+    //#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR        (10.0f)
+    //#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR    (0.0f)
+    //
+    //#define AL_REVERB_MIN_DECAY_HFLIMIT              AL_FALSE
+    //#define AL_REVERB_MAX_DECAY_HFLIMIT              AL_TRUE
+    //#define AL_REVERB_DEFAULT_DECAY_HFLIMIT          AL_TRUE
+    //
+    ///* EAX reverb effect */
+    //#define AL_EAXREVERB_MIN_DENSITY                 (0.0f)
+    //#define AL_EAXREVERB_MAX_DENSITY                 (1.0f)
+    //#define AL_EAXREVERB_DEFAULT_DENSITY             (1.0f)
+    //
+    //#define AL_EAXREVERB_MIN_DIFFUSION               (0.0f)
+    //#define AL_EAXREVERB_MAX_DIFFUSION               (1.0f)
+    //#define AL_EAXREVERB_DEFAULT_DIFFUSION           (1.0f)
+    //
+    //#define AL_EAXREVERB_MIN_GAIN                    (0.0f)
+    //#define AL_EAXREVERB_MAX_GAIN                    (1.0f)
+    //#define AL_EAXREVERB_DEFAULT_GAIN                (0.32f)
+    //
+    //#define AL_EAXREVERB_MIN_GAINHF                  (0.0f)
+    //#define AL_EAXREVERB_MAX_GAINHF                  (1.0f)
+    //#define AL_EAXREVERB_DEFAULT_GAINHF              (0.89f)
+    //
+    //#define AL_EAXREVERB_MIN_GAINLF                  (0.0f)
+    //#define AL_EAXREVERB_MAX_GAINLF                  (1.0f)
+    //#define AL_EAXREVERB_DEFAULT_GAINLF              (1.0f)
+    //
+    //#define AL_EAXREVERB_MIN_DECAY_TIME              (0.1f)
+    //#define AL_EAXREVERB_MAX_DECAY_TIME              (20.0f)
+    //#define AL_EAXREVERB_DEFAULT_DECAY_TIME          (1.49f)
+    //
+    //#define AL_EAXREVERB_MIN_DECAY_HFRATIO           (0.1f)
+    //#define AL_EAXREVERB_MAX_DECAY_HFRATIO           (2.0f)
+    //#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO       (0.83f)
+    //
+    //#define AL_EAXREVERB_MIN_DECAY_LFRATIO           (0.1f)
+    //#define AL_EAXREVERB_MAX_DECAY_LFRATIO           (2.0f)
+    //#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO       (1.0f)
+    //
+    //#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN        (0.0f)
+    //#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN        (3.16f)
+    //#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN    (0.05f)
+    //
+    //#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY       (0.0f)
+    //#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY       (0.3f)
+    //#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY   (0.007f)
+    //
+    //#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f)
+    //
+    //#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN        (0.0f)
+    //#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN        (10.0f)
+    //#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN    (1.26f)
+    //
+    //#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY       (0.0f)
+    //#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY       (0.1f)
+    //#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY   (0.011f)
+    //
+    //#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f)
+    //
+    //#define AL_EAXREVERB_MIN_ECHO_TIME               (0.075f)
+    //#define AL_EAXREVERB_MAX_ECHO_TIME               (0.25f)
+    //#define AL_EAXREVERB_DEFAULT_ECHO_TIME           (0.25f)
+    //
+    //#define AL_EAXREVERB_MIN_ECHO_DEPTH              (0.0f)
+    //#define AL_EAXREVERB_MAX_ECHO_DEPTH              (1.0f)
+    //#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH          (0.0f)
+    //
+    //#define AL_EAXREVERB_MIN_MODULATION_TIME         (0.04f)
+    //#define AL_EAXREVERB_MAX_MODULATION_TIME         (4.0f)
+    //#define AL_EAXREVERB_DEFAULT_MODULATION_TIME     (0.25f)
+    //
+    //#define AL_EAXREVERB_MIN_MODULATION_DEPTH        (0.0f)
+    //#define AL_EAXREVERB_MAX_MODULATION_DEPTH        (1.0f)
+    //#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH    (0.0f)
+    //
+    //#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF   (0.892f)
+    //#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF   (1.0f)
+    //#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
+    //
+    //#define AL_EAXREVERB_MIN_HFREFERENCE             (1000.0f)
+    //#define AL_EAXREVERB_MAX_HFREFERENCE             (20000.0f)
+    //#define AL_EAXREVERB_DEFAULT_HFREFERENCE         (5000.0f)
+    //
+    //#define AL_EAXREVERB_MIN_LFREFERENCE             (20.0f)
+    //#define AL_EAXREVERB_MAX_LFREFERENCE             (1000.0f)
+    //#define AL_EAXREVERB_DEFAULT_LFREFERENCE         (250.0f)
+    //
+    //#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR     (0.0f)
+    //#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR     (10.0f)
+    //#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
+    //
+    //#define AL_EAXREVERB_MIN_DECAY_HFLIMIT           AL_FALSE
+    //#define AL_EAXREVERB_MAX_DECAY_HFLIMIT           AL_TRUE
+    //#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT       AL_TRUE
+    //
+    ///* Chorus effect */
+    //#define AL_CHORUS_WAVEFORM_SINUSOID              (0)
+    //#define AL_CHORUS_WAVEFORM_TRIANGLE              (1)
+    //
+    //#define AL_CHORUS_MIN_WAVEFORM                   (0)
+    //#define AL_CHORUS_MAX_WAVEFORM                   (1)
+    //#define AL_CHORUS_DEFAULT_WAVEFORM               (1)
+    //
+    //#define AL_CHORUS_MIN_PHASE                      (-180)
+    //#define AL_CHORUS_MAX_PHASE                      (180)
+    //#define AL_CHORUS_DEFAULT_PHASE                  (90)
+    //
+    //#define AL_CHORUS_MIN_RATE                       (0.0f)
+    //#define AL_CHORUS_MAX_RATE                       (10.0f)
+    //#define AL_CHORUS_DEFAULT_RATE                   (1.1f)
+    //
+    //#define AL_CHORUS_MIN_DEPTH                      (0.0f)
+    //#define AL_CHORUS_MAX_DEPTH                      (1.0f)
+    //#define AL_CHORUS_DEFAULT_DEPTH                  (0.1f)
+    //
+    //#define AL_CHORUS_MIN_FEEDBACK                   (-1.0f)
+    //#define AL_CHORUS_MAX_FEEDBACK                   (1.0f)
+    //#define AL_CHORUS_DEFAULT_FEEDBACK               (0.25f)
+    //
+    //#define AL_CHORUS_MIN_DELAY                      (0.0f)
+    //#define AL_CHORUS_MAX_DELAY                      (0.016f)
+    //#define AL_CHORUS_DEFAULT_DELAY                  (0.016f)
+    //
+    ///* Distortion effect */
+    //#define AL_DISTORTION_MIN_EDGE                   (0.0f)
+    //#define AL_DISTORTION_MAX_EDGE                   (1.0f)
+    //#define AL_DISTORTION_DEFAULT_EDGE               (0.2f)
+    //
+    //#define AL_DISTORTION_MIN_GAIN                   (0.01f)
+    //#define AL_DISTORTION_MAX_GAIN                   (1.0f)
+    //#define AL_DISTORTION_DEFAULT_GAIN               (0.05f)
+    //
+    //#define AL_DISTORTION_MIN_LOWPASS_CUTOFF         (80.0f)
+    //#define AL_DISTORTION_MAX_LOWPASS_CUTOFF         (24000.0f)
+    //#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF     (8000.0f)
+    //
+    //#define AL_DISTORTION_MIN_EQCENTER               (80.0f)
+    //#define AL_DISTORTION_MAX_EQCENTER               (24000.0f)
+    //#define AL_DISTORTION_DEFAULT_EQCENTER           (3600.0f)
+    //
+    //#define AL_DISTORTION_MIN_EQBANDWIDTH            (80.0f)
+    //#define AL_DISTORTION_MAX_EQBANDWIDTH            (24000.0f)
+    //#define AL_DISTORTION_DEFAULT_EQBANDWIDTH        (3600.0f)
+    //
+    ///* Echo effect */
+    //#define AL_ECHO_MIN_DELAY                        (0.0f)
+    //#define AL_ECHO_MAX_DELAY                        (0.207f)
+    //#define AL_ECHO_DEFAULT_DELAY                    (0.1f)
+    //
+    //#define AL_ECHO_MIN_LRDELAY                      (0.0f)
+    //#define AL_ECHO_MAX_LRDELAY                      (0.404f)
+    //#define AL_ECHO_DEFAULT_LRDELAY                  (0.1f)
+    //
+    //#define AL_ECHO_MIN_DAMPING                      (0.0f)
+    //#define AL_ECHO_MAX_DAMPING                      (0.99f)
+    //#define AL_ECHO_DEFAULT_DAMPING                  (0.5f)
+    //
+    //#define AL_ECHO_MIN_FEEDBACK                     (0.0f)
+    //#define AL_ECHO_MAX_FEEDBACK                     (1.0f)
+    //#define AL_ECHO_DEFAULT_FEEDBACK                 (0.5f)
+    //
+    //#define AL_ECHO_MIN_SPREAD                       (-1.0f)
+    //#define AL_ECHO_MAX_SPREAD                       (1.0f)
+    //#define AL_ECHO_DEFAULT_SPREAD                   (-1.0f)
+    //
+    ///* Flanger effect */
+    //#define AL_FLANGER_WAVEFORM_SINUSOID             (0)
+    //#define AL_FLANGER_WAVEFORM_TRIANGLE             (1)
+    //
+    //#define AL_FLANGER_MIN_WAVEFORM                  (0)
+    //#define AL_FLANGER_MAX_WAVEFORM                  (1)
+    //#define AL_FLANGER_DEFAULT_WAVEFORM              (1)
+    //
+    //#define AL_FLANGER_MIN_PHASE                     (-180)
+    //#define AL_FLANGER_MAX_PHASE                     (180)
+    //#define AL_FLANGER_DEFAULT_PHASE                 (0)
+    //
+    //#define AL_FLANGER_MIN_RATE                      (0.0f)
+    //#define AL_FLANGER_MAX_RATE                      (10.0f)
+    //#define AL_FLANGER_DEFAULT_RATE                  (0.27f)
+    //
+    //#define AL_FLANGER_MIN_DEPTH                     (0.0f)
+    //#define AL_FLANGER_MAX_DEPTH                     (1.0f)
+    //#define AL_FLANGER_DEFAULT_DEPTH                 (1.0f)
+    //
+    //#define AL_FLANGER_MIN_FEEDBACK                  (-1.0f)
+    //#define AL_FLANGER_MAX_FEEDBACK                  (1.0f)
+    //#define AL_FLANGER_DEFAULT_FEEDBACK              (-0.5f)
+    //
+    //#define AL_FLANGER_MIN_DELAY                     (0.0f)
+    //#define AL_FLANGER_MAX_DELAY                     (0.004f)
+    //#define AL_FLANGER_DEFAULT_DELAY                 (0.002f)
+    //
+    ///* Frequency shifter effect */
+    //#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY       (0.0f)
+    //#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY       (24000.0f)
+    //#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY   (0.0f)
+    //
+    //#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION  (0)
+    //#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION  (2)
+    //#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0)
+    //
+    //#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN      (0)
+    //#define AL_FREQUENCY_SHIFTER_DIRECTION_UP        (1)
+    //#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF       (2)
+    //
+    //#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0)
+    //#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2)
+    //#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0)
+    //
+    ///* Vocal morpher effect */
+    //#define AL_VOCAL_MORPHER_MIN_PHONEMEA            (0)
+    //#define AL_VOCAL_MORPHER_MAX_PHONEMEA            (29)
+    //#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA        (0)
+    //
+    //#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24)
+    //#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24)
+    //#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0)
+    //
+    //#define AL_VOCAL_MORPHER_MIN_PHONEMEB            (0)
+    //#define AL_VOCAL_MORPHER_MAX_PHONEMEB            (29)
+    //#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB        (10)
+    //
+    //#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24)
+    //#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24)
+    //#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0)
+    //
+    //#define AL_VOCAL_MORPHER_PHONEME_A               (0)
+    //#define AL_VOCAL_MORPHER_PHONEME_E               (1)
+    //#define AL_VOCAL_MORPHER_PHONEME_I               (2)
+    //#define AL_VOCAL_MORPHER_PHONEME_O               (3)
+    //#define AL_VOCAL_MORPHER_PHONEME_U               (4)
+    //#define AL_VOCAL_MORPHER_PHONEME_AA              (5)
+    //#define AL_VOCAL_MORPHER_PHONEME_AE              (6)
+    //#define AL_VOCAL_MORPHER_PHONEME_AH              (7)
+    //#define AL_VOCAL_MORPHER_PHONEME_AO              (8)
+    //#define AL_VOCAL_MORPHER_PHONEME_EH              (9)
+    //#define AL_VOCAL_MORPHER_PHONEME_ER              (10)
+    //#define AL_VOCAL_MORPHER_PHONEME_IH              (11)
+    //#define AL_VOCAL_MORPHER_PHONEME_IY              (12)
+    //#define AL_VOCAL_MORPHER_PHONEME_UH              (13)
+    //#define AL_VOCAL_MORPHER_PHONEME_UW              (14)
+    //#define AL_VOCAL_MORPHER_PHONEME_B               (15)
+    //#define AL_VOCAL_MORPHER_PHONEME_D               (16)
+    //#define AL_VOCAL_MORPHER_PHONEME_F               (17)
+    //#define AL_VOCAL_MORPHER_PHONEME_G               (18)
+    //#define AL_VOCAL_MORPHER_PHONEME_J               (19)
+    //#define AL_VOCAL_MORPHER_PHONEME_K               (20)
+    //#define AL_VOCAL_MORPHER_PHONEME_L               (21)
+    //#define AL_VOCAL_MORPHER_PHONEME_M               (22)
+    //#define AL_VOCAL_MORPHER_PHONEME_N               (23)
+    //#define AL_VOCAL_MORPHER_PHONEME_P               (24)
+    //#define AL_VOCAL_MORPHER_PHONEME_R               (25)
+    //#define AL_VOCAL_MORPHER_PHONEME_S               (26)
+    //#define AL_VOCAL_MORPHER_PHONEME_T               (27)
+    //#define AL_VOCAL_MORPHER_PHONEME_V               (28)
+    //#define AL_VOCAL_MORPHER_PHONEME_Z               (29)
+    //
+    //#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID       (0)
+    //#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE       (1)
+    //#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH       (2)
+    //
+    //#define AL_VOCAL_MORPHER_MIN_WAVEFORM            (0)
+    //#define AL_VOCAL_MORPHER_MAX_WAVEFORM            (2)
+    //#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM        (0)
+    //
+    //#define AL_VOCAL_MORPHER_MIN_RATE                (0.0f)
+    //#define AL_VOCAL_MORPHER_MAX_RATE                (10.0f)
+    //#define AL_VOCAL_MORPHER_DEFAULT_RATE            (1.41f)
+    //
+    ///* Pitch shifter effect */
+    //#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE         (-12)
+    //#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE         (12)
+    //#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE     (12)
+    //
+    //#define AL_PITCH_SHIFTER_MIN_FINE_TUNE           (-50)
+    //#define AL_PITCH_SHIFTER_MAX_FINE_TUNE           (50)
+    //#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE       (0)
+    //
+    ///* Ring modulator effect */
+    //#define AL_RING_MODULATOR_MIN_FREQUENCY          (0.0f)
+    //#define AL_RING_MODULATOR_MAX_FREQUENCY          (8000.0f)
+    //#define AL_RING_MODULATOR_DEFAULT_FREQUENCY      (440.0f)
+    //
+    //#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF    (0.0f)
+    //#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF    (24000.0f)
+    //#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f)
+    //
+    //#define AL_RING_MODULATOR_SINUSOID               (0)
+    //#define AL_RING_MODULATOR_SAWTOOTH               (1)
+    //#define AL_RING_MODULATOR_SQUARE                 (2)
+    //
+    //#define AL_RING_MODULATOR_MIN_WAVEFORM           (0)
+    //#define AL_RING_MODULATOR_MAX_WAVEFORM           (2)
+    //#define AL_RING_MODULATOR_DEFAULT_WAVEFORM       (0)
+    //
+    ///* Autowah effect */
+    //#define AL_AUTOWAH_MIN_ATTACK_TIME               (0.0001f)
+    //#define AL_AUTOWAH_MAX_ATTACK_TIME               (1.0f)
+    //#define AL_AUTOWAH_DEFAULT_ATTACK_TIME           (0.06f)
+    //
+    //#define AL_AUTOWAH_MIN_RELEASE_TIME              (0.0001f)
+    //#define AL_AUTOWAH_MAX_RELEASE_TIME              (1.0f)
+    //#define AL_AUTOWAH_DEFAULT_RELEASE_TIME          (0.06f)
+    //
+    //#define AL_AUTOWAH_MIN_RESONANCE                 (2.0f)
+    //#define AL_AUTOWAH_MAX_RESONANCE                 (1000.0f)
+    //#define AL_AUTOWAH_DEFAULT_RESONANCE             (1000.0f)
+    //
+    //#define AL_AUTOWAH_MIN_PEAK_GAIN                 (0.00003f)
+    //#define AL_AUTOWAH_MAX_PEAK_GAIN                 (31621.0f)
+    //#define AL_AUTOWAH_DEFAULT_PEAK_GAIN             (11.22f)
+    //
+    ///* Compressor effect */
+    //#define AL_COMPRESSOR_MIN_ONOFF                  (0)
+    //#define AL_COMPRESSOR_MAX_ONOFF                  (1)
+    //#define AL_COMPRESSOR_DEFAULT_ONOFF              (1)
+    //
+    ///* Equalizer effect */
+    //#define AL_EQUALIZER_MIN_LOW_GAIN                (0.126f)
+    //#define AL_EQUALIZER_MAX_LOW_GAIN                (7.943f)
+    //#define AL_EQUALIZER_DEFAULT_LOW_GAIN            (1.0f)
+    //
+    //#define AL_EQUALIZER_MIN_LOW_CUTOFF              (50.0f)
+    //#define AL_EQUALIZER_MAX_LOW_CUTOFF              (800.0f)
+    //#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF          (200.0f)
+    //
+    //#define AL_EQUALIZER_MIN_MID1_GAIN               (0.126f)
+    //#define AL_EQUALIZER_MAX_MID1_GAIN               (7.943f)
+    //#define AL_EQUALIZER_DEFAULT_MID1_GAIN           (1.0f)
+    //
+    //#define AL_EQUALIZER_MIN_MID1_CENTER             (200.0f)
+    //#define AL_EQUALIZER_MAX_MID1_CENTER             (3000.0f)
+    //#define AL_EQUALIZER_DEFAULT_MID1_CENTER         (500.0f)
+    //
+    //#define AL_EQUALIZER_MIN_MID1_WIDTH              (0.01f)
+    //#define AL_EQUALIZER_MAX_MID1_WIDTH              (1.0f)
+    //#define AL_EQUALIZER_DEFAULT_MID1_WIDTH          (1.0f)
+    //
+    //#define AL_EQUALIZER_MIN_MID2_GAIN               (0.126f)
+    //#define AL_EQUALIZER_MAX_MID2_GAIN               (7.943f)
+    //#define AL_EQUALIZER_DEFAULT_MID2_GAIN           (1.0f)
+    //
+    //#define AL_EQUALIZER_MIN_MID2_CENTER             (1000.0f)
+    //#define AL_EQUALIZER_MAX_MID2_CENTER             (8000.0f)
+    //#define AL_EQUALIZER_DEFAULT_MID2_CENTER         (3000.0f)
+    //
+    //#define AL_EQUALIZER_MIN_MID2_WIDTH              (0.01f)
+    //#define AL_EQUALIZER_MAX_MID2_WIDTH              (1.0f)
+    //#define AL_EQUALIZER_DEFAULT_MID2_WIDTH          (1.0f)
+    //
+    //#define AL_EQUALIZER_MIN_HIGH_GAIN               (0.126f)
+    //#define AL_EQUALIZER_MAX_HIGH_GAIN               (7.943f)
+    //#define AL_EQUALIZER_DEFAULT_HIGH_GAIN           (1.0f)
+    //
+    //#define AL_EQUALIZER_MIN_HIGH_CUTOFF             (4000.0f)
+    //#define AL_EQUALIZER_MAX_HIGH_CUTOFF             (16000.0f)
+    //#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF         (6000.0f)
+    //
+    //
+    ///* Source parameter value ranges and defaults. */
+    //#define AL_MIN_AIR_ABSORPTION_FACTOR             (0.0f)
+    //#define AL_MAX_AIR_ABSORPTION_FACTOR             (10.0f)
+    //#define AL_DEFAULT_AIR_ABSORPTION_FACTOR         (0.0f)
+    //
+    //#define AL_MIN_ROOM_ROLLOFF_FACTOR               (0.0f)
+    //#define AL_MAX_ROOM_ROLLOFF_FACTOR               (10.0f)
+    //#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR           (0.0f)
+    //
+    //#define AL_MIN_CONE_OUTER_GAINHF                 (0.0f)
+    //#define AL_MAX_CONE_OUTER_GAINHF                 (1.0f)
+    //#define AL_DEFAULT_CONE_OUTER_GAINHF             (1.0f)
+    //
+    //#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO         AL_FALSE
+    //#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO         AL_TRUE
+    //#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO     AL_TRUE
+    //
+    //#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO   AL_FALSE
+    //#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO   AL_TRUE
+    //#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE
+    //
+    //#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE
+    //#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
+    //#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
+
+    /**
+     * Requests a number of effect slots.
+     *
+     * @param numSlots the slots count.
+     * @param buffers  the buffer that will receive the effect slots.
+     */
     public void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers);
+
+    /**
+     * Requests a number of effects.
+     *
+     * @param numEffects the effects count.
+     * @param buffers    the buffer that will receive the effects.
+     */
     public void alGenEffects(int numEffects, IntBuffer buffers);
+
+    /**
+     * Sets the integer value of an effect parameter.
+     *
+     * @param effect the effect to modify.
+     * @param param  the parameter to modify.
+     * @param value  the parameter value.
+     */
     public void alEffecti(int effect, int param, int value);
+
+    /**
+     * Sets the integer value of an effect slot parameter.
+     *
+     * @param effectSlot the effect slot to modify.
+     * @param param      the parameter to modify.
+     * @param value      the parameter value.
+     */
     public void alAuxiliaryEffectSloti(int effectSlot, int param, int value);
+
+    /**
+     * Deletes a number of effects.
+     *
+     * @param numEffects the effects count.
+     * @param buffers    the effect to delete.
+     */
     public void alDeleteEffects(int numEffects, IntBuffer buffers);
+
+    /**
+     * Deletes a number of effect slots.
+     *
+     * @param numEffectSlots the effect slots count.
+     * @param buffers        the effect slots to delete.
+     */
     public void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers);
+
+    /**
+     * Requests a number of filters.
+     *
+     * @param numFilters the filters count.
+     * @param buffers    the buffer that will receive the filters.
+     */
     public void alGenFilters(int numFilters, IntBuffer buffers);
+
+    /**
+     * Sets the integer value of a filter parameter.
+     *
+     * @param filter the filter to modify.
+     * @param param  the parameter to modify.
+     * @param value  the parameter value.
+     */
     public void alFilteri(int filter, int param, int value);
+
+    /**
+     * Sets the float value of a filter parameter.
+     *
+     * @param filter the filter to modify.
+     * @param param  the parameter to modify.
+     * @param value  the parameter value.
+     */
     public void alFilterf(int filter, int param, float value);
+
+    /**
+     * Deletes a number of filters.
+     *
+     * @param numFilters the filters count.
+     * @param buffers the filter to delete.
+     */
     public void alDeleteFilters(int numFilters, IntBuffer buffers);
+
+    /**
+     * Sets the float value of an effect parameter.
+     *
+     * @param effect the effect to modify.
+     * @param param  the parameter to modify.
+     * @param value  the parameter value.
+     */
     public void alEffectf(int effect, int param, float value);
-    
 }

+ 715 - 720
jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java

@@ -1,720 +1,715 @@
-/*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.cinematic;
-
-import com.jme3.animation.LoopMode;
-import com.jme3.app.Application;
-import com.jme3.app.state.AppState;
-import com.jme3.app.state.AppStateManager;
-import com.jme3.cinematic.events.AbstractCinematicEvent;
-import com.jme3.cinematic.events.CinematicEvent;
-import com.jme3.export.*;
-import com.jme3.renderer.Camera;
-import com.jme3.renderer.RenderManager;
-import com.jme3.scene.CameraNode;
-import com.jme3.scene.Node;
-import com.jme3.scene.control.CameraControl;
-import com.jme3.scene.control.CameraControl.ControlDirection;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * An appstate for composing and playing cut scenes in a game. The cinematic
- * schedules CinematicEvents over a timeline. Once the Cinematic created it has
- * to be attached to the stateManager.
- *
- * You can add various CinematicEvents to a Cinematic, see package
- * com.jme3.cinematic.events
- *
- * Two main methods can be used to add an event :
- *
- * @see Cinematic#addCinematicEvent(float,
- * com.jme3.cinematic.events.CinematicEvent) , that adds an event at the given
- * time form the cinematic start.
- *
- * @see
- * Cinematic#enqueueCinematicEvent(com.jme3.cinematic.events.CinematicEvent)
- * that enqueue events one after the other according to their initialDuration
- *
- * a cinematic has convenient methods to handle the playback :
- * @see Cinematic#play()
- * @see Cinematic#pause()
- * @see Cinematic#stop()
- *
- * A cinematic is itself a CinematicEvent, meaning you can embed several
- * Cinematics Embed cinematics must not be added to the stateManager though.
- *
- * Cinematic has a way to handle several point of view by creating CameraNode
- * over a cam and activating them on schedule.
- * @see Cinematic#bindCamera(java.lang.String, com.jme3.renderer.Camera)
- * @see Cinematic#activateCamera(float, java.lang.String)
- * @see Cinematic#setActiveCamera(java.lang.String)
- *
- * @author Nehon
- */
-public class Cinematic extends AbstractCinematicEvent implements AppState {
-
-    private static final Logger logger = Logger.getLogger(Application.class.getName());
-    private Node scene;
-    protected TimeLine timeLine = new TimeLine();
-    private int lastFetchedKeyFrame = -1;
-    private List<CinematicEvent> cinematicEvents = new ArrayList<CinematicEvent>();
-    private Map<String, CameraNode> cameras = new HashMap<String, CameraNode>();
-    private CameraNode currentCam;
-    private boolean initialized = false;
-    private Map<String, Map<Object, Object>> eventsData;
-    private float nextEnqueue = 0;
-
-    /**
-     * Used for serialization creates a cinematic, don't use this constructor
-     * directly
-     */
-    public Cinematic() {
-    }
-
-    /**
-     * creates a cinematic
-     *
-     * @param scene the scene in which the cinematic should take place
-     */
-    public Cinematic(Node scene) {
-        this.scene = scene;
-    }
-
-    /**
-     * creates a cinematic
-     *
-     * @param scene the scene in which the cinematic should take place
-     * @param initialDuration the duration of the cinematic (without considering
-     * the speed)
-     */
-    public Cinematic(Node scene, float initialDuration) {
-        super(initialDuration);
-        this.scene = scene;
-    }
-
-    /**
-     * creates a cinematic
-     *
-     * @param scene the scene in which the cinematic should take place
-     * @param loopMode tells if this cinematic should be looped or not
-     */
-    public Cinematic(Node scene, LoopMode loopMode) {
-        super(loopMode);
-        this.scene = scene;
-    }
-
-    /**
-     * creates a cinematic
-     *
-     * @param scene the scene in which the cinematic should take place
-     * @param initialDuration the duration of the cinematic (without considering
-     * the speed)
-     * @param loopMode tells if this cinematic should be looped or not
-     */
-    public Cinematic(Node scene, float initialDuration, LoopMode loopMode) {
-        super(initialDuration, loopMode);
-        this.scene = scene;
-    }
-
-    /**
-     * called internally
-     */
-    @Override
-    public void onPlay() {
-        if (isInitialized()) {
-            if (playState == PlayState.Paused) {
-                for (int i = 0; i < cinematicEvents.size(); i++) {
-                    CinematicEvent ce = cinematicEvents.get(i);
-                    if (ce.getPlayState() == PlayState.Paused) {
-                        ce.play();
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * called internally
-     */
-    @Override
-    public void onStop() {
-        time = 0;
-        lastFetchedKeyFrame = -1;
-        for (int i = 0; i < cinematicEvents.size(); i++) {
-            CinematicEvent ce = cinematicEvents.get(i);
-            ce.setTime(0);
-            ce.forceStop();
-        }
-        setEnableCurrentCam(false);
-    }
-
-    /**
-     * called internally
-     */
-    @Override
-    public void onPause() {
-        for (int i = 0; i < cinematicEvents.size(); i++) {
-            CinematicEvent ce = cinematicEvents.get(i);
-            if (ce.getPlayState() == PlayState.Playing) {
-                ce.pause();
-            }
-        }
-    }
-
-    /**
-     * used internally for serialization
-     *
-     * @param ex
-     * @throws IOException
-     */
-    @Override
-    public void write(JmeExporter ex) throws IOException {
-        super.write(ex);
-        OutputCapsule oc = ex.getCapsule(this);
-
-        oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null);
-        oc.writeStringSavableMap(cameras, "cameras", null);
-        oc.write(timeLine, "timeLine", null);
-
-    }
-
-    /**
-     * used internally for serialization
-     *
-     * @param im
-     * @throws IOException
-     */
-    @Override
-    public void read(JmeImporter im) throws IOException {
-        super.read(im);
-        InputCapsule ic = im.getCapsule(this);
-
-        cinematicEvents = ic.readSavableArrayList("cinematicEvents", null);
-        cameras = (Map<String, CameraNode>) ic.readStringSavableMap("cameras", null);
-        timeLine = (TimeLine) ic.readSavable("timeLine", null);
-    }
-
-    /**
-     * sets the speed of the cinematic. Note that it will set the speed of all
-     * events in the cinematic. 1 is normal speed. use 0.5f to make the
-     * cinematic twice slower, use 2 to make it twice faster
-     *
-     * @param speed the speed
-     */
-    @Override
-    public void setSpeed(float speed) {
-        super.setSpeed(speed);
-        for (int i = 0; i < cinematicEvents.size(); i++) {
-            CinematicEvent ce = cinematicEvents.get(i);
-            ce.setSpeed(speed);
-        }
-
-
-    }
-
-    /**
-     * used internally
-     *
-     * @param stateManager the state manager
-     * @param app the application
-     */
-    public void initialize(AppStateManager stateManager, Application app) {
-        initEvent(app, this);
-        for (CinematicEvent cinematicEvent : cinematicEvents) {
-            cinematicEvent.initEvent(app, this);
-        }
-
-        initialized = true;
-    }
-
-    /**
-     * used internally
-     *
-     * @return
-     */
-    public boolean isInitialized() {
-        return initialized;
-    }
-
-    /**
-     * passing true has the same effect as play() you should use play(),
-     * pause(), stop() to handle the cinematic playing state.
-     *
-     * @param enabled true or false
-     */
-    public void setEnabled(boolean enabled) {
-        if (enabled) {
-            play();
-        }
-    }
-
-    /**
-     * return true if the cinematic appstate is enabled (the cinematic is
-     * playing)
-     *
-     * @return true if enabled
-     */
-    public boolean isEnabled() {
-        return playState == PlayState.Playing;
-    }
-
-    /**
-     * called internally
-     *
-     * @param stateManager the state manager
-     */
-    public void stateAttached(AppStateManager stateManager) {
-    }
-
-    /**
-     * called internally
-     *
-     * @param stateManager the state manager
-     */
-    public void stateDetached(AppStateManager stateManager) {
-        stop();
-    }
-
-    /**
-     * called internally don't call it directly.
-     *
-     * @param tpf
-     */
-    public void update(float tpf) {
-        if (isInitialized()) {
-            internalUpdate(tpf);
-        }
-    }
-
-    /**
-     * used internally, don't call this directly.
-     *
-     * @param tpf
-     */
-    @Override
-    public void onUpdate(float tpf) {
-        int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time);
-
-        //iterate to make sure every key frame is triggered
-        for (int i = lastFetchedKeyFrame + 1; i <= keyFrameIndex; i++) {
-            KeyFrame keyFrame = timeLine.get(i);
-            if (keyFrame != null) {
-                keyFrame.trigger();
-            }
-        }
-
-        
-        for (int i = 0; i < cinematicEvents.size(); i++) {
-            CinematicEvent ce = cinematicEvents.get(i);
-            ce.internalUpdate(tpf);
-        }
-
-        
-        lastFetchedKeyFrame = keyFrameIndex;
-    }
-
-    /**
-     * This is used internally but can be called to shuffle through the
-     * cinematic.
-     *
-     * @param time the time to shuffle to.
-     */
-    @Override
-    public void setTime(float time) {
-
-        //stopping all events
-        onStop();
-        super.setTime(time);
-
-        int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time);
-        //triggering all the event from start to "time" 
-        //then computing timeOffset for each event
-        for (int i = 0; i <= keyFrameIndex; i++) {
-            KeyFrame keyFrame = timeLine.get(i);
-            if (keyFrame != null) {
-                for (CinematicEvent ce : keyFrame.getCinematicEvents()) {
-                    float t = this.time - timeLine.getKeyFrameTime(keyFrame);
-                    if (t >= 0 && (t <= ce.getInitialDuration() || ce.getLoopMode() != LoopMode.DontLoop)) {
-                        ce.play();
-                    }
-                    ce.setTime(t);
-                }
-            }
-        }
-        lastFetchedKeyFrame = keyFrameIndex;
-        if (playState != PlayState.Playing) {
-            pause();
-        }
-    }
-
-    /**
-     * Adds a cinematic event to this cinematic at the given timestamp. This
-     * operation returns a keyFrame
-     *
-     * @param timeStamp the time when the event will start after the beginning of
-     * the cinematic
-     * @param cinematicEvent the cinematic event
-     * @return the keyFrame for that event.
-     */
-    public KeyFrame addCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) {
-        KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp);
-        if (keyFrame == null) {
-            keyFrame = new KeyFrame();
-            timeLine.addKeyFrameAtTime(timeStamp, keyFrame);
-        }
-        keyFrame.cinematicEvents.add(cinematicEvent);
-        cinematicEvents.add(cinematicEvent);
-        if (isInitialized()) {
-            cinematicEvent.initEvent(null, this);
-        }
-        return keyFrame;
-    }
-
-    /**
-     * enqueue a cinematic event to a cinematic. This is a handy method when you
-     * want to chain event of a given duration without knowing their initial
-     * duration
-     *
-     * @param cinematicEvent the cinematic event to enqueue
-     * @return the timestamp the event was scheduled.
-     */
-    public float enqueueCinematicEvent(CinematicEvent cinematicEvent) {
-        float scheduleTime = nextEnqueue;
-        addCinematicEvent(scheduleTime, cinematicEvent);
-        nextEnqueue += cinematicEvent.getInitialDuration();
-        return scheduleTime;
-    }
-
-    /**
-     * removes the first occurrence found of the given cinematicEvent.
-     *
-     * @param cinematicEvent the cinematicEvent to remove
-     * @return true if the element has been removed
-     */
-    public boolean removeCinematicEvent(CinematicEvent cinematicEvent) {
-        cinematicEvent.dispose();
-        cinematicEvents.remove(cinematicEvent);
-        for (KeyFrame keyFrame : timeLine.values()) {
-            if (keyFrame.cinematicEvents.remove(cinematicEvent)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * removes the first occurrence found of the given cinematicEvent for the
-     * given time stamp.
-     *
-     * @param timeStamp the timestamp when the cinematicEvent has been added
-     * @param cinematicEvent the cinematicEvent to remove
-     * @return true if the element has been removed
-     */
-    public boolean removeCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) {
-        cinematicEvent.dispose();
-        KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp);
-        return removeCinematicEvent(keyFrame, cinematicEvent);
-    }
-
-    /**
-     * removes the first occurrence found of the given cinematicEvent for the
-     * given keyFrame
-     *
-     * @param keyFrame the keyFrame returned by the addCinematicEvent method.
-     * @param cinematicEvent the cinematicEvent to remove
-     * @return true if the element has been removed
-     */
-    public boolean removeCinematicEvent(KeyFrame keyFrame, CinematicEvent cinematicEvent) {
-        cinematicEvent.dispose();
-        boolean ret = keyFrame.cinematicEvents.remove(cinematicEvent);
-        cinematicEvents.remove(cinematicEvent);
-        if (keyFrame.isEmpty()) {
-            timeLine.removeKeyFrame(keyFrame.getIndex());
-        }
-        return ret;
-    }
-
-    /**
-     * called internally
-     *
-     * @see AppState#render(com.jme3.renderer.RenderManager) 
-     */
-    public void render(RenderManager rm) {
-    }
-
-    /**
-     * called internally
-     *
-     * @see AppState#postRender()
-     */
-    public void postRender() {
-    }
-
-    /**
-     * called internally
-     *
-     * @see AppState#cleanup()
-     */
-    public void cleanup() {
-    }
-
-    /**
-     * fits the duration of the cinematic to the duration of all its child
-     * cinematic events
-     */
-    public void fitDuration() {
-        KeyFrame kf = timeLine.getKeyFrameAtIndex(timeLine.getLastKeyFrameIndex());
-        float d = 0;
-        for (int i = 0; i < kf.getCinematicEvents().size(); i++) {
-            CinematicEvent ce = kf.getCinematicEvents().get(i);
-            float dur = timeLine.getKeyFrameTime(kf) + ce.getDuration() * ce.getSpeed();
-            if (d < dur) {
-                d = dur;
-            }
-        }
-
-        initialDuration = d;
-    }
-
-    /**
-     * Binds a camera to this cinematic, tagged by a unique name. This methods
-     * creates and returns a CameraNode for the cam and attach it to the scene.
-     * The control direction is set to SpatialToCamera. This camera Node can
-     * then be used in other events to handle the camera movements during the
-     * playback
-     *
-     * @param cameraName the unique tag the camera should have
-     * @param cam the scene camera.
-     * @return the created CameraNode.
-     */
-    public CameraNode bindCamera(String cameraName, Camera cam) {
-        if (cameras.containsKey(cameraName)) {
-            throw new IllegalArgumentException("Camera " + cameraName + " is already binded to this cinematic");
-        }
-        CameraNode node = new CameraNode(cameraName, cam);
-        node.setControlDir(ControlDirection.SpatialToCamera);
-        node.getControl(CameraControl.class).setEnabled(false);
-        cameras.put(cameraName, node);
-        scene.attachChild(node);
-        return node;
-    }
-
-    /**
-     * returns a cameraNode given its name
-     *
-     * @param cameraName the camera name (as registered in
-     * Cinematic#bindCamera())
-     * @return the cameraNode for this name
-     */
-    public CameraNode getCamera(String cameraName) {
-        return cameras.get(cameraName);
-    }
-
-    /**
-     * enable/disable the camera control of the cameraNode of the current cam
-     *
-     * @param enabled
-     */
-    private void setEnableCurrentCam(boolean enabled) {
-        if (currentCam != null) {
-            currentCam.getControl(CameraControl.class).setEnabled(enabled);
-        }
-    }
-
-    /**
-     * Sets the active camera instantly (use activateCamera if you want to
-     * schedule that event)
-     *
-     * @param cameraName the camera name (as registered in
-     * Cinematic#bindCamera())
-     */
-    public void setActiveCamera(String cameraName) {
-        setEnableCurrentCam(false);
-        currentCam = cameras.get(cameraName);
-        if (currentCam == null) {
-            logger.log(Level.WARNING, "{0} is not a camera bond to the cinematic, cannot activate", cameraName);
-        }
-        setEnableCurrentCam(true);
-    }
-
-    /**
-     * schedule an event that will activate the camera at the given time
-     *
-     * @param timeStamp the time to activate the cam
-     * @param cameraName the camera name (as registered in
-     * Cinematic#bindCamera())
-     */
-    public void activateCamera(final float timeStamp, final String cameraName) {
-        addCinematicEvent(timeStamp, new AbstractCinematicEvent() {
-            @Override
-            public void play() {
-                super.play();
-                stop();
-            }
-
-            @Override
-            public void onPlay() {
-                setActiveCamera(cameraName);
-            }
-
-            @Override
-            public void onUpdate(float tpf) {
-            }
-
-            @Override
-            public void onStop() {
-            }
-
-            @Override
-            public void onPause() {
-            }
-
-            @Override
-            public void forceStop() {
-            }
-
-            @Override
-            public void setTime(float time) {
-                play();
-            }
-        });
-    }
-
-    /**
-     * returns the complete eventdata map
-     *
-     * @return the eventdata map
-     */
-    private Map<String, Map<Object, Object>> getEventsData() {
-        if (eventsData == null) {
-            eventsData = new HashMap<String, Map<Object, Object>>();
-        }
-        return eventsData;
-    }
-
-    /**
-     * used internally put an eventdata in the cinematic
-     *
-     * @param type the type of data
-     * @param key the key
-     * @param object the data
-     */
-    public void putEventData(String type, Object key, Object object) {
-        Map<String, Map<Object, Object>> data = getEventsData();
-        Map<Object, Object> row = data.get(type);
-        if (row == null) {
-            row = new HashMap<Object, Object>();
-        }
-        row.put(key, object);
-        data.put(type, row);
-    }
-
-    /**
-     * used internally return and event data
-     *
-     * @param type the type of data
-     * @param key the key
-     * @return
-     */
-    public Object getEventData(String type, Object key) {
-        if (eventsData != null) {
-            Map<Object, Object> row = eventsData.get(type);
-            if (row != null) {
-                return row.get(key);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Used internally remove an eventData
-     *
-     * @param type the type of data
-     * @param key the key of the data
-     */
-    public void removeEventData(String type, Object key) {
-        if (eventsData != null) {
-            Map<Object, Object> row = eventsData.get(type);
-            if (row != null) {
-                row.remove(key);
-            }
-        }
-    }
-
-    /**
-     * sets the scene to use for this cinematic it is expected that the scene is
-     * added before adding events to the cinematic
-     *
-     * @param scene the scene where the cinematic should take place.
-     */
-    public void setScene(Node scene) {
-        this.scene = scene;
-    }
-
-    /**
-     * return the scene where the cinematic occur
-     *
-     * @return the scene
-     */
-    public Node getScene() {
-        return scene;
-    }
-
-    /**
-     * clear the cinematic of its events.
-     */
-    public void clear() {
-        dispose();
-        cinematicEvents.clear();
-        timeLine.clear();
-        if (eventsData != null) {
-            eventsData.clear();
-        }
-    }
-
-    /**
-     * used internally to cleanup the cinematic. Called when the clear() method
-     * is called
-     */
-    @Override
-    public void dispose() {
-        for (CinematicEvent event : cinematicEvents) {
-            event.dispose();
-        }
-    }
-}
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.app.state.AppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.cinematic.events.AbstractCinematicEvent;
+import com.jme3.cinematic.events.CameraEvent;
+import com.jme3.cinematic.events.CinematicEvent;
+import com.jme3.export.*;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.CameraNode;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.CameraControl;
+import com.jme3.scene.control.CameraControl.ControlDirection;
+import com.jme3.scene.control.Control;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * An appstate for composing and playing cut scenes in a game. The cinematic
+ * schedules CinematicEvents over a timeline. Once the Cinematic created it has
+ * to be attached to the stateManager.
+ *
+ * You can add various CinematicEvents to a Cinematic, see package
+ * com.jme3.cinematic.events
+ *
+ * Two main methods can be used to add an event :
+ *
+ * @see Cinematic#addCinematicEvent(float,
+ * com.jme3.cinematic.events.CinematicEvent) , that adds an event at the given
+ * time form the cinematic start.
+ *
+ * @see
+ * Cinematic#enqueueCinematicEvent(com.jme3.cinematic.events.CinematicEvent)
+ * that enqueue events one after the other according to their initialDuration
+ *
+ * a cinematic has convenient methods to handle the playback :
+ * @see Cinematic#play()
+ * @see Cinematic#pause()
+ * @see Cinematic#stop()
+ *
+ * A cinematic is itself a CinematicEvent, meaning you can embed several
+ * Cinematics Embed cinematics must not be added to the stateManager though.
+ *
+ * Cinematic has a way to handle several point of view by creating CameraNode
+ * over a cam and activating them on schedule.
+ * @see Cinematic#bindCamera(java.lang.String, com.jme3.renderer.Camera)
+ * @see Cinematic#activateCamera(float, java.lang.String)
+ * @see Cinematic#setActiveCamera(java.lang.String)
+ *
+ * @author Nehon
+ */
+public class Cinematic extends AbstractCinematicEvent implements AppState {
+
+    private static final Logger logger = Logger.getLogger(Cinematic.class.getName());
+    private Node scene;
+    protected TimeLine timeLine = new TimeLine();
+    private int lastFetchedKeyFrame = -1;
+    private List<CinematicEvent> cinematicEvents = new ArrayList<>();
+    private Map<String, CameraNode> cameras = new HashMap<>();
+    private CameraNode currentCam;
+    private boolean initialized = false;
+    private Map<String, Map<Object, Object>> eventsData;
+    private float nextEnqueue = 0;
+
+    /**
+     * Used for serialization creates a cinematic, don't use this constructor
+     * directly
+     */
+    public Cinematic() {
+        super();
+    }
+
+    public Cinematic(float initialDuration) {
+        super(initialDuration);
+    }
+
+    public Cinematic(LoopMode loopMode) {
+        super(loopMode);
+    }
+
+    public Cinematic(float initialDuration, LoopMode loopMode) {
+        super(initialDuration, loopMode);
+    }
+
+    /**
+     * creates a cinematic
+     *
+     * @param scene the scene in which the cinematic should take place
+     */
+    public Cinematic(Node scene) {
+        this.scene = scene;
+    }
+
+    /**
+     * creates a cinematic
+     *
+     * @param scene the scene in which the cinematic should take place
+     * @param initialDuration the duration of the cinematic (without considering
+     * the speed)
+     */
+    public Cinematic(Node scene, float initialDuration) {
+        super(initialDuration);
+        this.scene = scene;
+    }
+
+    /**
+     * creates a cinematic
+     *
+     * @param scene the scene in which the cinematic should take place
+     * @param loopMode tells if this cinematic should be looped or not
+     */
+    public Cinematic(Node scene, LoopMode loopMode) {
+        super(loopMode);
+        this.scene = scene;
+    }
+
+    /**
+     * creates a cinematic
+     *
+     * @param scene the scene in which the cinematic should take place
+     * @param initialDuration the duration of the cinematic (without considering
+     * the speed)
+     * @param loopMode tells if this cinematic should be looped or not
+     */
+    public Cinematic(Node scene, float initialDuration, LoopMode loopMode) {
+        super(initialDuration, loopMode);
+        this.scene = scene;
+    }
+
+    /**
+     * called internally
+     */
+    @Override
+    public void onPlay() {
+        if (isInitialized()) {
+            if (playState == PlayState.Paused) {
+                for (int i = 0; i < cinematicEvents.size(); i++) {
+                    CinematicEvent ce = cinematicEvents.get(i);
+                    if (ce.getPlayState() == PlayState.Paused) {
+                        ce.play();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * called internally
+     */
+    @Override
+    public void onStop() {
+        time = 0;
+        lastFetchedKeyFrame = -1;
+        for (int i = 0; i < cinematicEvents.size(); i++) {
+            CinematicEvent ce = cinematicEvents.get(i);
+            ce.setTime(0);
+            ce.forceStop();
+        }
+        setEnableCurrentCam(false);
+    }
+
+    /**
+     * called internally
+     */
+    @Override
+    public void onPause() {
+        for (int i = 0; i < cinematicEvents.size(); i++) {
+            CinematicEvent ce = cinematicEvents.get(i);
+            if (ce.getPlayState() == PlayState.Playing) {
+                ce.pause();
+            }
+        }
+    }
+
+    /**
+     * used internally for serialization
+     *
+     * @param ex
+     * @throws IOException
+     */
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        super.write(ex);
+        OutputCapsule oc = ex.getCapsule(this);
+        oc.write(cinematicEvents.toArray(new CinematicEvent[cinematicEvents.size()]), "cinematicEvents", null);
+        oc.writeStringSavableMap(cameras, "cameras", null);
+        oc.write(timeLine, "timeLine", null);
+
+    }
+
+    /**
+     * used internally for serialization
+     *
+     * @param im
+     * @throws IOException
+     */
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        super.read(im);
+        InputCapsule ic = im.getCapsule(this);
+
+        Savable[] events = ic.readSavableArray("cinematicEvents", null);
+        for (Savable c : events) {
+//            addCinematicEvent(((CinematicEvent) c).getTime(), (CinematicEvent) c)
+            cinematicEvents.add((CinematicEvent) c);
+        }
+        cameras = (Map<String, CameraNode>) ic.readStringSavableMap("cameras", null);
+        timeLine = (TimeLine) ic.readSavable("timeLine", null);
+    }
+
+    /**
+     * sets the speed of the cinematic. Note that it will set the speed of all
+     * events in the cinematic. 1 is normal speed. use 0.5f to make the
+     * cinematic twice slower, use 2 to make it twice faster
+     *
+     * @param speed the speed
+     */
+    @Override
+    public void setSpeed(float speed) {
+        super.setSpeed(speed);
+        for (int i = 0; i < cinematicEvents.size(); i++) {
+            CinematicEvent ce = cinematicEvents.get(i);
+            ce.setSpeed(speed);
+        }
+
+    }
+
+    /**
+     * used internally
+     *
+     * @param stateManager the state manager
+     * @param app the application
+     */
+    public void initialize(AppStateManager stateManager, Application app) {
+        initEvent(app, this);
+        for (CinematicEvent cinematicEvent : cinematicEvents) {
+            cinematicEvent.initEvent(app, this);
+        }
+        if(!cameras.isEmpty()){
+            for(CameraNode n : cameras.values()){
+                n.setCamera(app.getCamera());
+            }
+        }
+        initialized = true;
+    }
+
+    /**
+     * used internally
+     *
+     * @return
+     */
+    public boolean isInitialized() {
+        return initialized;
+    }
+
+    /**
+     * passing true has the same effect as play() you should use play(),
+     * pause(), stop() to handle the cinematic playing state.
+     *
+     * @param enabled true or false
+     */
+    public void setEnabled(boolean enabled) {
+        if (enabled) {
+            play();
+        }
+    }
+
+    /**
+     * return true if the cinematic appstate is enabled (the cinematic is
+     * playing)
+     *
+     * @return true if enabled
+     */
+    public boolean isEnabled() {
+        return playState == PlayState.Playing;
+    }
+
+    /**
+     * called internally
+     *
+     * @param stateManager the state manager
+     */
+    public void stateAttached(AppStateManager stateManager) {
+    }
+
+    /**
+     * called internally
+     *
+     * @param stateManager the state manager
+     */
+    public void stateDetached(AppStateManager stateManager) {
+        stop();
+    }
+
+    /**
+     * called internally don't call it directly.
+     *
+     * @param tpf
+     */
+    @Override
+    public void update(float tpf) {
+        if (isInitialized() && playState == PlayState.Playing) {
+            internalUpdate(tpf);
+        }
+    }
+
+    /**
+     * used internally, don't call this directly.
+     *
+     * @param tpf
+     */
+    @Override
+    public void onUpdate(float tpf) {
+        int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time);
+
+        //iterate to make sure every key frame is triggered
+        for (int i = lastFetchedKeyFrame + 1; i <= keyFrameIndex; i++) {
+            KeyFrame keyFrame = timeLine.get(i);
+            if (keyFrame != null) {
+                keyFrame.trigger();
+            }
+        }
+
+        for (int i = 0; i < cinematicEvents.size(); i++) {
+            CinematicEvent ce = cinematicEvents.get(i);
+            ce.internalUpdate(tpf);
+        }
+
+        lastFetchedKeyFrame = keyFrameIndex;
+    }
+
+    /**
+     * This is used internally but can be called to shuffle through the
+     * cinematic.
+     *
+     * @param time the time to shuffle to.
+     */
+    @Override
+    public void setTime(float time) {
+
+        //stopping all events
+        onStop();
+        super.setTime(time);
+
+        int keyFrameIndex = timeLine.getKeyFrameIndexFromTime(time);
+        //triggering all the event from start to "time" 
+        //then computing timeOffset for each event
+        for (int i = 0; i <= keyFrameIndex; i++) {
+            KeyFrame keyFrame = timeLine.get(i);
+            if (keyFrame != null) {
+                for (CinematicEvent ce : keyFrame.getCinematicEvents()) {
+                    float t = this.time - timeLine.getKeyFrameTime(keyFrame);
+                    if (t >= 0 && (t <= ce.getInitialDuration() || ce.getLoopMode() != LoopMode.DontLoop)) {
+                        ce.play();
+                    }
+                    ce.setTime(t);
+                }
+            }
+        }
+        lastFetchedKeyFrame = keyFrameIndex;
+        if (playState != PlayState.Playing) {
+            pause();
+        }
+    }
+
+    /**
+     * Adds a cinematic event to this cinematic at the given timestamp. This
+     * operation returns a keyFrame
+     *
+     * @param timeStamp the time when the event will start after the beginning
+     * of the cinematic
+     * @param cinematicEvent the cinematic event
+     * @return the keyFrame for that event.
+     */
+    public KeyFrame addCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) {
+        KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp);
+        if (keyFrame == null) {
+            keyFrame = new KeyFrame();
+            timeLine.addKeyFrameAtTime(timeStamp, keyFrame);
+        }
+        keyFrame.cinematicEvents.add(cinematicEvent);
+        cinematicEvents.add(cinematicEvent);
+        if (isInitialized()) {
+            cinematicEvent.initEvent(null, this);
+        }
+        return keyFrame;
+    }
+
+    /**
+     * enqueue a cinematic event to a cinematic. This is a handy method when you
+     * want to chain event of a given duration without knowing their initial
+     * duration
+     *
+     * @param cinematicEvent the cinematic event to enqueue
+     * @return the timestamp the event was scheduled.
+     */
+    public float enqueueCinematicEvent(CinematicEvent cinematicEvent) {
+        float scheduleTime = nextEnqueue;
+        addCinematicEvent(scheduleTime, cinematicEvent);
+        nextEnqueue += cinematicEvent.getInitialDuration();
+        return scheduleTime;
+    }
+
+    /**
+     * removes the first occurrence found of the given cinematicEvent.
+     *
+     * @param cinematicEvent the cinematicEvent to remove
+     * @return true if the element has been removed
+     */
+    public boolean removeCinematicEvent(CinematicEvent cinematicEvent) {
+        cinematicEvent.dispose();
+        cinematicEvents.remove(cinematicEvent);
+        for (KeyFrame keyFrame : timeLine.values()) {
+            if (keyFrame.cinematicEvents.remove(cinematicEvent)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * removes the first occurrence found of the given cinematicEvent for the
+     * given time stamp.
+     *
+     * @param timeStamp the timestamp when the cinematicEvent has been added
+     * @param cinematicEvent the cinematicEvent to remove
+     * @return true if the element has been removed
+     */
+    public boolean removeCinematicEvent(float timeStamp, CinematicEvent cinematicEvent) {
+        cinematicEvent.dispose();
+        KeyFrame keyFrame = timeLine.getKeyFrameAtTime(timeStamp);
+        return removeCinematicEvent(keyFrame, cinematicEvent);
+    }
+
+    /**
+     * removes the first occurrence found of the given cinematicEvent for the
+     * given keyFrame
+     *
+     * @param keyFrame the keyFrame returned by the addCinematicEvent method.
+     * @param cinematicEvent the cinematicEvent to remove
+     * @return true if the element has been removed
+     */
+    public boolean removeCinematicEvent(KeyFrame keyFrame, CinematicEvent cinematicEvent) {
+        cinematicEvent.dispose();
+        boolean ret = keyFrame.cinematicEvents.remove(cinematicEvent);
+        cinematicEvents.remove(cinematicEvent);
+        if (keyFrame.isEmpty()) {
+            timeLine.removeKeyFrame(keyFrame.getIndex());
+        }
+        return ret;
+    }
+
+    /**
+     * called internally
+     *
+     * @see AppState#render(com.jme3.renderer.RenderManager)
+     */
+    public void render(RenderManager rm) {
+    }
+
+    /**
+     * called internally
+     *
+     * @see AppState#postRender()
+     */
+    public void postRender() {
+    }
+
+    /**
+     * called internally
+     *
+     * @see AppState#cleanup()
+     */
+    public void cleanup() {
+    }
+
+    /**
+     * fits the duration of the cinematic to the duration of all its child
+     * cinematic events
+     */
+    public void fitDuration() {
+        KeyFrame kf = timeLine.getKeyFrameAtIndex(timeLine.getLastKeyFrameIndex());
+        float d = 0;
+        for (int i = 0; i < kf.getCinematicEvents().size(); i++) {
+            CinematicEvent ce = kf.getCinematicEvents().get(i);
+            float dur = timeLine.getKeyFrameTime(kf) + ce.getDuration() * ce.getSpeed();
+            if (d < dur) {
+                d = dur;
+            }
+        }
+
+        initialDuration = d;
+    }
+
+    /**
+     * Binds a camera to this cinematic, tagged by a unique name. This methods
+     * creates and returns a CameraNode for the cam and attach it to the scene.
+     * The control direction is set to SpatialToCamera. This camera Node can
+     * then be used in other events to handle the camera movements during the
+     * playback
+     *
+     * @param cameraName the unique tag the camera should have
+     * @param cam the scene camera.
+     * @return the created CameraNode.
+     */
+    public CameraNode bindCamera(String cameraName, Camera cam) {
+        if (cameras.containsKey(cameraName)) {
+            throw new IllegalArgumentException("Camera " + cameraName + " is already binded to this cinematic");
+        }
+        CameraNode node = new CameraNode(cameraName, cam);
+        node.setControlDir(ControlDirection.SpatialToCamera);
+        node.getControl(CameraControl.class).setEnabled(false);
+        cameras.put(cameraName, node);
+        scene.attachChild(node);
+        return node;
+    }
+
+    /**
+     * returns a cameraNode given its name
+     *
+     * @param cameraName the camera name (as registered in
+     * Cinematic#bindCamera())
+     * @return the cameraNode for this name
+     */
+    public CameraNode getCamera(String cameraName) {
+        return cameras.get(cameraName);
+    }
+
+    /**
+     * enable/disable the camera control of the cameraNode of the current cam
+     *
+     * @param enabled
+     */
+    private void setEnableCurrentCam(boolean enabled) {
+        if (currentCam != null) {
+            currentCam.getControl(CameraControl.class).setEnabled(enabled);
+        }
+    }
+
+    /**
+     * Sets the active camera instantly (use activateCamera if you want to
+     * schedule that event)
+     *
+     * @param cameraName the camera name (as registered in
+     * Cinematic#bindCamera())
+     */
+    public void setActiveCamera(String cameraName) {
+        setEnableCurrentCam(false);
+        currentCam = cameras.get(cameraName);
+        if (currentCam == null) {
+            logger.log(Level.WARNING, "{0} is not a camera bond to the cinematic, cannot activate", cameraName);
+        }
+        setEnableCurrentCam(true);
+    }
+
+    /**
+     * schedule an event that will activate the camera at the given time
+     *
+     * @param timeStamp the time to activate the cam
+     * @param cameraName the camera name (as registered in
+     * Cinematic#bindCamera())
+     */
+    public void activateCamera(final float timeStamp, final String cameraName) {
+        addCinematicEvent(timeStamp, new CameraEvent(this, cameraName));
+    }
+
+    /**
+     * returns the complete eventdata map
+     *
+     * @return the eventdata map
+     */
+    private Map<String, Map<Object, Object>> getEventsData() {
+        if (eventsData == null) {
+            eventsData = new HashMap<String, Map<Object, Object>>();
+        }
+        return eventsData;
+    }
+
+    /**
+     * used internally put an eventdata in the cinematic
+     *
+     * @param type the type of data
+     * @param key the key
+     * @param object the data
+     */
+    public void putEventData(String type, Object key, Object object) {
+        Map<String, Map<Object, Object>> data = getEventsData();
+        Map<Object, Object> row = data.get(type);
+        if (row == null) {
+            row = new HashMap<Object, Object>();
+        }
+        row.put(key, object);
+        data.put(type, row);
+    }
+
+    /**
+     * used internally return and event data
+     *
+     * @param type the type of data
+     * @param key the key
+     * @return
+     */
+    public Object getEventData(String type, Object key) {
+        if (eventsData != null) {
+            Map<Object, Object> row = eventsData.get(type);
+            if (row != null) {
+                return row.get(key);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Used internally remove an eventData
+     *
+     * @param type the type of data
+     * @param key the key of the data
+     */
+    public void removeEventData(String type, Object key) {
+        if (eventsData != null) {
+            Map<Object, Object> row = eventsData.get(type);
+            if (row != null) {
+                row.remove(key);
+            }
+        }
+    }
+
+    /**
+     * sets the scene to use for this cinematic it is expected that the scene is
+     * added before adding events to the cinematic
+     *
+     * @param scene the scene where the cinematic should take place.
+     */
+    public void setScene(Node scene) {
+        this.scene = scene;
+        if(!cameras.isEmpty()){
+            for(CameraNode n : cameras.values()){
+                this.scene.attachChild(n);
+            }
+        }
+    }
+
+    /**
+     * return the scene where the cinematic occur
+     *
+     * @return the scene
+     */
+    public Node getScene() {
+        return scene;
+    }
+
+    /**
+     * clear the cinematic of its events.
+     */
+    public void clear() {
+        dispose();
+        cinematicEvents.clear();
+        timeLine.clear();
+        if (eventsData != null) {
+            eventsData.clear();
+        }
+    }
+
+    /**
+     * used internally to cleanup the cinematic. Called when the clear() method
+     * is called
+     */
+    @Override
+    public void dispose() {
+        for (CinematicEvent event : cinematicEvents) {
+            event.dispose();
+        }
+    }
+}

+ 93 - 89
jme3-core/src/main/java/com/jme3/cinematic/KeyFrame.java

@@ -1,89 +1,93 @@
-/*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.cinematic;
-
-import com.jme3.cinematic.events.CinematicEvent;
-import com.jme3.export.*;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- *
- * @author Nehon
- */
-public class KeyFrame implements Savable {
-
-    List<CinematicEvent> cinematicEvents = new ArrayList<CinematicEvent>();
-    private int index;
-
-    public List<CinematicEvent> getCinematicEvents() {
-        return cinematicEvents;
-    }
-
-    public void setCinematicEvents(List<CinematicEvent> cinematicEvents) {
-        this.cinematicEvents = cinematicEvents;
-    }
-
-    public List<CinematicEvent> trigger() {
-        for (CinematicEvent event : cinematicEvents) {
-            event.play();
-        }
-        return cinematicEvents;
-    }
-    
-    public boolean isEmpty(){
-        return cinematicEvents.isEmpty();
-    }
-
-    public void write(JmeExporter ex) throws IOException {
-        OutputCapsule oc = ex.getCapsule(this);
-        oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null);
-        oc.write(index, "index", 0);
-    }
-
-    public void read(JmeImporter im) throws IOException {
-        InputCapsule ic = im.getCapsule(this);
-        cinematicEvents = ic.readSavableArrayList("cinematicEvents", null);
-        index=ic.readInt("index", 0);
-    }
-
-    public int getIndex() {
-        return index;
-    }
-
-    public void setIndex(int index) {
-        this.index = index;
-    }
-
-
-}
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic;
+
+import com.jme3.cinematic.events.CinematicEvent;
+import com.jme3.export.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Nehon
+ */
+public class KeyFrame implements Savable {
+
+    public KeyFrame(){
+        
+    }
+    
+    List<CinematicEvent> cinematicEvents = new ArrayList<>();
+    private int index;
+
+    public List<CinematicEvent> getCinematicEvents() {
+        return cinematicEvents;
+    }
+
+    public void setCinematicEvents(List<CinematicEvent> cinematicEvents) {
+        this.cinematicEvents = cinematicEvents;
+    }
+
+    public List<CinematicEvent> trigger() {
+        for (CinematicEvent event : cinematicEvents) {
+            event.play();
+        }
+        return cinematicEvents;
+    }
+    
+    public boolean isEmpty(){
+        return cinematicEvents.isEmpty();
+    }
+
+    public void write(JmeExporter ex) throws IOException {
+        OutputCapsule oc = ex.getCapsule(this);
+        oc.writeSavableArrayList((ArrayList) cinematicEvents, "cinematicEvents", null);
+        oc.write(index, "index", 0);
+    }
+
+    public void read(JmeImporter im) throws IOException {
+        InputCapsule ic = im.getCapsule(this);
+        cinematicEvents = ic.readSavableArrayList("cinematicEvents", null);
+        index=ic.readInt("index", 0);
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+
+}

+ 3 - 3
jme3-core/src/main/java/com/jme3/cinematic/MotionPath.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -202,7 +202,7 @@ public class MotionPath implements Savable {
     }
 
     /**
-     * Addsa waypoint to the path
+     * Add a waypoint to the path
      * @param wayPoint a position in world space
      */
     public void addWayPoint(Vector3f wayPoint) {
@@ -210,7 +210,7 @@ public class MotionPath implements Savable {
     }
 
     /**
-     * retruns the length of the path in world units
+     * Return the length of the path in world units
      * @return the length
      */
     public float getLength() {

+ 2 - 2
jme3-core/src/main/java/com/jme3/cinematic/MotionPathListener.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,7 @@ package com.jme3.cinematic;
 import com.jme3.cinematic.events.MotionEvent;
 
 /**
- * Trigger the events happening on an motion path
+ * Trigger the events happening on a motion path
  * @author Nehon
  */
 public interface MotionPathListener {

+ 32 - 3
jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java

@@ -41,7 +41,10 @@ import com.jme3.export.InputCapsule;
 import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
+import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.HashMap;
@@ -80,8 +83,9 @@ public class AnimationEvent extends AbstractCinematicEvent {
      * constructors
      */
     public AnimationEvent() {
+        super();
     }
-
+    
     /**
      * creates an animation event
      *
@@ -90,6 +94,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
      */
     public AnimationEvent(Spatial model, String animationName) {
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
         initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName);
     }
@@ -104,6 +109,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
     public AnimationEvent(Spatial model, String animationName, float initialDuration) {
         super(initialDuration);
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
     }
 
@@ -119,6 +125,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
         super(loopMode);
         initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName);
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
     }
 
@@ -134,6 +141,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
     public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode) {
         super(initialDuration, loopMode);
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
     }
 
@@ -149,6 +157,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
     public AnimationEvent(Spatial model, String animationName, float initialDuration, float blendTime) {
         super(initialDuration);
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
         this.blendTime = blendTime;
     }
@@ -167,6 +176,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
         super(loopMode);
         initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName);
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
         this.blendTime = blendTime;
     }
@@ -185,6 +195,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
     public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode, float blendTime) {
         super(initialDuration, loopMode);
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
         this.blendTime = blendTime;
     }
@@ -203,6 +214,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
         super(loopMode);
         initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName);
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
         this.channelIndex = channelIndex;
     }
@@ -217,6 +229,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
      */
     public AnimationEvent(Spatial model, String animationName, int channelIndex) {
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
         initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName);
         this.channelIndex = channelIndex;
@@ -233,6 +246,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
      */
     public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, int channelIndex, float blendTime) {
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
         this.loopMode = loopMode;
         initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName);
@@ -252,6 +266,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
     public AnimationEvent(Spatial model, String animationName, float initialDuration, int channelIndex) {
         super(initialDuration);
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
         this.channelIndex = channelIndex;
     }
@@ -270,6 +285,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
     public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode, int channelIndex) {
         super(initialDuration, loopMode);
         this.model = model;
+        this.modelName = model.getName();
         this.animationName = animationName;
         this.channelIndex = channelIndex;
     }
@@ -299,6 +315,18 @@ public class AnimationEvent extends AbstractCinematicEvent {
                     model = cinematic.getScene().getChild(modelName);
                 }
                 if (model != null) {
+                    if(cinematic.getScene() != null){
+                        Spatial sceneModel = cinematic.getScene().getChild(model.getName());
+                        if(sceneModel != null){
+                            Node parent = sceneModel.getParent();
+                            parent.detachChild(sceneModel);
+                            sceneModel = model;
+                            parent.attachChild(sceneModel);
+                        } else {
+                            cinematic.getScene().attachChild(model);
+                        }
+                    }
+                    
                     channel = model.getControl(AnimControl.class).createChannel();
                     map.put(channelIndex, channel);
                 } else {
@@ -401,6 +429,7 @@ public class AnimationEvent extends AbstractCinematicEvent {
         OutputCapsule oc = ex.getCapsule(this);
 
         oc.write(model, "model", null);
+        oc.write(modelName, "modelName", null);
         oc.write(animationName, "animationName", "");
         oc.write(blendTime, "blendTime", 0f);
         oc.write(channelIndex, "channelIndex", 0);
@@ -411,9 +440,9 @@ public class AnimationEvent extends AbstractCinematicEvent {
     public void read(JmeImporter im) throws IOException {
         super.read(im);
         InputCapsule ic = im.getCapsule(this);
-        if (im.getFormatVersion() == 0) {
+//        if (im.getFormatVersion() == 0) {
             modelName = ic.readString("modelName", "");
-        }
+//        }
         //FIXME always the same issue, because of the clonning of assets, this won't work
         //we have to somehow store userdata in the spatial and then recurse the 
         //scene sub scenegraph to find the correct instance of the model

+ 146 - 0
jme3-core/src/main/java/com/jme3/cinematic/events/CameraEvent.java

@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2009-2017 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.cinematic.events;
+
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.cinematic.TimeLine;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.scene.CameraNode;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ *
+ * @author Rickard <neph1 @ github>
+ */
+public class CameraEvent extends AbstractCinematicEvent{
+
+    private String cameraName;
+    private Cinematic cinematic;
+
+    public String getCameraName() {
+        return cameraName;
+    }
+
+    public void setCameraName(String cameraName) {
+        this.cameraName = cameraName;
+    }
+
+    public CameraEvent(){
+        
+    }
+    
+    public CameraEvent(Cinematic parentEvent, String cameraName){
+        this.cinematic = parentEvent;
+        this.cameraName = cameraName;
+    }
+    
+     @Override
+    public void initEvent(Application app, Cinematic cinematic) {
+        super.initEvent(app, cinematic);
+        this.cinematic = cinematic;
+    }
+    
+    @Override
+    public void play() {
+        super.play();
+        stop();
+    }
+    
+    @Override
+    public void onPlay() {
+        cinematic.setActiveCamera(cameraName);
+    }
+
+    @Override
+    public void onUpdate(float tpf) {
+    }
+
+    @Override
+    public void onStop() {
+    }
+
+    @Override
+    public void onPause() {
+    }
+
+    @Override
+    public void forceStop() {
+    }
+
+    @Override
+    public void setTime(float time) {
+        play();
+    }
+
+    public Cinematic getCinematic() {
+        return cinematic;
+    }
+
+    public void setCinematic(Cinematic cinematic) {
+        this.cinematic = cinematic;
+    }
+    
+    
+    
+    /**
+     * used internally for serialization
+     *
+     * @param ex
+     * @throws IOException
+     */
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        super.write(ex);
+        OutputCapsule oc = ex.getCapsule(this);
+        oc.write(cameraName, "cameraName", null);
+
+    }
+
+    /**
+     * used internally for serialization
+     *
+     * @param im
+     * @throws IOException
+     */
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        super.read(im);
+        InputCapsule ic = im.getCapsule(this);
+        cameraName = ic.readString("cameraName", null);
+    }
+}

+ 478 - 491
jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java

@@ -1,491 +1,478 @@
-/*
- * Copyright (c) 2009-2016 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.cinematic.events;
-
-import com.jme3.animation.AnimationUtils;
-import com.jme3.animation.LoopMode;
-import com.jme3.app.Application;
-import com.jme3.cinematic.Cinematic;
-import com.jme3.cinematic.MotionPath;
-import com.jme3.cinematic.PlayState;
-import com.jme3.export.InputCapsule;
-import com.jme3.export.JmeExporter;
-import com.jme3.export.JmeImporter;
-import com.jme3.export.OutputCapsule;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector3f;
-import com.jme3.renderer.RenderManager;
-import com.jme3.renderer.ViewPort;
-import com.jme3.scene.Spatial;
-import com.jme3.scene.control.Control;
-import com.jme3.util.clone.Cloner;
-import com.jme3.util.clone.JmeCloneable;
-import java.io.IOException;
-
-/**
- * A MotionEvent is a control over the spatial that manages the position and direction of the spatial while following a motion Path.
- *
- * You must first create a MotionPath and then create a MotionEvent to associate a spatial and the path.
- *
- * @author Nehon
- */
-public class MotionEvent extends AbstractCinematicEvent implements Control, JmeCloneable {
-
-    protected Spatial spatial;
-    protected int currentWayPoint;
-    protected float currentValue;
-    protected Vector3f direction = new Vector3f();
-    protected Vector3f lookAt = null;
-    protected Vector3f upVector = Vector3f.UNIT_Y;
-    protected Quaternion rotation = null;
-    protected Direction directionType = Direction.None;
-    protected MotionPath path;
-    private boolean isControl = true;
-    private int travelDirection = 1;
-    /**
-     * the distance traveled by the spatial on the path
-     */
-    protected float traveledDistance = 0;
-
-    /**
-     * Enum for the different type of target direction behavior.
-     */
-    public enum Direction {
-
-        /**
-         * The target stays in the starting direction.
-         */
-        None,
-        /**
-         * The target rotates with the direction of the path.
-         */
-        Path,
-        /**
-         * The target rotates with the direction of the path but with the addition of a rotation.
-         * You need to use the setRotation method when using this Direction.
-         */
-        PathAndRotation,
-        /**
-         * The target rotates with the given rotation.
-         */
-        Rotation,
-        /**
-         * The target looks at a point.
-         * You need to use the setLookAt method when using this direction.
-         */
-        LookAt
-    }
-
-    /**
-     * Create MotionEvent,
-     * when using this constructor don't forget to assign spatial and path.
-     */
-    public MotionEvent() {
-        super();
-    }
-
-    /**
-     * Creates a MotionPath for the given spatial on the given motion path.
-     * @param spatial
-     * @param path
-     */
-    public MotionEvent(Spatial spatial, MotionPath path) {
-        super();
-        spatial.addControl(this);
-        this.path = path;
-    }
-
-    /**
-     * Creates a MotionPath for the given spatial on the given motion path.
-     * @param spatial
-     * @param path
-     */
-    public MotionEvent(Spatial spatial, MotionPath path, float initialDuration) {
-        super(initialDuration);
-        spatial.addControl(this);
-        this.path = path;
-    }
-
-    /**
-     * Creates a MotionPath for the given spatial on the given motion path.
-     * @param spatial
-     * @param path
-     */
-    public MotionEvent(Spatial spatial, MotionPath path, LoopMode loopMode) {
-        super();
-        spatial.addControl(this);
-        this.path = path;
-        this.loopMode = loopMode;
-    }
-
-    /**
-     * Creates a MotionPath for the given spatial on the given motion path.
-     * @param spatial
-     * @param path
-     */
-    public MotionEvent(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode) {
-        super(initialDuration);
-        spatial.addControl(this);
-        this.path = path;
-        this.loopMode = loopMode;
-    }
-
-    public void update(float tpf) {
-        if (isControl) {
-            internalUpdate(tpf);
-        }
-    }
-
-    @Override
-    public void internalUpdate(float tpf) {
-        if (playState == PlayState.Playing) {
-            time = time + (tpf * speed);
-            if (loopMode == LoopMode.Loop && time < 0) {
-                time = initialDuration;
-            }            
-            if ((time >= initialDuration || time < 0) && loopMode == LoopMode.DontLoop) {
-                if (time >= initialDuration) {
-                    path.triggerWayPointReach(path.getNbWayPoints() - 1, this);
-                }
-                stop();
-            } else {
-                time = AnimationUtils.clampWrapTime(time, initialDuration, loopMode);
-                if(time<0){
-                    speed = - speed;
-                    time = - time;
-                }
-                onUpdate(tpf);
-            }
-        }
-    }
-
-    @Override
-    public void initEvent(Application app, Cinematic cinematic) {
-        super.initEvent(app, cinematic);
-        isControl = false;
-    }
-
-    @Override
-    public void setTime(float time) {
-        super.setTime(time);
-        onUpdate(0);
-    }
-
-    public void onUpdate(float tpf) {
-        traveledDistance = path.interpolatePath(time, this, tpf);
-        computeTargetDirection();
-    }
-
-    @Override
-    public void write(JmeExporter ex) throws IOException {
-        super.write(ex);
-        OutputCapsule oc = ex.getCapsule(this);
-        oc.write(lookAt, "lookAt", null);
-        oc.write(upVector, "upVector", Vector3f.UNIT_Y);
-        oc.write(rotation, "rotation", null);
-        oc.write(directionType, "directionType", Direction.None);
-        oc.write(path, "path", null);
-        oc.write(spatial, "spatial", null);
-    }
-
-    @Override
-    public void read(JmeImporter im) throws IOException {
-        super.read(im);
-        InputCapsule in = im.getCapsule(this);
-        lookAt = (Vector3f) in.readSavable("lookAt", null);
-        upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y);
-        rotation = (Quaternion) in.readSavable("rotation", null);
-        directionType = in.readEnum("directionType", Direction.class, Direction.None);
-        path = (MotionPath) in.readSavable("path", null);
-        spatial = (Spatial) in.readSavable("spatial", null);
-    }
-
-    /**
-     * This method is meant to be called by the motion path only.
-     * @return
-     */
-    public boolean needsDirection() {
-        return directionType == Direction.Path || directionType == Direction.PathAndRotation;
-    }
-
-    private void computeTargetDirection() {
-        switch (directionType) {
-            case Path:
-                Quaternion q = new Quaternion();
-                q.lookAt(direction, upVector);
-                spatial.setLocalRotation(q);
-                break;
-            case LookAt:
-                if (lookAt != null) {
-                    spatial.lookAt(lookAt, upVector);
-                }
-                break;
-            case PathAndRotation:
-                if (rotation != null) {
-                    Quaternion q2 = new Quaternion();
-                    q2.lookAt(direction, upVector);
-                    q2.multLocal(rotation);
-                    spatial.setLocalRotation(q2);
-                }
-                break;
-            case Rotation:
-                if (rotation != null) {
-                    spatial.setLocalRotation(rotation);
-                }
-                break;
-            case None:
-                break;
-            default:
-                break;
-        }
-    }
-
-    /**
-     * Clone this control for the given spatial.
-     * @param spatial
-     * @return
-     */
-    @Override
-    public Control cloneForSpatial(Spatial spatial) {
-        MotionEvent control = new MotionEvent();
-        control.setPath(path);
-        control.playState = playState;
-        control.currentWayPoint = currentWayPoint;
-        control.currentValue = currentValue;
-        control.direction = direction.clone();
-        control.lookAt = lookAt;
-        control.upVector = upVector.clone();
-        control.rotation = rotation;
-        control.initialDuration = initialDuration;
-        control.speed = speed;
-        control.loopMode = loopMode;
-        control.directionType = directionType;
-
-        return control;
-    }
-
-    @Override   
-    public Object jmeClone() {
-        MotionEvent control = new MotionEvent();
-        control.path = path;
-        control.playState = playState;
-        control.currentWayPoint = currentWayPoint;
-        control.currentValue = currentValue;
-        control.direction = direction.clone();
-        control.lookAt = lookAt;
-        control.upVector = upVector.clone();
-        control.rotation = rotation;
-        control.initialDuration = initialDuration;
-        control.speed = speed;
-        control.loopMode = loopMode;
-        control.directionType = directionType;
-        control.spatial = spatial;
-
-        return control;
-    }     
-
-    @Override   
-    public void cloneFields( Cloner cloner, Object original ) { 
-        this.spatial = cloner.clone(spatial);
-    }
-         
-    @Override
-    public void onPlay() {
-        traveledDistance = 0;
-    }
-
-    @Override
-    public void onStop() {
-        currentWayPoint = 0;
-    }
-
-    @Override
-    public void onPause() {
-    }
-
-    /**
-     * This method is meant to be called by the motion path only.
-     * @return
-     */
-    public float getCurrentValue() {
-        return currentValue;
-    }
-
-    /**
-     * This method is meant to be called by the motion path only.
-     *
-     */
-    public void setCurrentValue(float currentValue) {
-        this.currentValue = currentValue;
-    }
-
-    /**
-     * This method is meant to be called by the motion path only.
-     * @return
-     */
-    public int getCurrentWayPoint() {
-        return currentWayPoint;
-    }
-
-    /**
-     * This method is meant to be called by the motion path only.
-     *
-     */
-    public void setCurrentWayPoint(int currentWayPoint) {
-        this.currentWayPoint = currentWayPoint;
-    }
-
-    /**
-     * Returns the direction the spatial is moving.
-     * @return
-     */
-    public Vector3f getDirection() {
-        return direction;
-    }
-
-    /**
-     * Sets the direction of the spatial, using the Y axis as the up vector.
-     * Use MotionEvent#setDirection((Vector3f direction,Vector3f upVector) if 
-     * you want a custum up vector.
-     * This method is used by the motion path.
-     * @param direction
-     */
-    public void setDirection(Vector3f direction) {
-        setDirection(direction, Vector3f.UNIT_Y); 
-   }
-    
-    /**
-     * Sets the direction of the spatial with the given up vector.
-     * This method is used by the motion path.
-     * @param direction
-     * @param upVector the up vector to consider for this direction.
-     */
-    public void setDirection(Vector3f direction,Vector3f upVector) {
-        this.direction.set(direction);
-        this.upVector.set(upVector);
-    }
-
-    /**
-     * Returns the direction type of the target.
-     * @return the direction type.
-     */
-    public Direction getDirectionType() {
-        return directionType;
-    }
-
-    /**
-     * Sets the direction type of the target.
-     * On each update the direction given to the target can have different behavior.
-     * See the Direction Enum for explanations.
-     * @param directionType the direction type.
-     */
-    public void setDirectionType(Direction directionType) {
-        this.directionType = directionType;
-    }
-
-    /**
-     * Set the lookAt for the target.
-     * This can be used only if direction Type is Direction.LookAt.
-     * @param lookAt the position to look at.
-     * @param upVector the up vector.
-     */
-    public void setLookAt(Vector3f lookAt, Vector3f upVector) {
-        this.lookAt = lookAt;
-        this.upVector = upVector;
-    }
-
-    /**
-     * Returns the rotation of the target.
-     * @return the rotation quaternion.
-     */
-    public Quaternion getRotation() {
-        return rotation;
-    }
-
-    /**
-     * Sets the rotation of the target.
-     * This can be used only if direction Type is Direction.PathAndRotation or Direction.Rotation.
-     * With PathAndRotation the target will face the direction of the path multiplied by the given Quaternion.
-     * With Rotation the rotation of the target will be set with the given Quaternion.
-     * @param rotation the rotation quaternion.
-     */
-    public void setRotation(Quaternion rotation) {
-        this.rotation = rotation;
-    }
-
-    /**
-     * Return the motion path this control follows.
-     * @return
-     */
-    public MotionPath getPath() {
-        return path;
-    }
-
-    /**
-     * Sets the motion path to follow.
-     * @param path
-     */
-    public void setPath(MotionPath path) {
-        this.path = path;
-    }
-
-    public void setEnabled(boolean enabled) {
-        if (enabled) {
-            play();
-        } else {
-            pause();
-        }
-    }
-
-    public boolean isEnabled() {
-        return playState != PlayState.Stopped;
-    }
-
-    public void render(RenderManager rm, ViewPort vp) {
-    }
-
-    public void setSpatial(Spatial spatial) {
-        this.spatial = spatial;
-    }
-
-    public Spatial getSpatial() {
-        return spatial;
-    }
-
-    /**
-     * Return the distance traveled by the spatial on the path.
-     * @return 
-     */
-    public float getTraveledDistance() {
-        return traveledDistance;
-    }
-}
+/*
+ * Copyright (c) 2009-2018 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.cinematic.events;
+
+import com.jme3.animation.AnimationUtils;
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.cinematic.MotionPath;
+import com.jme3.cinematic.PlayState;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.Control;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
+import java.io.IOException;
+
+/**
+ * A MotionEvent is a control over the spatial that manages the position and direction of the spatial while following a motion Path.
+ *
+ * You must first create a MotionPath and then create a MotionEvent to associate a spatial and the path.
+ *
+ * @author Nehon
+ */
+public class MotionEvent extends AbstractCinematicEvent implements Control, JmeCloneable {
+
+    protected Spatial spatial;
+    protected int currentWayPoint;
+    protected float currentValue;
+    protected Vector3f direction = new Vector3f();
+    protected Vector3f lookAt = null;
+    protected Vector3f upVector = Vector3f.UNIT_Y;
+    protected Quaternion rotation = null;
+    protected Direction directionType = Direction.None;
+    protected MotionPath path;
+    private boolean isControl = true;
+    private int travelDirection = 1;
+    /**
+     * the distance traveled by the spatial on the path
+     */
+    protected float traveledDistance = 0;
+
+    /**
+     * Enum for the different type of target direction behavior.
+     */
+    public enum Direction {
+
+        /**
+         * The target stays in the starting direction.
+         */
+        None,
+        /**
+         * The target rotates with the direction of the path.
+         */
+        Path,
+        /**
+         * The target rotates with the direction of the path but with the addition of a rotation.
+         * You need to use the setRotation method when using this Direction.
+         */
+        PathAndRotation,
+        /**
+         * The target rotates with the given rotation.
+         */
+        Rotation,
+        /**
+         * The target looks at a point.
+         * You need to use the setLookAt method when using this direction.
+         */
+        LookAt
+    }
+
+    /**
+     * Create MotionEvent,
+     * when using this constructor don't forget to assign spatial and path.
+     */
+    public MotionEvent() {
+        super();
+    }
+    
+    /**
+     * Creates a MotionPath for the given spatial on the given motion path.
+     * @param spatial
+     * @param path
+     */
+    public MotionEvent(Spatial spatial, MotionPath path) {
+        super();
+        spatial.addControl(this);
+        this.path = path;
+    }
+
+    /**
+     * Creates a MotionPath for the given spatial on the given motion path.
+     * @param spatial
+     * @param path
+     */
+    public MotionEvent(Spatial spatial, MotionPath path, float initialDuration) {
+        super(initialDuration);
+        spatial.addControl(this);
+        this.path = path;
+    }
+
+    /**
+     * Creates a MotionPath for the given spatial on the given motion path.
+     * @param spatial
+     * @param path
+     */
+    public MotionEvent(Spatial spatial, MotionPath path, LoopMode loopMode) {
+        super();
+        spatial.addControl(this);
+        this.path = path;
+        this.loopMode = loopMode;
+    }
+
+    /**
+     * Creates a MotionPath for the given spatial on the given motion path.
+     * @param spatial
+     * @param path
+     */
+    public MotionEvent(Spatial spatial, MotionPath path, float initialDuration, LoopMode loopMode) {
+        super(initialDuration);
+        spatial.addControl(this);
+        this.path = path;
+        this.loopMode = loopMode;
+    }
+
+    public void update(float tpf) {
+        if (isControl) {
+            internalUpdate(tpf);
+        }
+    }
+
+    @Override
+    public void internalUpdate(float tpf) {
+        if (playState == PlayState.Playing) {
+            time = time + (tpf * speed);
+            if (loopMode == LoopMode.Loop && time < 0) {
+                time = initialDuration;
+            }            
+            if ((time >= initialDuration || time < 0) && loopMode == LoopMode.DontLoop) {
+                if (time >= initialDuration) {
+                    path.triggerWayPointReach(path.getNbWayPoints() - 1, this);
+                }
+                stop();
+            } else {
+                time = AnimationUtils.clampWrapTime(time, initialDuration, loopMode);
+                if(time<0){
+                    speed = - speed;
+                    time = - time;
+                }
+                onUpdate(tpf);
+            }
+        }
+    }
+
+    @Override
+    public void initEvent(Application app, Cinematic cinematic) {
+        super.initEvent(app, cinematic);
+        isControl = false;
+    }
+
+    @Override
+    public void setTime(float time) {
+        super.setTime(time);
+        onUpdate(0);
+    }
+
+    public void onUpdate(float tpf) {
+        traveledDistance = path.interpolatePath(time, this, tpf);
+        computeTargetDirection();
+    }
+
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        super.write(ex);
+        OutputCapsule oc = ex.getCapsule(this);
+        oc.write(lookAt, "lookAt", null);
+        oc.write(upVector, "upVector", Vector3f.UNIT_Y);
+        oc.write(rotation, "rotation", null);
+        oc.write(directionType, "directionType", Direction.None);
+        oc.write(path, "path", null);
+        oc.write(spatial, "spatial", null);
+    }
+
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        super.read(im);
+        InputCapsule in = im.getCapsule(this);
+        lookAt = (Vector3f) in.readSavable("lookAt", null);
+        upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y);
+        rotation = (Quaternion) in.readSavable("rotation", null);
+        directionType = in.readEnum("directionType", Direction.class, Direction.None);
+        path = (MotionPath) in.readSavable("path", null);
+        spatial = (Spatial) in.readSavable("spatial", null);
+    }
+
+    /**
+     * This method is meant to be called by the motion path only.
+     * @return
+     */
+    public boolean needsDirection() {
+        return directionType == Direction.Path || directionType == Direction.PathAndRotation;
+    }
+
+    private void computeTargetDirection() {
+        switch (directionType) {
+            case Path:
+                Quaternion q = new Quaternion();
+                q.lookAt(direction, upVector);
+                spatial.setLocalRotation(q);
+                break;
+            case LookAt:
+                if (lookAt != null) {
+                    spatial.lookAt(lookAt, upVector);
+                }
+                break;
+            case PathAndRotation:
+                if (rotation != null) {
+                    Quaternion q2 = new Quaternion();
+                    q2.lookAt(direction, upVector);
+                    q2.multLocal(rotation);
+                    spatial.setLocalRotation(q2);
+                }
+                break;
+            case Rotation:
+                if (rotation != null) {
+                    spatial.setLocalRotation(rotation);
+                }
+                break;
+            case None:
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Clone this control for the given spatial.
+     * @param spatial
+     * @return
+     */
+    @Deprecated
+    @Override
+    public Control cloneForSpatial(Spatial spatial) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override   
+    public Object jmeClone() {
+        MotionEvent control = new MotionEvent();
+        control.path = path;
+        control.playState = playState;
+        control.currentWayPoint = currentWayPoint;
+        control.currentValue = currentValue;
+        control.direction = direction.clone();
+        control.lookAt = lookAt;
+        control.upVector = upVector.clone();
+        control.rotation = rotation;
+        control.initialDuration = initialDuration;
+        control.speed = speed;
+        control.loopMode = loopMode;
+        control.directionType = directionType;
+        control.spatial = spatial;
+
+        return control;
+    }     
+
+    @Override   
+    public void cloneFields( Cloner cloner, Object original ) { 
+        this.spatial = cloner.clone(spatial);
+    }
+         
+    @Override
+    public void onPlay() {
+        traveledDistance = 0;
+    }
+
+    @Override
+    public void onStop() {
+        currentWayPoint = 0;
+    }
+
+    @Override
+    public void onPause() {
+    }
+
+    /**
+     * This method is meant to be called by the motion path only.
+     * @return
+     */
+    public float getCurrentValue() {
+        return currentValue;
+    }
+
+    /**
+     * This method is meant to be called by the motion path only.
+     *
+     */
+    public void setCurrentValue(float currentValue) {
+        this.currentValue = currentValue;
+    }
+
+    /**
+     * This method is meant to be called by the motion path only.
+     * @return
+     */
+    public int getCurrentWayPoint() {
+        return currentWayPoint;
+    }
+
+    /**
+     * This method is meant to be called by the motion path only.
+     *
+     */
+    public void setCurrentWayPoint(int currentWayPoint) {
+        this.currentWayPoint = currentWayPoint;
+    }
+
+    /**
+     * Returns the direction the spatial is moving.
+     * @return
+     */
+    public Vector3f getDirection() {
+        return direction;
+    }
+
+    /**
+     * Sets the direction of the spatial, using the Y axis as the up vector.
+     * Use MotionEvent#setDirection((Vector3f direction,Vector3f upVector) if 
+     * you want a custum up vector.
+     * This method is used by the motion path.
+     * @param direction
+     */
+    public void setDirection(Vector3f direction) {
+        setDirection(direction, Vector3f.UNIT_Y); 
+   }
+    
+    /**
+     * Sets the direction of the spatial with the given up vector.
+     * This method is used by the motion path.
+     * @param direction
+     * @param upVector the up vector to consider for this direction.
+     */
+    public void setDirection(Vector3f direction,Vector3f upVector) {
+        this.direction.set(direction);
+        this.upVector.set(upVector);
+    }
+
+    /**
+     * Returns the direction type of the target.
+     * @return the direction type.
+     */
+    public Direction getDirectionType() {
+        return directionType;
+    }
+
+    /**
+     * Sets the direction type of the target.
+     * On each update the direction given to the target can have different behavior.
+     * See the Direction Enum for explanations.
+     * @param directionType the direction type.
+     */
+    public void setDirectionType(Direction directionType) {
+        this.directionType = directionType;
+    }
+
+    /**
+     * Set the lookAt for the target.
+     * This can be used only if direction Type is Direction.LookAt.
+     * @param lookAt the position to look at.
+     * @param upVector the up vector.
+     */
+    public void setLookAt(Vector3f lookAt, Vector3f upVector) {
+        this.lookAt = lookAt;
+        this.upVector = upVector;
+    }
+
+    /**
+     * Returns the rotation of the target.
+     * @return the rotation quaternion.
+     */
+    public Quaternion getRotation() {
+        return rotation;
+    }
+
+    /**
+     * Sets the rotation of the target.
+     * This can be used only if direction Type is Direction.PathAndRotation or Direction.Rotation.
+     * With PathAndRotation the target will face the direction of the path multiplied by the given Quaternion.
+     * With Rotation the rotation of the target will be set with the given Quaternion.
+     * @param rotation the rotation quaternion.
+     */
+    public void setRotation(Quaternion rotation) {
+        this.rotation = rotation;
+    }
+
+    /**
+     * Return the motion path this control follows.
+     * @return
+     */
+    public MotionPath getPath() {
+        return path;
+    }
+
+    /**
+     * Sets the motion path to follow.
+     * @param path
+     */
+    public void setPath(MotionPath path) {
+        this.path = path;
+    }
+
+    public void setEnabled(boolean enabled) {
+        if (enabled) {
+            play();
+        } else {
+            pause();
+        }
+    }
+
+    public boolean isEnabled() {
+        return playState != PlayState.Stopped;
+    }
+
+    public void render(RenderManager rm, ViewPort vp) {
+    }
+
+    public void setSpatial(Spatial spatial) {
+        this.spatial = spatial;
+    }
+
+    public Spatial getSpatial() {
+        return spatial;
+    }
+
+    /**
+     * Return the distance traveled by the spatial on the path.
+     * @return 
+     */
+    public float getTraveledDistance() {
+        return traveledDistance;
+    }
+}

+ 230 - 229
jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java

@@ -1,229 +1,230 @@
-/*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.cinematic.events;
-
-import com.jme3.animation.LoopMode;
-import com.jme3.app.Application;
-import com.jme3.audio.AudioNode;
-import com.jme3.audio.AudioSource;
-import com.jme3.cinematic.Cinematic;
-import com.jme3.export.InputCapsule;
-import com.jme3.export.JmeExporter;
-import com.jme3.export.JmeImporter;
-import com.jme3.export.OutputCapsule;
-import java.io.IOException;
-
-/**
- * A sound track to be played in a cinematic.
- * @author Nehon
- */
-public class SoundEvent extends AbstractCinematicEvent {
-
-    protected String path;
-    protected AudioNode audioNode;
-    protected boolean stream = false;
-
-    /**
-     * creates a sound track from the given resource path
-     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
-     */
-    public SoundEvent(String path) {
-        this.path = path;
-    }
-
-    /**
-     * creates a sound track from the given resource path
-     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
-     * @param stream true to make the audio data streamed
-     */
-    public SoundEvent(String path, boolean stream) {
-        this(path);
-        this.stream = stream;
-    }
-
-    /**
-     * creates a sound track from the given resource path
-     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
-     * @param stream true to make the audio data streamed
-     * @param initialDuration the initial duration of the event
-     */
-    public SoundEvent(String path, boolean stream, float initialDuration) {
-        super(initialDuration);
-        this.path = path;
-        this.stream = stream;
-    }
-
-    /**
-     * creates a sound track from the given resource path
-     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
-     * @param stream true to make the audio data streamed
-     * @param loopMode the loopMode 
-     * @see LoopMode
-     */
-    public SoundEvent(String path, boolean stream, LoopMode loopMode) {
-        super(loopMode);
-        this.path = path;
-        this.stream = stream;
-    }
-
-     /**
-     * creates a sound track from the given resource path
-     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
-     * @param stream true to make the audio data streamed
-     * @param initialDuration the initial duration of the event
-     * @param loopMode the loopMode 
-     * @see LoopMode
-     */
-    public SoundEvent(String path, boolean stream, float initialDuration, LoopMode loopMode) {
-        super(initialDuration, loopMode);
-        this.path = path;
-        this.stream = stream;
-    }
-
-     /**
-     * creates a sound track from the given resource path
-     * @param path the path to an audio file (ie : "Sounds/mySound.wav")    
-     * @param initialDuration the initial duration of the event
-     */
-    public SoundEvent(String path, float initialDuration) {
-        super(initialDuration);
-        this.path = path;
-    }
-
-     /**
-     * creates a sound track from the given resource path
-     * @param path the path to an audio file (ie : "Sounds/mySound.wav")   
-     * @param loopMode the loopMode 
-     * @see LoopMode
-     */
-    public SoundEvent(String path, LoopMode loopMode) {
-        super(loopMode);
-        this.path = path;
-    }
-
-     /**
-     * creates a sound track from the given resource path
-     * @param path the path to an audio file (ie : "Sounds/mySound.wav")    
-     * @param initialDuration the initial duration of the event
-     * @param loopMode the loopMode 
-     * @see LoopMode
-     */
-    public SoundEvent(String path, float initialDuration, LoopMode loopMode) {
-        super(initialDuration, loopMode);
-        this.path = path;
-    }
-
-    /**
-     * creates a sound event
-     * used for serialization
-     */
-    public SoundEvent() {
-    }
-
-    @Override
-    public void initEvent(Application app, Cinematic cinematic) {
-        super.initEvent(app, cinematic);
-        audioNode = new AudioNode(app.getAssetManager(), path, stream);
-        audioNode.setPositional(false);
-        setLoopMode(loopMode);
-    }
-
-    @Override
-    public void setTime(float time) {
-        super.setTime(time);
-        //can occur on rewind
-        if (time < 0f) {            
-            stop();
-        }else{
-            audioNode.setTimeOffset(time);
-        }
-    }
-
-    @Override
-    public void onPlay() {
-        audioNode.play();
-    }
-
-    @Override
-    public void onStop() {
-        audioNode.stop();
-
-    }
-
-    @Override
-    public void onPause() {
-        audioNode.pause();
-    }
-
-    @Override
-    public void onUpdate(float tpf) {
-        if (audioNode.getStatus() == AudioSource.Status.Stopped) {
-            stop();
-        }
-    }
-
-    /**
-     *  Returns the underlying audio node of this sound track
-     * @return
-     */
-    public AudioNode getAudioNode() {
-        return audioNode;
-    }
-
-    @Override
-    public void setLoopMode(LoopMode loopMode) {
-        super.setLoopMode(loopMode);
-
-        if (loopMode != LoopMode.DontLoop) {
-            audioNode.setLooping(true);
-        } else {
-            audioNode.setLooping(false);
-        }
-    }
-
-    @Override
-    public void write(JmeExporter ex) throws IOException {
-        super.write(ex);
-        OutputCapsule oc = ex.getCapsule(this);
-        oc.write(path, "path", "");
-        oc.write(stream, "stream", false);
-    }
-
-    @Override
-    public void read(JmeImporter im) throws IOException {
-        super.read(im);
-        InputCapsule ic = im.getCapsule(this);
-        path = ic.readString("path", "");
-        stream = ic.readBoolean("stream", false);
-
-    }
-}
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.cinematic.events;
+
+import com.jme3.animation.LoopMode;
+import com.jme3.app.Application;
+import com.jme3.audio.AudioNode;
+import com.jme3.audio.AudioSource;
+import com.jme3.cinematic.Cinematic;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import java.io.IOException;
+
+/**
+ * A sound track to be played in a cinematic.
+ * @author Nehon
+ */
+public class SoundEvent extends AbstractCinematicEvent {
+
+    protected String path;
+    protected AudioNode audioNode;
+    protected boolean stream = false;
+
+    /**
+     * creates a sound track from the given resource path
+     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
+     */
+    public SoundEvent(String path) {
+        this.path = path;
+    }
+
+    /**
+     * creates a sound track from the given resource path
+     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
+     * @param stream true to make the audio data streamed
+     */
+    public SoundEvent(String path, boolean stream) {
+        this(path);
+        this.stream = stream;
+    }
+
+    /**
+     * creates a sound track from the given resource path
+     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
+     * @param stream true to make the audio data streamed
+     * @param initialDuration the initial duration of the event
+     */
+    public SoundEvent(String path, boolean stream, float initialDuration) {
+        super(initialDuration);
+        this.path = path;
+        this.stream = stream;
+    }
+
+    /**
+     * creates a sound track from the given resource path
+     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
+     * @param stream true to make the audio data streamed
+     * @param loopMode the loopMode 
+     * @see LoopMode
+     */
+    public SoundEvent(String path, boolean stream, LoopMode loopMode) {
+        super(loopMode);
+        this.path = path;
+        this.stream = stream;
+    }
+
+     /**
+     * creates a sound track from the given resource path
+     * @param path the path to an audio file (ie : "Sounds/mySound.wav")
+     * @param stream true to make the audio data streamed
+     * @param initialDuration the initial duration of the event
+     * @param loopMode the loopMode 
+     * @see LoopMode
+     */
+    public SoundEvent(String path, boolean stream, float initialDuration, LoopMode loopMode) {
+        super(initialDuration, loopMode);
+        this.path = path;
+        this.stream = stream;
+    }
+
+     /**
+     * creates a sound track from the given resource path
+     * @param path the path to an audio file (ie : "Sounds/mySound.wav")    
+     * @param initialDuration the initial duration of the event
+     */
+    public SoundEvent(String path, float initialDuration) {
+        super(initialDuration);
+        this.path = path;
+    }
+
+     /**
+     * creates a sound track from the given resource path
+     * @param path the path to an audio file (ie : "Sounds/mySound.wav")   
+     * @param loopMode the loopMode 
+     * @see LoopMode
+     */
+    public SoundEvent(String path, LoopMode loopMode) {
+        super(loopMode);
+        this.path = path;
+    }
+
+     /**
+     * creates a sound track from the given resource path
+     * @param path the path to an audio file (ie : "Sounds/mySound.wav")    
+     * @param initialDuration the initial duration of the event
+     * @param loopMode the loopMode 
+     * @see LoopMode
+     */
+    public SoundEvent(String path, float initialDuration, LoopMode loopMode) {
+        super(initialDuration, loopMode);
+        this.path = path;
+    }
+
+    /**
+     * creates a sound event
+     * used for serialization
+     */
+    public SoundEvent() {
+        super();
+    }
+
+    @Override
+    public void initEvent(Application app, Cinematic cinematic) {
+        super.initEvent(app, cinematic);
+        audioNode = new AudioNode(app.getAssetManager(), path, stream);
+        audioNode.setPositional(false);
+        setLoopMode(loopMode);
+    }
+
+    @Override
+    public void setTime(float time) {
+        super.setTime(time);
+        //can occur on rewind
+        if (time < 0f) {            
+            stop();
+        }else{
+            audioNode.setTimeOffset(time);
+        }
+    }
+
+    @Override
+    public void onPlay() {
+        audioNode.play();
+    }
+
+    @Override
+    public void onStop() {
+        audioNode.stop();
+
+    }
+
+    @Override
+    public void onPause() {
+        audioNode.pause();
+    }
+
+    @Override
+    public void onUpdate(float tpf) {
+        if (audioNode.getStatus() == AudioSource.Status.Stopped) {
+            stop();
+        }
+    }
+
+    /**
+     *  Returns the underlying audio node of this sound track
+     * @return
+     */
+    public AudioNode getAudioNode() {
+        return audioNode;
+    }
+
+    @Override
+    public void setLoopMode(LoopMode loopMode) {
+        super.setLoopMode(loopMode);
+
+        if (loopMode != LoopMode.DontLoop) {
+            audioNode.setLooping(true);
+        } else {
+            audioNode.setLooping(false);
+        }
+    }
+
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        super.write(ex);
+        OutputCapsule oc = ex.getCapsule(this);
+        oc.write(path, "path", "");
+        oc.write(stream, "stream", false);
+    }
+
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        super.read(im);
+        InputCapsule ic = im.getCapsule(this);
+        path = ic.readString("path", "");
+        stream = ic.readBoolean("stream", false);
+
+    }
+}

+ 2 - 2
jme3-core/src/main/java/com/jme3/collision/SweepSphere.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -217,7 +217,7 @@ class SweepSphere implements Collidable {
 
         float signedDistanceToPlane = triPlane.pseudoDistance(sCenter);
         if (normalDotVelocity == 0.0f){
-            // we are travelling exactly parrallel to the plane
+            // we are travelling exactly parallel to the plane
             if (FastMath.abs(signedDistanceToPlane) >= 1.0f){
                 // no collision possible
                 return null;

+ 5 - 5
jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -121,10 +121,10 @@ public class ParticleEmitter extends Geometry {
             this.parentEmitter = parentEmitter;
         }
 
+        @Deprecated
         @Override
         public Control cloneForSpatial(Spatial spatial) {
-            return this; // WARNING: Sets wrong control on spatial. Will be
-            // fixed automatically by ParticleEmitter.clone() method.
+            throw new UnsupportedOperationException();
         }
 
         @Override
@@ -384,7 +384,7 @@ public class ParticleEmitter extends Geometry {
      * Set to true if particles should spawn in world space.
      *
      * <p>If set to true and the particle emitter is moved in the scene,
-     * then particles that have already spawned won't be effected by this
+     * then particles that have already spawned won't be affected by this
      * motion. If set to false, the particles will emit in local space
      * and when the emitter is moved, so are all the particles that
      * were emitted previously.
@@ -846,7 +846,7 @@ public class ParticleEmitter extends Geometry {
      * @param initialVelocity Set the initial velocity a particle is spawned with,
      * the initial velocity given in the parameter will be varied according
      * to the velocity variation set in {@link ParticleEmitter#setVelocityVariation(float) }.
-     * A particle will move toward its velocity unless it is effected by the
+     * The particle will move with this velocity unless it is affected by
      * gravity.
      *
      * @deprecated

+ 2 - 2
jme3-core/src/main/java/com/jme3/effect/influencers/ParticleInfluencer.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -64,7 +64,7 @@ public interface ParticleInfluencer extends Savable, Cloneable, JmeCloneable {
      *        Set the initial velocity a particle is spawned with,
      *        the initial velocity given in the parameter will be varied according
      *        to the velocity variation set in {@link ParticleEmitter#setVelocityVariation(float) }.
-     *        A particle will move toward its velocity unless it is effected by the
+     *        The particle will move with this velocity unless it is affected by
      *        gravity.
      */
     void setInitialVelocity(Vector3f initialVelocity);

+ 2 - 2
jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshConvexHullShape.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,7 +37,7 @@ import com.jme3.scene.Mesh;
 import java.util.List;
 
 /**
- * This emiter shape emits the particles from the given shape's interior constrained by its convex hull
+ * This emitter shape emits the particles from the given shape's interior constrained by its convex hull
  * (a geometry that tightly wraps the mesh). So in case of multiple meshes some vertices may appear
  * in a space between them.
  * @author Marcin Roguski (Kaelthas)

+ 2 - 2
jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * This emiter shape emits the particles from the given shape's faces.
+ * This emitter shape emits the particles from the given shape's faces.
  * @author Marcin Roguski (Kaelthas)
  */
 public class EmitterMeshFaceShape extends EmitterMeshVertexShape {

+ 3 - 3
jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -50,7 +50,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 /**
- * This emiter shape emits the particles from the given shape's vertices
+ * This emitter shape emits the particles from the given shape's vertices
  * @author Marcin Roguski (Kaelthas)
  */
 public class EmitterMeshVertexShape implements EmitterShape {
@@ -74,7 +74,7 @@ public class EmitterMeshVertexShape implements EmitterShape {
     }
 
     /**
-     * This method sets the meshes that will form the emiter's shape.
+     * This method sets the meshes that will form the emitter's shape.
      * @param meshes
      *        a list of meshes that will form the emitter's shape
      */

+ 6 - 4
jme3-core/src/main/java/com/jme3/effect/shapes/EmitterSphereShape.java

@@ -96,10 +96,12 @@ public class EmitterSphereShape implements EmitterShape {
     @Override
     public void getRandomPoint(Vector3f store) {
         do {
-            store.x = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
-            store.y = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
-            store.z = (FastMath.nextRandomFloat() * 2f - 1f) * radius;
-        } while (store.distance(center) > radius);
+            store.x = (FastMath.nextRandomFloat() * 2f - 1f);
+            store.y = (FastMath.nextRandomFloat() * 2f - 1f);
+            store.z = (FastMath.nextRandomFloat() * 2f - 1f);
+        } while (store.lengthSquared() > 1);
+        store.multLocal(radius);
+        store.addLocal(center);
     }
 
     @Override

+ 5 - 1
jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java

@@ -48,6 +48,7 @@ import com.jme3.texture.Texture2D;
 import com.jme3.texture.TextureCubeMap;
 import com.jme3.texture.image.ColorSpace;
 import com.jme3.util.BufferUtils;
+import com.jme3.util.MipMapGenerator;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -72,6 +73,8 @@ public class EnvironmentCamera extends BaseAppState {
 
     protected Image.Format imageFormat = Image.Format.RGB16F;
 
+    public TextureCubeMap debugEnv;
+
     //Axis for cameras
     static {
         //PositiveX axis(left, up, direction)
@@ -188,10 +191,11 @@ public class EnvironmentCamera extends BaseAppState {
             buffers[i] = BufferUtils.createByteBuffer(size * size * imageFormat.getBitsPerPixel() / 8);
             renderManager.getRenderer().readFrameBufferWithFormat(framebuffers[i], buffers[i], imageFormat);
             images[i] = new Image(imageFormat, size, size, buffers[i], ColorSpace.Linear);
+            MipMapGenerator.generateMipMaps(images[i]);
         }
 
         final TextureCubeMap map = EnvMapUtils.makeCubeMap(images[0], images[1], images[2], images[3], images[4], images[5], imageFormat);
-
+            debugEnv = map;
         job.callback.done(map);
         map.getImage().dispose();
         jobs.remove(0);

+ 43 - 33
jme3-core/src/main/java/com/jme3/environment/LightProbeFactory.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,13 +31,14 @@
  */
 package com.jme3.environment;
 
+import com.jme3.app.Application;
 import com.jme3.environment.generation.*;
-import com.jme3.light.LightProbe;
 import com.jme3.environment.util.EnvMapUtils;
-import com.jme3.app.Application;
+import com.jme3.light.LightProbe;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import com.jme3.texture.TextureCubeMap;
+
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 
 /**
@@ -46,7 +47,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
  * Since the process can be long, you can provide a JobProgressListener that 
  * will be notified of the ongoing generation process when calling the makeProbe method.
  * 
- * The process is the folowing : 
+ * The process is as follows: 
  * 1. Create an EnvironmentCamera
  * 2. give it a position in the scene
  * 3. call {@link LightProbeFactory#makeProbe(com.jme3.environment.EnvironmentCamera, com.jme3.scene.Node)}
@@ -60,7 +61,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
  * This class is entirely thread safe and can be called from any thread. 
  * 
  * Note that in case you are using a {@link JobProgressListener} all the its 
- * method will be called inside and app.enqueu callable.
+ * method will be called inside and app.enqueue callable.
  * This means that it's completely safe to modify the scenegraph within the 
  * Listener method, but also means that the even will be delayed until next update loop.
  * 
@@ -72,7 +73,7 @@ public class LightProbeFactory {
     /**
      * Creates a LightProbe with the giver EnvironmentCamera in the given scene.
      * 
-     * Note that this is an assynchronous process that will run on multiple threads.
+     * Note that this is an asynchronous process that will run on multiple threads.
      * The process is thread safe.
      * The created lightProbe will only be marked as ready when the rendering process is done.
      * 
@@ -93,7 +94,7 @@ public class LightProbeFactory {
     /**
      * Creates a LightProbe with the giver EnvironmentCamera in the given scene.
      * 
-     * Note that this is an assynchronous process that will run on multiple threads.
+     * Note that this is an asynchronous process that will run on multiple threads.
      * The process is thread safe.
      * The created lightProbe will only be marked as ready when the rendering process is done.
      *      
@@ -106,10 +107,11 @@ public class LightProbeFactory {
      
      * @param envCam the EnvironmentCamera
      * @param scene the Scene
-     * @param listener the listener of the genration progress.
+     * @param genType Fast or HighQuality. Fast may be ok for many types of environment, but you may need high quality when an environment map has very high lighting values.
+     * @param listener the listener of the generation progress.
      * @return the created LightProbe
      */
-    public static LightProbe makeProbe(final EnvironmentCamera envCam, Spatial scene, final JobProgressListener<LightProbe> listener) {
+    public static LightProbe makeProbe(final EnvironmentCamera envCam, Spatial scene, final EnvMapUtils.GenerationType genType, final JobProgressListener<LightProbe> listener) {
         final LightProbe probe = new LightProbe();
         probe.setPosition(envCam.getPosition());
         probe.setPrefilteredMap(EnvMapUtils.createPrefilteredEnvMap(envCam.getSize(), envCam.getImageFormat()));
@@ -117,33 +119,37 @@ public class LightProbeFactory {
 
             @Override
             public void done(TextureCubeMap map) {
-                generatePbrMaps(map, probe, envCam.getApplication(), listener);
+                generatePbrMaps(map, probe, envCam.getApplication(), genType, listener);
             }
         });
         return probe;
     }
-    
-     /**
-     * Updates a LightProbe with the giver EnvironmentCamera in the given scene.
-     * 
-     * Note that this is an assynchronous process that will run on multiple threads.
+
+    public static LightProbe makeProbe(final EnvironmentCamera envCam, Spatial scene, final JobProgressListener<LightProbe> listener) {
+        return makeProbe(envCam, scene, EnvMapUtils.GenerationType.Fast, listener);
+    }
+
+    /**
+     * Updates a LightProbe with the given EnvironmentCamera in the given scene.
+     * <p>
+     * Note that this is an asynchronous process that will run on multiple threads.
      * The process is thread safe.
      * The created lightProbe will only be marked as ready when the rendering process is done.
-     *      
-     * The JobProgressListener will be notified of the progress of the generation. 
-     * Note that you can also use a {@link JobProgressAdapter}. 
-     *      
+     * <p>
+     * The JobProgressListener will be notified of the progress of the generation.
+     * Note that you can also use a {@link JobProgressAdapter}.
+     *
+     * @param probe    the Light probe to update
+     * @param envCam   the EnvironmentCamera
+     * @param scene    the Scene
+     * @param genType  Fast or HighQuality. Fast may be ok for many types of environment, but you may need high quality when an environment map has very high lighting values.
+     * @param listener the listener of the generation progress.
+     * @return the created LightProbe
      * @see LightProbe
      * @see EnvironmentCamera
      * @see JobProgressListener
-     * 
-     * @param probe the Light probe to update
-     * @param envCam the EnvironmentCamera
-     * @param scene the Scene
-     * @param listener the listener of the genration progress.
-     * @return the created LightProbe
      */
-    public static LightProbe updateProbe(final LightProbe probe, final EnvironmentCamera envCam, Spatial scene, final JobProgressListener<LightProbe> listener) {
+    public static LightProbe updateProbe(final LightProbe probe, final EnvironmentCamera envCam, Spatial scene, final EnvMapUtils.GenerationType genType, final JobProgressListener<LightProbe> listener) {
         
         envCam.setPosition(probe.getPosition());
         
@@ -159,23 +165,27 @@ public class LightProbeFactory {
 
             @Override
             public void done(TextureCubeMap map) {
-                generatePbrMaps(map, probe, envCam.getApplication(), listener);
+                generatePbrMaps(map, probe, envCam.getApplication(), genType, listener);
             }
         });
         return probe;
     }
 
+    public static LightProbe updateProbe(final LightProbe probe, final EnvironmentCamera envCam, Spatial scene, final JobProgressListener<LightProbe> listener) {
+        return updateProbe(probe, envCam, scene, EnvMapUtils.GenerationType.Fast, listener);
+    }
+
     /**
      * Internally called to generate the maps.
      * This method will spawn 7 thread (one for the Irradiance spherical harmonics generator, and one for each face of the prefiltered env map).
-     * Those threads will be executed in a ScheduledThreadPoolExecutor that will be shutdown when the genration is done.
-     * 
+     * Those threads will be executed in a ScheduledThreadPoolExecutor that will be shutdown when the generation is done.
+     *
      * @param envMap the raw env map rendered by the env camera
-     * @param probe the LigthProbe to generate maps for
+     * @param probe the LightProbe to generate maps for
      * @param app the Application
      * @param listener a progress listener. (can be null if no progress reporting is needed)
      */
-    private static void generatePbrMaps(TextureCubeMap envMap, final LightProbe probe, Application app, final JobProgressListener<LightProbe> listener) {
+    private static void generatePbrMaps(TextureCubeMap envMap, final LightProbe probe, Application app, EnvMapUtils.GenerationType genType, final JobProgressListener<LightProbe> listener) {
         IrradianceSphericalHarmonicsGenerator irrShGenerator;
         PrefilteredEnvMapFaceGenerator[] pemGenerators = new PrefilteredEnvMapFaceGenerator[6];
 
@@ -189,7 +199,7 @@ public class LightProbeFactory {
 
         for (int i = 0; i < pemGenerators.length; i++) {
             pemGenerators[i] = new PrefilteredEnvMapFaceGenerator(app, i, new JobListener(listener, jobState, probe, i));
-            pemGenerators[i].setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), size, EnvMapUtils.FixSeamsMethod.None, probe.getPrefilteredEnvMap());
+            pemGenerators[i].setGenerationParam(EnvMapUtils.duplicateCubeMap(envMap), size, EnvMapUtils.FixSeamsMethod.None, genType, probe.getPrefilteredEnvMap());
             jobState.executor.execute(pemGenerators[i]);
         }
     }
@@ -227,7 +237,7 @@ public class LightProbeFactory {
     }
 
     /**
-     * An inner JobProgressListener to controll the genration process and properly clean up when it's done
+     * An inner JobProgressListener to control the generation process and properly clean up when it's done
      */
     private static class JobListener extends JobProgressAdapter<Integer> {
 

+ 2 - 2
jme3-core/src/main/java/com/jme3/environment/generation/IrradianceSphericalHarmonicsGenerator.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -73,7 +73,7 @@ public class IrradianceSphericalHarmonicsGenerator extends RunnableWithProgress
     }
 
     /**
-     * Fills all the genration parameters
+     * Fills all the generation parameters
      *
      * @param sourceMap the source cube map
      *                  {@link EnvMapUtils.FixSeamsMethod}

+ 162 - 73
jme3-core/src/main/java/com/jme3/environment/generation/PrefilteredEnvMapFaceGenerator.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,30 +31,22 @@
  */
 package com.jme3.environment.generation;
 
+import com.jme3.app.Application;
 import com.jme3.environment.util.CubeMapWrapper;
 import com.jme3.environment.util.EnvMapUtils;
-import com.jme3.app.Application;
 import com.jme3.math.*;
-
-import static com.jme3.math.FastMath.abs;
-import static com.jme3.math.FastMath.clamp;
-import static com.jme3.math.FastMath.pow;
-import static com.jme3.math.FastMath.sqrt;
-
 import com.jme3.texture.TextureCubeMap;
 
-import static com.jme3.environment.util.EnvMapUtils.getHammersleyPoint;
-import static com.jme3.environment.util.EnvMapUtils.getRoughnessFromMip;
-import static com.jme3.environment.util.EnvMapUtils.getSampleFromMip;
-import static com.jme3.environment.util.EnvMapUtils.getVectorFromCubemapFaceTexCoord;
-
 import java.util.concurrent.Callable;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import static com.jme3.environment.util.EnvMapUtils.*;
+import static com.jme3.math.FastMath.abs;
+import static com.jme3.math.FastMath.sqrt;
+
 /**
- * Generates one face of the prefiltered environnement map for PBR. This job can
- * be lauched from a separate thread.
+ * Generates one face of the prefiltered environment map for PBR. This job can
+ * be launched from a separate thread.
  * <p>
  * TODO there is a lot of duplicate code here with the EnvMapUtils.
  *
@@ -69,6 +61,7 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
 
     private int targetMapSize;
     private EnvMapUtils.FixSeamsMethod fixSeamsMethod;
+    private EnvMapUtils.GenerationType genType;
     private TextureCubeMap sourceMap;
     private TextureCubeMap store;
     private final Application app;
@@ -98,7 +91,7 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
 
 
     /**
-     * Fills all the genration parameters
+     * Fills all the generation parameters
      *
      * @param sourceMap      the source cube map
      * @param targetMapSize  the size of the generated map (width or height in
@@ -107,11 +100,12 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
      *                       {@link EnvMapUtils.FixSeamsMethod}
      * @param store          The cube map to store the result in.
      */
-    public void setGenerationParam(TextureCubeMap sourceMap, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) {
+    public void setGenerationParam(TextureCubeMap sourceMap, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, EnvMapUtils.GenerationType genType, TextureCubeMap store) {
         this.sourceMap = sourceMap;
         this.targetMapSize = targetMapSize;
         this.fixSeamsMethod = fixSeamsMethod;
         this.store = store;
+        this.genType = genType;
         init();
     }
 
@@ -162,68 +156,105 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
      * @return The irradiance cube map for the given coefficients
      */
     private TextureCubeMap generatePrefilteredEnvMap(TextureCubeMap sourceEnvMap, int targetMapSize, EnvMapUtils.FixSeamsMethod fixSeamsMethod, TextureCubeMap store) {
-        TextureCubeMap pem = store;
+        try {
+            TextureCubeMap pem = store;
 
-        int nbMipMap = (int) (Math.log(targetMapSize) / Math.log(2) - 1);
+            int nbMipMap = store.getImage().getMipMapSizes().length;
 
-        setEnd(nbMipMap);
+            setEnd(nbMipMap);
 
+            if (!sourceEnvMap.getImage().hasMipmaps() || sourceEnvMap.getImage().getMipMapSizes().length < nbMipMap) {
+                throw new IllegalArgumentException("The input cube map must have at least " + nbMipMap + "mip maps");
+            }
 
-        CubeMapWrapper sourceWrapper = new CubeMapWrapper(sourceEnvMap);
-        CubeMapWrapper targetWrapper = new CubeMapWrapper(pem);
+            CubeMapWrapper sourceWrapper = new CubeMapWrapper(sourceEnvMap);
+            CubeMapWrapper targetWrapper = new CubeMapWrapper(pem);
 
-        Vector3f texelVect = new Vector3f();
-        Vector3f color = new Vector3f();
-        ColorRGBA outColor = new ColorRGBA();
-        for (int mipLevel = 0; mipLevel < nbMipMap; mipLevel++) {
-            float roughness = getRoughnessFromMip(mipLevel, nbMipMap);
-            int nbSamples = getSampleFromMip(mipLevel, nbMipMap);
-            int targetMipMapSize = (int) pow(2, nbMipMap + 1 - mipLevel);
+            Vector3f texelVect = new Vector3f();
+            Vector3f color = new Vector3f();
+            ColorRGBA outColor = new ColorRGBA();
+            int targetMipMapSize = targetMapSize;
+            for (int mipLevel = 0; mipLevel < nbMipMap; mipLevel++) {
+                float roughness = getRoughnessFromMip(mipLevel, nbMipMap);
+                int nbSamples = getSampleFromMip(mipLevel, nbMipMap);
 
-            for (int y = 0; y < targetMipMapSize; y++) {
-                for (int x = 0; x < targetMipMapSize; x++) {
-                    color.set(0, 0, 0);
-                    getVectorFromCubemapFaceTexCoord(x, y, targetMipMapSize, face, texelVect, fixSeamsMethod);
-                    prefilterEnvMapTexel(sourceWrapper, roughness, texelVect, nbSamples, color);
+                for (int y = 0; y < targetMipMapSize; y++) {
+                    for (int x = 0; x < targetMipMapSize; x++) {
+                        color.set(0, 0, 0);
+                        getVectorFromCubemapFaceTexCoord(x, y, targetMipMapSize, face, texelVect, fixSeamsMethod);
+                        prefilterEnvMapTexel(sourceWrapper, roughness, texelVect, nbSamples, mipLevel, color);
 
-                    outColor.set(Math.max(color.x, 0.0001f), Math.max(color.y, 0.0001f), Math.max(color.z, 0.0001f), 1);
-                    log.log(Level.FINE, "coords {0},{1}", new Object[]{x, y});
-                    targetWrapper.setPixel(x, y, face, mipLevel, outColor);
+                        outColor.set(Math.max(color.x, 0.0001f), Math.max(color.y, 0.0001f), Math.max(color.z, 0.0001f), 1);
+                        targetWrapper.setPixel(x, y, face, mipLevel, outColor);
 
+                    }
                 }
+                targetMipMapSize /= 2;
+                progress();
             }
-            progress();
-        }
 
-        return pem;
+            return pem;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw e;
+        }
     }
 
-    private Vector3f prefilterEnvMapTexel(CubeMapWrapper envMapReader, float roughness, Vector3f N, int numSamples, Vector3f store) {
+    private Vector3f prefilterEnvMapTexel(CubeMapWrapper envMapReader, float roughness, Vector3f N, int numSamples, int mipLevel, Vector3f store) {
 
         Vector3f prefilteredColor = store;
         float totalWeight = 0.0f;
 
+        int nbRotations = 1;
+        if (genType == GenerationType.HighQuality) {
+            nbRotations = numSamples == 1 ? 1 : 18;
+        }
+
+        float rad = 2f * FastMath.PI / (float) nbRotations;
+        // offset rotation to avoid sampling pattern
+        float gi = (float) (FastMath.abs(N.z + N.x) * 256.0);
+        float offset = rad * (FastMath.cos((gi * 0.5f) % (2f * FastMath.PI)) * 0.5f + 0.5f);
+
         // a = roughness² and a2 = a²
         float a2 = roughness * roughness;
         a2 *= a2;
+
+        //Computing tangent frame
+        Vector3f upVector = Vector3f.UNIT_X;
+        if (abs(N.z) < 0.999) {
+            upVector = Vector3f.UNIT_Y;
+        }
+        Vector3f tangentX = tmp1.set(upVector).crossLocal(N).normalizeLocal();
+        Vector3f tangentY = tmp2.set(N).crossLocal(tangentX);
+
+        // https://placeholderart.wordpress.com/2015/07/28/implementation-notes-runtime-environment-map-filtering-for-image-based-lighting/
+        // in local space view == normal == 0,0,1
+        Vector3f V = new Vector3f(0, 0, 1);
+
+        Vector3f lWorld = new Vector3f();
         for (int i = 0; i < numSamples; i++) {
             Xi = getHammersleyPoint(i, numSamples, Xi);
-            H = importanceSampleGGX(Xi, a2, N, H);
-
+            H = importanceSampleGGX(Xi, a2, H);
             H.normalizeLocal();
-            tmp.set(H);
-            float NoH = N.dot(tmp);
-
-            Vector3f L = tmp.multLocal(NoH * 2).subtractLocal(N);
-            float NoL = clamp(N.dot(L), 0.0f, 1.0f);
-            if (NoL > 0) {
-                envMapReader.getPixel(L, c);
-                prefilteredColor.setX(prefilteredColor.x + c.r * NoL);
-                prefilteredColor.setY(prefilteredColor.y + c.g * NoL);
-                prefilteredColor.setZ(prefilteredColor.z + c.b * NoL);
-
-                totalWeight += NoL;
+            float VoH = H.z;
+            Vector3f L = H.multLocal(VoH * 2f).subtractLocal(V);
+            float NoL = L.z;
+
+            float computedMipLevel = mipLevel;
+            if (mipLevel != 0) {
+                computedMipLevel = computeMipLevel(roughness, numSamples, this.targetMapSize, VoH);
+            }
+
+            toWorld(L, N, tangentX, tangentY, lWorld);
+            totalWeight += samplePixel(envMapReader, lWorld, NoL, computedMipLevel, prefilteredColor);
+
+            for (int j = 1; j < nbRotations; j++) {
+                rotateDirection(offset + j * rad, L, lWorld);
+                L.set(lWorld);
+                toWorld(L, N, tangentX, tangentY, lWorld);
+                totalWeight += samplePixel(envMapReader, lWorld, NoL, computedMipLevel, prefilteredColor);
             }
+
         }
         if (totalWeight > 0) {
             prefilteredColor.divideLocal(totalWeight);
@@ -232,7 +263,78 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
         return prefilteredColor;
     }
 
-    public Vector3f importanceSampleGGX(Vector4f xi, float a2, Vector3f normal, Vector3f store) {
+    private float samplePixel(CubeMapWrapper envMapReader, Vector3f lWorld, float NoL, float computedMipLevel, Vector3f store) {
+
+        if (NoL <= 0) {
+            return 0;
+        }
+        envMapReader.getPixel(lWorld, computedMipLevel, c);
+        store.setX(store.x + c.r * NoL);
+        store.setY(store.y + c.g * NoL);
+        store.setZ(store.z + c.b * NoL);
+
+        return NoL;
+    }
+
+    private void toWorld(Vector3f L, Vector3f N, Vector3f tangentX, Vector3f tangentY, Vector3f store) {
+        store.set(tangentX).multLocal(L.x);
+        tmp.set(tangentY).multLocal(L.y);
+        store.addLocal(tmp);
+        tmp.set(N).multLocal(L.z);
+        store.addLocal(tmp);
+    }
+
+    private float computeMipLevel(float roughness, int numSamples, float size, float voH) {
+        // H[2] is NoH in local space
+        // adds 1.e-5 to avoid ggx / 0.0
+        float NoH = voH + 1E-5f;
+
+        // Probability Distribution Function
+        float Pdf = ggx(NoH, roughness) * NoH / (4.0f * voH);
+
+        // Solid angle represented by this sample
+        float omegaS = 1.0f / (numSamples * Pdf);
+
+        // Solid angle covered by 1 pixel with 6 faces that are EnvMapSize X EnvMapSize
+        float omegaP = 4.0f * FastMath.PI / (6.0f * size * size);
+
+        // Original paper suggest biasing the mip to improve the results
+        float mipBias = 1.0f; // I tested that the result is better with bias 1
+        double maxLod = Math.log(size) / Math.log(2f);
+        double log2 = Math.log(omegaS / omegaP) / Math.log(2);
+        return Math.min(Math.max(0.5f * (float) log2 + mipBias, 0.0f), (float) maxLod);
+    }
+
+
+    private float ggx(float NoH, float alpha) {
+        // use GGX / Trowbridge-Reitz, same as Disney and Unreal 4
+        // cf http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p3
+        float tmp = alpha / (NoH * NoH * (alpha * alpha - 1.0f) + 1.0f);
+        return tmp * tmp * (1f / FastMath.PI);
+    }
+
+    private Vector3f rotateDirection(float angle, Vector3f l, Vector3f store) {
+        float s, c, t;
+
+        s = FastMath.sin(angle);
+        c = FastMath.cos(angle);
+        t = 1.f - c;
+
+        store.x = l.x * c + l.y * s;
+        store.y = -l.x * s + l.y * c;
+        store.z = l.z * (t + c);
+        return store;
+    }
+
+    /**
+     * Computes GGX half vector in local space
+     *
+     * @param xi
+     * @param a2
+     * @param store
+     * @return
+     */
+    public Vector3f importanceSampleGGX(Vector4f xi, float a2, Vector3f store) {
         if (store == null) {
             store = new Vector3f();
         }
@@ -243,22 +345,9 @@ public class PrefilteredEnvMapFaceGenerator extends RunnableWithProgress {
         float sinThetaCosPhi = sinTheta * xi.z;//xi.z is cos(phi)
         float sinThetaSinPhi = sinTheta * xi.w;//xi.w is sin(phi)
 
-        Vector3f upVector = Vector3f.UNIT_X;
-
-        if (abs(normal.z) < 0.999) {
-            upVector = Vector3f.UNIT_Y;
-        }
-
-        Vector3f tangentX = tmp1.set(upVector).crossLocal(normal).normalizeLocal();
-        Vector3f tangentY = tmp2.set(normal).crossLocal(tangentX);
-
-        // Tangent to world space
-        tangentX.multLocal(sinThetaCosPhi);
-        tangentY.multLocal(sinThetaSinPhi);
-        tmp3.set(normal).multLocal(cosTheta);
-
-        // Tangent to world space
-        store.set(tangentX).addLocal(tangentY).addLocal(tmp3);
+        store.x = sinThetaCosPhi;
+        store.y = sinThetaSinPhi;
+        store.z = cosTheta;
 
         return store;
     }

+ 2 - 2
jme3-core/src/main/java/com/jme3/environment/generation/RunnableWithProgress.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -61,7 +61,7 @@ public abstract class RunnableWithProgress implements Runnable {
     }
 
     /**
-     * return the curent progress of the process.
+     * return the current progress of the process.
      *
      * @return
      */

+ 25 - 9
jme3-core/src/main/java/com/jme3/environment/util/CubeMapWrapper.java

@@ -31,17 +31,15 @@
  */
 package com.jme3.environment.util;
 
-import com.jme3.environment.util.EnvMapUtils;
-import com.jme3.math.ColorRGBA;
-import static com.jme3.math.FastMath.pow;
-import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
+import com.jme3.math.*;
 import com.jme3.texture.Image;
 import com.jme3.texture.TextureCubeMap;
 import com.jme3.texture.image.DefaultImageRaster;
 import com.jme3.texture.image.MipMapImageRaster;
 import com.jme3.util.BufferUtils;
 
+import static com.jme3.math.FastMath.pow;
+
 /**
  * Wraps a Cube map and allows to read from or write pixels into it.
  * 
@@ -57,6 +55,8 @@ public class CubeMapWrapper {
     private final Vector2f uvs = new Vector2f();
     private final Image image;
 
+    private final ColorRGBA tmpColor = new ColorRGBA();
+
     /**
      * Creates a CubeMapWrapper for the given cube map
      * Note that the cube map must be initialized, and the mipmaps sizes should 
@@ -105,7 +105,7 @@ public class CubeMapWrapper {
      * @param store the color in which to store the pixel color read.
      * @return the color of the pixel read.
      */
-    public ColorRGBA getPixel(Vector3f vector, int mipLevel, ColorRGBA store) {
+    public ColorRGBA getPixel(Vector3f vector, float mipLevel, ColorRGBA store) {
         if (mipMapRaster == null) {
             throw new IllegalArgumentException("This cube map has no mip maps");
         }
@@ -113,10 +113,26 @@ public class CubeMapWrapper {
             store = new ColorRGBA();
         }
 
-        int face = EnvMapUtils.getCubemapFaceTexCoordFromVector(vector, sizes[mipLevel], uvs, EnvMapUtils.FixSeamsMethod.Stretch);
+        int lowerMipLevel = (int) mipLevel;
+        int higherMipLevel = (int) FastMath.ceil(mipLevel);
+        float ratio = mipLevel - lowerMipLevel;
+
+        int face = EnvMapUtils.getCubemapFaceTexCoordFromVector(vector, sizes[lowerMipLevel], uvs, EnvMapUtils.FixSeamsMethod.Stretch);
         mipMapRaster.setSlice(face);
-        mipMapRaster.setMipLevel(mipLevel);
-        return mipMapRaster.getPixel((int) uvs.x, (int) uvs.y, store);
+        mipMapRaster.setMipLevel(lowerMipLevel);
+        mipMapRaster.getPixel((int) uvs.x, (int) uvs.y, store);
+
+        face = EnvMapUtils.getCubemapFaceTexCoordFromVector(vector, sizes[higherMipLevel], uvs, EnvMapUtils.FixSeamsMethod.Stretch);
+        mipMapRaster.setSlice(face);
+        mipMapRaster.setMipLevel(higherMipLevel);
+        mipMapRaster.getPixel((int) uvs.x, (int) uvs.y, tmpColor);
+
+        store.r = FastMath.interpolateLinear(ratio, store.r, tmpColor.r);
+        store.g = FastMath.interpolateLinear(ratio, store.g, tmpColor.g);
+        store.b = FastMath.interpolateLinear(ratio, store.b, tmpColor.b);
+        store.a = FastMath.interpolateLinear(ratio, store.a, tmpColor.a);
+
+        return store;
     }
 
     /**

+ 14 - 8
jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java

@@ -37,18 +37,15 @@ import com.jme3.math.*;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Node;
 import com.jme3.scene.shape.Quad;
-import com.jme3.texture.Image;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture2D;
-import com.jme3.texture.TextureCubeMap;
+import com.jme3.texture.*;
 import com.jme3.texture.image.ColorSpace;
 import com.jme3.ui.Picture;
 import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+
 import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import static com.jme3.math.FastMath.*;
 
-import com.jme3.util.TempVars;
+import static com.jme3.math.FastMath.*;
 
 /**
  *
@@ -88,6 +85,11 @@ public class EnvMapUtils {
         None
     }
 
+    public static enum GenerationType {
+        Fast,
+        HighQuality
+    }
+
     /**
      * Creates a cube map from 6 images
      *
@@ -117,6 +119,8 @@ public class EnvMapUtils {
         cubeImage.addData(backImg.getData(0));
         cubeImage.addData(frontImg.getData(0));
 
+        cubeImage.setMipMapSizes(rightImg.getMipMapSizes());
+
         TextureCubeMap cubeMap = new TextureCubeMap(cubeImage);
         cubeMap.setAnisotropicFilter(0);
         cubeMap.setMagFilter(Texture.MagFilter.Bilinear);
@@ -148,6 +152,8 @@ public class EnvMapUtils {
             cubeImage.addData(d.duplicate());
         }
 
+        cubeImage.setMipMapSizes(srcImg.getMipMapSizes());
+
         TextureCubeMap cubeMap = new TextureCubeMap(cubeImage);
         cubeMap.setAnisotropicFilter(sourceMap.getAnisotropicFilter());
         cubeMap.setMagFilter(sourceMap.getMagFilter());
@@ -730,7 +736,7 @@ public class EnvMapUtils {
         pem.setMagFilter(Texture.MagFilter.Bilinear);
         pem.setMinFilter(Texture.MinFilter.Trilinear);
         pem.getImage().setColorSpace(ColorSpace.Linear);
-        int nbMipMap = (int) (Math.log(size) / Math.log(2) - 1);
+        int nbMipMap = Math.min(6, (int) (Math.log(size) / Math.log(2)));
         CubeMapWrapper targetWrapper = new CubeMapWrapper(pem);
         targetWrapper.initMipMaps(nbMipMap);
         return pem;

+ 2 - 2
jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -114,7 +114,7 @@ public class LightsDebugState extends BaseAppState {
     }
 
     /**
-     * Set the scenes for wich to render light gizmos.
+     * Set the scenes for which to render light gizmos.
      * @param scene 
      */
     public void setScene(Spatial scene) {

+ 6 - 8
jme3-core/src/main/java/com/jme3/input/ChaseCamera.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,6 @@ package com.jme3.input;
 import com.jme3.export.InputCapsule;
 import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;
-import com.jme3.export.OutputCapsule;
 import com.jme3.input.controls.*;
 import com.jme3.math.FastMath;
 import com.jme3.math.Vector3f;
@@ -420,7 +419,7 @@ public class ChaseCamera implements ActionListener, AnalogListener, Control, Jme
 
                 //the user is rotating the cam by dragging the mouse
                 if (canRotate) {
-                    //reseting the trailing lerp factor
+                    //reset the trailing lerp factor
                     trailingLerpFactor = 0;
                     //stop trailing user has the control
                     trailing = false;
@@ -582,18 +581,17 @@ public class ChaseCamera implements ActionListener, AnalogListener, Control, Jme
 
     /**
      * clone this camera for a spatial
+     *
      * @param spatial
      * @return
      */
+    @Deprecated
     @Override
     public Control cloneForSpatial(Spatial spatial) {
-        ChaseCamera cc = new ChaseCamera(cam, spatial, inputManager);
-        cc.setMaxDistance(getMaxDistance());
-        cc.setMinDistance(getMinDistance());
-        return cc;
+        throw new UnsupportedOperationException();
     }
 
-    @Override   
+    @Override
     public Object jmeClone() {
         ChaseCamera cc = new ChaseCamera(cam, inputManager);
         cc.target = target;

+ 14 - 7
jme3-core/src/main/java/com/jme3/input/InputManager.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -423,15 +423,22 @@ public class InputManager implements RawInputListener {
 
     /**
      * Callback from RawInputListener. Do not use.
+     *
+     * @param evt event to add to the input queue (not null)
      */
     @Override
     public void onMouseMotionEvent(MouseMotionEvent evt) {
-        if (!eventsPermitted) {
-            throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
-        }
-
+        /*
+         * If events aren't allowed, the event may be a "first mouse event"
+         * triggered by the constructor setting the mouse listener.
+         * In that case, use the event to initialize the cursor position,
+         * but don't queue it for further processing.
+         * This is part of the fix for issue #792.
+         */
         cursorPos.set(evt.getX(), evt.getY());
-        inputQueue.add(evt);
+        if (eventsPermitted) {
+            inputQueue.add(evt);
+        }
     }
 
     private void onMouseButtonEventQueued(MouseButtonEvent evt) {
@@ -867,7 +874,7 @@ public class InputManager implements RawInputListener {
             // larynx, 2011.06.10 - flag event as reusable because
             // the android input uses a non-allocating ringbuffer which
             // needs to know when the event is not anymore in inputQueue
-            // and therefor can be reused.
+            // and therefore can be reused.
             event.setConsumed();
         }
 

+ 2 - 2
jme3-core/src/main/java/com/jme3/input/Joystick.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -123,7 +123,7 @@ public interface Joystick {
 
     /**
      * Returns the POV Y axis for this joystick.  This is a convenience axis 
-     * providing an y-axis subview of the HAT axis.
+     * providing a y-axis subview of the HAT axis.
      *
      * @see JoystickAxis#assignAxis(java.lang.String, java.lang.String)
      */

+ 10 - 0
jme3-core/src/main/java/com/jme3/input/KeyInput.java

@@ -445,6 +445,16 @@ public interface KeyInput extends Input {
      * (J3100).
      */
     public static final int KEY_UNLABELED = 0x97;
+    /**
+     * PrtScr key.
+     * Note: for use on keyboards with a PrtScr key that is
+     * separate from the SysRq key. Most keyboards combine
+     * SysRq and PrtScr so if the intent is to actually
+     * capture the user's desire to capture the screen
+     * then SysRq is the most likely scan code.
+     * Use PrtScr to catch the rest (laptops, mini-keyboards, etc.)
+     */
+    public static final int KEY_PRTSCR = 0x9A;
     /**
      * Enter key (num pad).
      */

+ 2 - 5
jme3-core/src/main/java/com/jme3/light/LightProbe.java

@@ -127,15 +127,13 @@ public class LightProbe extends Light implements Savable {
     public void read(JmeImporter im) throws IOException {
         super.read(im);
         InputCapsule ic = im.getCapsule(this);
-
-
+        
         prefilteredEnvMap = (TextureCubeMap) ic.readSavable("prefilteredEnvMap", null);
-        position = (Vector3f) ic.readSavable("position", this);
+        position = (Vector3f) ic.readSavable("position", null);
         bounds = (BoundingVolume) ic.readSavable("bounds", new BoundingSphere(1.0f, Vector3f.ZERO));
         nbMipMaps = ic.readInt("nbMipMaps", 0);
         ready = ic.readBoolean("ready", false);
 
-
         Savable[] coeffs = ic.readSavableArray("shCoeffs", null);
         if (coeffs == null) {
             ready = false;
@@ -145,7 +143,6 @@ public class LightProbe extends Light implements Savable {
             for (int i = 0; i < coeffs.length; i++) {
                 shCoeffs[i] = (Vector3f) coeffs[i];
             }
-
         }
     }
 

+ 2 - 2
jme3-core/src/main/java/com/jme3/light/LightProbeBlendingStrategy.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2015 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -48,7 +48,7 @@ public interface LightProbeBlendingStrategy {
     public void registerProbe(LightProbe probe);
     /**
      * Populates the resulting light probes into the given light list.
-     * @param g the geometry for wich the light list is computed
+     * @param g the geometry for which the light list is computed
      * @param lightList the result light list
      */
     public void populateProbes(Geometry g, LightList lightList);

+ 3 - 3
jme3-core/src/main/java/com/jme3/light/PointLight.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012, 2015-2016 jMonkeyEngine
+ * Copyright (c) 2009-2012, 2015-2016, 2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -54,7 +54,7 @@ import java.io.IOException;
  * <p>
  * In addition to a position, point lights also have a radius which 
  * can be used to attenuate the influence of the light depending on the 
- * distance between the light and the effected object.
+ * distance between the light and the affected object.
  * 
  */
 public class PointLight extends Light {
@@ -155,7 +155,7 @@ public class PointLight extends Light {
      * Setting a non-zero radius indicates the light should use attenuation.
      * If a pixel's distance to this light's position
      * is greater than the light's radius, then the pixel will not be
-     * effected by this light, if the distance is less than the radius, then
+     * affected by this light, if the distance is less than the radius, then
      * the magnitude of the influence is equal to distance / radius.
      * 
      * @param radius the radius of the light influence.

+ 2 - 2
jme3-core/src/main/java/com/jme3/light/SpotLight.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012, 2015-2016 jMonkeyEngine
+ * Copyright (c) 2009-2012, 2015-2016, 2018 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -337,7 +337,7 @@ public class SpotLight extends Light {
      * Setting a non-zero range indicates the light should use attenuation.
      * If a pixel's distance to this light's position
      * is greater than the light's range, then the pixel will not be
-     * effected by this light, if the distance is less than the range, then
+     * affected by this light, if the distance is less than the range, then
      * the magnitude of the influence is equal to distance / range.
      * 
      * @param spotRange the range of the light influence.

+ 45 - 0
jme3-core/src/main/java/com/jme3/material/Materials.java

@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2018 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.material;
+
+/**
+ * This class is to provide some constants materials.
+ *
+ * @author oualid
+ */
+public class Materials {
+
+    public static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md";
+    public static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md";
+    public static final String PBR = "Common/MatDefs/Light/PBRLighting.j3md";
+
+}

+ 3 - 1
jme3-core/src/main/java/com/jme3/material/ShaderGenerationInfo.java

@@ -206,7 +206,9 @@ public class ShaderGenerationInfo implements Savable, Cloneable {
             clone.vertexUniforms.add(uniform.clone());
         }
 
-        clone.vertexGlobal = vertexGlobal.clone();
+        if (vertexGlobal != null) {
+            clone.vertexGlobal = vertexGlobal.clone();
+        }
 
         for (ShaderNodeVariable varying : varyings) {
             clone.varyings.add(varying.clone());

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است