Pārlūkot izejas kodu

Merge remote-tracking branch 'upstream/master'

Conflicts:
	jme3-bullet-native-android/build.gradle
Daniel Johansson 10 gadi atpakaļ
vecāks
revīzija
9ba90251d6
29 mainītis faili ar 594 papildinājumiem un 184 dzēšanām
  1. 2 3
      .travis.yml
  2. 3 1
      common.gradle
  3. 0 1
      jme3-bullet-native-android/build.gradle
  4. 2 2
      jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp
  5. 5 0
      jme3-core/build.gradle
  6. 3 6
      jme3-core/src/main/java/com/jme3/renderer/RenderContext.java
  7. 1 0
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java
  8. 78 56
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  9. 281 77
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java
  10. 2 0
      jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java
  11. 2 1
      jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md
  12. 1 1
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.frag
  13. 1 1
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.vert
  14. 0 14
      jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib
  15. 34 0
      jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib
  16. 5 12
      jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
  17. 117 0
      jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java
  18. 11 0
      jme3-core/src/test/resources/texture-parameters-newstyle.j3m
  19. 6 0
      jme3-core/src/test/resources/texture-parameters-oldstyle.j3m
  20. 1 1
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java
  21. 1 1
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java
  22. 1 1
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java
  23. 1 1
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java
  24. 13 0
      jme3-niftygui/build.gradle
  25. 3 0
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneEditTool.java
  26. 16 3
      sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java
  27. 1 1
      sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties
  28. 2 0
      sdk/nbproject/platform.properties
  29. 1 1
      settings.gradle

+ 2 - 3
.travis.yml

@@ -25,7 +25,7 @@ install:
 script:
   - ./gradlew check
   - ./gradlew createZipDistribution
-  - [ $TRAVIS_BRANCH == 'master' ] && [ $TRAVIS_PULL_REQUEST == 'false' ] && ./gradlew uploadArchives || :
+  - "[ $TRAVIS_BRANCH == 'master' ] && [ $TRAVIS_PULL_REQUEST == 'false' ] && ./gradlew uploadArchives || :"
 
 before_deploy:
   - export RELEASE_DIST=$(ls build/distributions/*.zip)
@@ -43,8 +43,7 @@ deploy:
 
 before_install:
   - git fetch --unshallow
-  - openssl aes-256-cbc -K $encrypted_a1949b55824a_key -iv $encrypted_a1949b55824a_iv 
-    -in private/www-updater.key.enc -out private/www-updater.key -d
+  - "[ $TRAVIS_PULL_REQUEST == 'false' ] && openssl aes-256-cbc -K $encrypted_a1949b55824a_key -iv $encrypted_a1949b55824a_iv -in private/www-updater.key.enc -out private/www-updater.key -d || :"
 
 # before_install:
   # required libs for android build tools

+ 3 - 1
common.gradle

@@ -27,7 +27,9 @@ configurations {
 
 dependencies {
     // Adding dependencies here will add the dependencies to each subproject.
-    testCompile group: 'junit', name: 'junit', version: '4.10'
+    testCompile group: 'junit', name: 'junit', version: '4.12'
+    testCompile group: 'org.mockito', name: 'mockito-core', version: '2.0.28-beta'
+    testCompile group: 'org.easytesting', name: 'fest-assert-core', version: '2.0M10'
     deployerJars "org.apache.maven.wagon:wagon-ssh:2.9"
 }
 

+ 0 - 1
jme3-bullet-native-android/build.gradle

@@ -21,7 +21,6 @@ if (!hasProperty('mainClass')) {
 }
 
 dependencies {
-//    compile project(':jme3-bullet-native')
     compile project(':jme3-bullet')
 }
 

+ 2 - 2
jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp

@@ -351,7 +351,7 @@ void jmeBulletUtil::addResult(JNIEnv* env, jobject resultlist, btVector3* hitnor
     env->SetFloatField(singleresult, jmeClasses::PhysicsRay_hitfraction, m_hitFraction);
 
     env->SetObjectField(singleresult, jmeClasses::PhysicsRay_collisionObject, up1->javaCollisionObject);
-    env->CallVoidMethod(resultlist, jmeClasses::PhysicsRay_addmethod, singleresult);
+    env->CallBooleanMethod(resultlist, jmeClasses::PhysicsRay_addmethod, singleresult);
     if (env->ExceptionCheck()) {
         env->Throw(env->ExceptionOccurred());
         return;
@@ -371,7 +371,7 @@ void jmeBulletUtil::addSweepResult(JNIEnv* env, jobject resultlist, btVector3* h
 	env->SetFloatField(singleresult, jmeClasses::PhysicsSweep_hitfraction, m_hitFraction);
 
 	env->SetObjectField(singleresult, jmeClasses::PhysicsSweep_collisionObject, up1->javaCollisionObject);
-	env->CallVoidMethod(resultlist, jmeClasses::PhysicsSweep_addmethod, singleresult);
+	env->CallBooleanMethod(resultlist, jmeClasses::PhysicsSweep_addmethod, singleresult);
 	if (env->ExceptionCheck()) {
 		env->Throw(env->ExceptionOccurred());
 		return;

+ 5 - 0
jme3-core/build.gradle

@@ -10,6 +10,11 @@ sourceSets {
             srcDir 'src/tools/java'
         }
     }
+    test {
+        java {
+            srcDir 'src/test/java'
+        }
+    }
 }
 
 task updateVersionPropertiesFile << {

+ 3 - 6
jme3-core/src/main/java/com/jme3/renderer/RenderContext.java

@@ -241,12 +241,12 @@ public class RenderContext {
     public IDList attribIndexList = new IDList();
     
     /**
-     * depth tets function
+     * depth test function
      */
-    public RenderState.TestFunction depthFunc = RenderState.TestFunction.LessOrEqual;
+    public RenderState.TestFunction depthFunc = RenderState.TestFunction.Less;
     
      /**
-     * alpha tets function
+     * alpha test function
      */
     public RenderState.TestFunction alphaFunc = RenderState.TestFunction.Greater;
 
@@ -255,8 +255,6 @@ public class RenderContext {
     
     public ColorRGBA clearColor = new ColorRGBA(0,0,0,0);
     
-    public boolean seamlessCubemap = false;
-    
     /**
      * Reset the RenderContext to default GL state
      */
@@ -308,6 +306,5 @@ public class RenderContext {
         depthFunc = RenderState.TestFunction.LessOrEqual;    
         alphaFunc = RenderState.TestFunction.Greater;
         clearColor.set(0,0,0,0);
-        seamlessCubemap = false;
     }
 }

+ 1 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java

@@ -161,6 +161,7 @@ public interface GL {
         public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
         public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
         public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
+        public static final int GL_TEXTURE_BASE_LEVEL = 0x813C;
 	public static final int GL_TEXTURE_MAG_FILTER = 0x2800;
 	public static final int GL_TEXTURE_MAX_LEVEL = 0x813D;
 	public static final int GL_TEXTURE_MIN_FILTER = 0x2801;

+ 78 - 56
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -503,6 +503,11 @@ public class GLRenderer implements Renderer {
 
         // Initialize default state..
         gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
+        
+        if (caps.contains(Caps.SeamlessCubemap)) {
+            // Enable this globally. Should be OK.
+            gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
+        }
 
         if (caps.contains(Caps.CoreProfile)) {
             // Core Profile requires VAO to be bound.
@@ -609,17 +614,16 @@ public class GLRenderer implements Renderer {
 
         if (state.isDepthTest() && !context.depthTestEnabled) {
             gl.glEnable(GL.GL_DEPTH_TEST);
-            gl.glDepthFunc(convertTestFunction(context.depthFunc));
             context.depthTestEnabled = true;
         } else if (!state.isDepthTest() && context.depthTestEnabled) {
             gl.glDisable(GL.GL_DEPTH_TEST);
             context.depthTestEnabled = false;
         }
-        if (state.getDepthFunc() != context.depthFunc) {
+        if (state.isDepthTest() && state.getDepthFunc() != context.depthFunc) {
             gl.glDepthFunc(convertTestFunction(state.getDepthFunc()));
             context.depthFunc = state.getDepthFunc();
         }
-
+        
         if (state.isDepthWrite() && !context.depthWriteEnabled) {
             gl.glDepthMask(true);
             context.depthWriteEnabled = true;
@@ -1428,7 +1432,7 @@ public class GLRenderer implements Renderer {
             // NOTE: For depth textures, sets nearest/no-mips mode
             // Required to fix "framebuffer unsupported"
             // for old NVIDIA drivers!
-            setupTextureParams(tex);
+            setupTextureParams(0, tex);
         }
 
         glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT,
@@ -1812,7 +1816,7 @@ public class GLRenderer implements Renderer {
     }
 
     @SuppressWarnings("fallthrough")
-    private void setupTextureParams(Texture tex) {
+    private void setupTextureParams(int unit, Texture tex) {
         Image image = tex.getImage();
         int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1);
 
@@ -1825,32 +1829,23 @@ public class GLRenderer implements Renderer {
         // filter things
         if (image.getLastTextureState().magFilter != tex.getMagFilter()) {
             int magFilter = convertMagFilter(tex.getMagFilter());
+            bindTextureAndUnit(target, image, unit);
             gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter);
             image.getLastTextureState().magFilter = tex.getMagFilter();
         }
         if (image.getLastTextureState().minFilter != tex.getMinFilter()) {
             int minFilter = convertMinFilter(tex.getMinFilter(), haveMips);
+            bindTextureAndUnit(target, image, unit);
             gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter);
             image.getLastTextureState().minFilter = tex.getMinFilter();
         }
-        if (caps.contains(Caps.SeamlessCubemap) && tex.getType() == Texture.Type.CubeMap) {
-            if (haveMips && !context.seamlessCubemap) {
-                // We can enable seamless cubemap filtering.
-                gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
-                context.seamlessCubemap = true;
-            } else if (!haveMips && context.seamlessCubemap) {
-                // For skyboxes (no mipmaps), disable seamless cubemap filtering.
-                gl.glDisable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
-                context.seamlessCubemap = false;
-            }
-        }
-
-        if (tex.getAnisotropicFilter() > 1) {
-            if (caps.contains(Caps.TextureFilterAnisotropic)) {
-                gl.glTexParameterf(target,
-                        GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT,
-                        tex.getAnisotropicFilter());
-            }
+        if (caps.contains(Caps.TextureFilterAnisotropic)
+                && image.getLastTextureState().anisoFilter != tex.getAnisotropicFilter()) {
+            bindTextureAndUnit(target, image, unit);
+            gl.glTexParameterf(target,
+                    GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT,
+                    tex.getAnisotropicFilter());
+            image.getLastTextureState().anisoFilter = tex.getAnisotropicFilter();
         }
 
         // repeat modes
@@ -1858,6 +1853,7 @@ public class GLRenderer implements Renderer {
             case ThreeDimensional:
             case CubeMap: // cubemaps use 3D coords
                 if (gl2 != null && image.getLastTextureState().rWrap != tex.getWrap(WrapAxis.R)) {
+                    bindTextureAndUnit(target, image, unit);
                     gl2.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
                     image.getLastTextureState().rWrap = tex.getWrap(WrapAxis.R);
                 }
@@ -1865,10 +1861,12 @@ public class GLRenderer implements Renderer {
             case TwoDimensional:
             case TwoDimensionalArray:
                 if (image.getLastTextureState().tWrap != tex.getWrap(WrapAxis.T)) {
+                    bindTextureAndUnit(target, image, unit);
                     gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
                     image.getLastTextureState().tWrap = tex.getWrap(WrapAxis.T);
                 }
                 if (image.getLastTextureState().sWrap != tex.getWrap(WrapAxis.S)) {
+                    bindTextureAndUnit(target, image, unit);
                     gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
                     image.getLastTextureState().sWrap = tex.getWrap(WrapAxis.S);
                 }
@@ -1877,9 +1875,10 @@ public class GLRenderer implements Renderer {
                 throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
         }
 
-        if(tex.isNeedCompareModeUpdate() && gl2 != null){
+        if (tex.isNeedCompareModeUpdate() && gl2 != null) {
             // R to Texture compare mode
             if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) {
+                bindTextureAndUnit(target, image, unit);
                 gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE);
                 gl2.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY);
                 if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) {
@@ -1887,12 +1886,16 @@ public class GLRenderer implements Renderer {
                 } else {
                     gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL);
                 }
-            }else{
+            } else {
+                bindTextureAndUnit(target, image, unit);
                 //restoring default value
                 gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE);
             }
             tex.compareModeUpdated();
         }
+        
+        // If at this point we didn't bind the texture, bind it now
+        bindTextureOnly(target, image, unit);
     }
 
     /**
@@ -1950,6 +1953,50 @@ public class GLRenderer implements Renderer {
         }
     }
 
+    /**
+     * Ensures that the texture is bound to the given unit
+     * and that the unit is currently active (for modification).
+     * 
+     * @param target The texture target, one of GL_TEXTURE_***
+     * @param img The image texture to bind
+     * @param unit At what unit to bind the texture.
+     */
+    private void bindTextureAndUnit(int target, Image img, int unit) {
+        if (context.boundTextureUnit != unit) {
+            gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
+            context.boundTextureUnit = unit;
+        }
+        if (context.boundTextures[unit] != img) {
+            gl.glBindTexture(target, img.getId());
+            context.boundTextures[unit] = img;
+            statistics.onTextureUse(img, true);
+        } else {
+            statistics.onTextureUse(img, false);
+        }
+    }
+    
+    /**
+     * Ensures that the texture is bound to the given unit,
+     * but does not care if the unit is active (for rendering).
+     * 
+     * @param target The texture target, one of GL_TEXTURE_***
+     * @param img The image texture to bind
+     * @param unit At what unit to bind the texture.
+     */
+    private void bindTextureOnly(int target, Image img, int unit) {
+        if (context.boundTextures[unit] != img) {
+            if (context.boundTextureUnit != unit) {
+                gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
+                context.boundTextureUnit = unit;
+            }
+            gl.glBindTexture(target, img.getId());
+            context.boundTextures[unit] = img;
+            statistics.onTextureUse(img, true);
+        } else {
+            statistics.onTextureUse(img, false);
+        }
+    }
+    
     /**
      * Uploads the given image to the GL driver.
      *
@@ -1971,19 +2018,9 @@ public class GLRenderer implements Renderer {
             statistics.onNewTexture();
         }
 
-        // bind texture       
+        // bind texture
         int target = convertTextureType(type, img.getMultiSamples(), -1);
-        if (context.boundTextures[unit] != img) {
-            if (context.boundTextureUnit != unit) {
-                gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
-                context.boundTextureUnit = unit;
-            }
-
-            gl.glBindTexture(target, texId);
-            context.boundTextures[unit] = img;
-
-            statistics.onTextureUse(img, true);
-        }
+        bindTextureAndUnit(target, img, unit);
 
         if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
             // Image does not have mipmaps, but they are required.
@@ -2092,6 +2129,7 @@ public class GLRenderer implements Renderer {
         img.clearUpdateNeeded();
     }
 
+    @Override
     public void setTexture(int unit, Texture tex) {
         Image image = tex.getImage();
         if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) {
@@ -2118,24 +2156,7 @@ public class GLRenderer implements Renderer {
         int texId = image.getId();
         assert texId != -1;
 
-        Image[] textures = context.boundTextures;
-
-        int type = convertTextureType(tex.getType(), image.getMultiSamples(), -1);
-        if (textures[unit] != image) {
-            if (context.boundTextureUnit != unit) {
-                gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
-                context.boundTextureUnit = unit;
-            }
-
-            gl.glBindTexture(type, texId);
-            textures[unit] = image;
-
-            statistics.onTextureUse(image, true);
-        } else {
-            statistics.onTextureUse(image, false);
-        }
-
-        setupTextureParams(tex);
+        setupTextureParams(unit, tex);
     }
 
     public void modifyTexture(Texture tex, Image pixels, int x, int y) {
@@ -2629,12 +2650,13 @@ public class GLRenderer implements Renderer {
             }
         }
 
+        clearVertexAttribs();
+        
         if (indices != null) {
             drawTriangleList(indices, mesh, count);
         } else {
             drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
         }
-        clearVertexAttribs();
     }
 
     public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {

+ 281 - 77
jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java

@@ -36,8 +36,14 @@ import java.lang.reflect.Field;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.nio.Buffer;
 import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
 import java.nio.IntBuffer;
+import java.nio.LongBuffer;
+import java.nio.ShortBuffer;
 import java.util.HashMap;
 
 /**
@@ -51,6 +57,17 @@ public final class GLTracer implements InvocationHandler {
     private final IntMap<String> constMap;
     private static final HashMap<String, IntMap<Void>> nonEnumArgMap = new HashMap<String, IntMap<Void>>();
     
+    private static final String ANSI_RESET = "\u001B[0m";
+    private static final String ANSI_BRIGHT = "\u001B[1m";
+    private static final String ANSI_BLACK = "\u001B[30m";
+    private static final String ANSI_RED = "\u001B[31m";
+    private static final String ANSI_GREEN = "\u001B[32m";
+    private static final String ANSI_YELLOW = "\u001B[33m";
+    private static final String ANSI_BLUE = "\u001B[34m";
+    private static final String ANSI_MAGENTA = "\u001B[35m";
+    private static final String ANSI_CYAN = "\u001B[36m";
+    private static final String ANSI_WHITE = "\u001B[37m";
+
     private static void noEnumArgs(String method, int... argSlots) {
         IntMap<Void> argSlotsMap = new IntMap<Void>();
         for (int argSlot : argSlots) {
@@ -174,100 +191,287 @@ public final class GLTracer implements InvocationHandler {
                                       new GLTracer(glInterface, constMap));
     }
     
-    private String translateInteger(String method, int value, int argIndex) {
-        IntMap<Void> argSlotMap = nonEnumArgMap.get(method);
-        if (argSlotMap != null && argSlotMap.containsKey(argIndex)) {
-            return Integer.toString(value);
-        }
+    private void printStyle(String style, String string) {
+        System.out.print(style + string + ANSI_RESET);
+    }
+    
+    private void print(String string) {
+        System.out.print(string);
+    }
+    
+    private void printInt(int value) {
+        print(Integer.toString(value));
+    }
+    
+    private void printEnum(int value) {
         String enumName = constMap.get(value);
         if (enumName != null) {
-            return enumName;
+            if (enumName.startsWith("GL_")) {
+                enumName = enumName.substring(3);
+            }
+            if (enumName.endsWith("_EXT") || enumName.endsWith("_ARB")) {
+                enumName = enumName.substring(0, enumName.length() - 4);
+            }
+            printStyle(ANSI_GREEN, enumName);
+        } else {
+            printStyle(ANSI_GREEN, "ENUM_" + Integer.toHexString(value));
+        }
+    }
+    
+    private void printIntOrEnum(String method, int value, int argIndex) {
+        IntMap<Void> argSlotMap = nonEnumArgMap.get(method);
+        if (argSlotMap != null && argSlotMap.containsKey(argIndex)) {
+            printInt(value);
         } else {
-            return "GL_ENUM_" + Integer.toHexString(value);
-            //throw new IllegalStateException("Untranslatable enum encountered on " + method + 
-            //                                " at argument " + argIndex + " with value " + value);
+            printEnum(value);
         }
     }
     
-    private String translateString(String value) {
-        return "\"" + value.replaceAll("\0", "\\\\0") + "\"";
+    private void printNewLine() {
+        System.out.println();
     }
     
-    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-        Object result = method.invoke(obj, args);
-        String methodName = method.getName();
+    private void printString(String value) {
+        if (value.length() > 150) {
+            value = value.substring(0, 150) + "...";
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append(ANSI_YELLOW);
+        sb.append("\"");
+        sb.append(ANSI_RESET);
+        for (String line : value.split("\n")) {
+            sb.append(ANSI_YELLOW);
+            sb.append(line.replaceAll("\0", "\\\\0"));
+            sb.append(ANSI_RESET);
+            sb.append("\n");
+        }
+        if (sb.length() > 1 && sb.charAt(sb.length() - 1) == '\n') {
+            sb.setLength(sb.length() - 1);
+        }
+        sb.append(ANSI_YELLOW);
+        sb.append("\"");
+        sb.append(ANSI_RESET);
+        print(sb.toString());
+    }
+    
+    private void printBoolean(boolean bool) {
+        printStyle(ANSI_BLUE, bool ? "true" : "false");
+    }
+    
+    private void printBuffer(Buffer buffer) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ANSI_MAGENTA);
+        if (buffer instanceof ByteBuffer) {
+            sb.append("byte");
+        } else if (buffer instanceof ShortBuffer) {
+            sb.append("short");
+        } else if (buffer instanceof CharBuffer) { 
+            sb.append("char");
+        } else if (buffer instanceof FloatBuffer) {
+            sb.append("float");
+        } else if (buffer instanceof IntBuffer) {
+            sb.append("int");
+        } else if (buffer instanceof LongBuffer) {
+            sb.append("long");
+        } else if (buffer instanceof DoubleBuffer) {
+            sb.append("double");
+        } else {
+            throw new UnsupportedOperationException();
+        }
+        sb.append(ANSI_RESET);
+        sb.append("[");
+        
+        if (buffer.position() == 0
+                && buffer.limit() == buffer.capacity()) {
+            // Common case. Just print buffer size.
+            sb.append(buffer.capacity());
+        } else {
+            sb.append("pos=").append(buffer.position());
+            sb.append(" lim=").append(buffer.limit());
+            sb.append(" cap=").append(buffer.capacity());
+        }
+        
+        sb.append("]");
+        print(sb.toString());
+    }
+    
+    private void printMethodName(String methodName) {
         if (methodName.startsWith("gl")) {
-            System.out.print(methodName);
-            System.out.print("(");
-            if (args != null) {
-                Class<?>[] paramTypes = method.getParameterTypes();
-                for (int i = 0; i < args.length; i++) {
-                    if (paramTypes[i] == int.class) {
-                        int val = (Integer)args[i];
-                        System.out.print(translateInteger(methodName, val, i));
-                    } else if (paramTypes[i] == String.class) {
-                        System.out.print(translateString((String)args[i]));
-                    } else if (paramTypes[i] == String[].class) {
-                        String[] arr = (String[]) args[i];
-                        if (arr.length == 1) {
-                            if (arr[0].length() > 150) {
-                                System.out.print("\"" + arr[0].substring(0, 150) + "...\"");
-                            } else {
-                                System.out.print("\"" + arr[0] + "\"");
-                            }
-                        } else {
-                            System.out.print("String[" + arr.length + "]");
-                        }
-                    } else if (args[i] instanceof IntBuffer) {
-                        IntBuffer buf = (IntBuffer) args[i];
-                        if (buf.capacity() == 16) {
-                            int val = buf.get(0);
-                            System.out.print("out=" + translateInteger(methodName, val, i));
-                        } else if (buf.capacity() == 1) {
-                            System.out.print("out=" + buf.get(0));
-                        } else {
-                            System.out.print(args[i]);
-                        }
-                    } else if (args[i] instanceof ByteBuffer) {
-                        ByteBuffer bb = (ByteBuffer)args[i];
-                        if (bb.capacity() == 250) {
-                            if (bb.get(0) != 0) {
-                                System.out.print("out=GL_TRUE");
-                            } else {
-                                System.out.print("out=GL_FALSE");
-                            }
-                        } else {
-                            System.out.print(args[i]);
-                        }
-                    } else {
-                        System.out.print(args[i]);
-                    }
-
-                    if (i != args.length - 1) {
-                        System.out.print(", ");
-                    }
+            // GL calls which actually draw (as opposed to change state)
+            // will be printed in darker color
+            methodName = methodName.substring(2);
+            if (methodName.equals("Clear")
+                    || methodName.equals("DrawRangeElements")) {
+                print(methodName);
+            } else {
+                if (methodName.endsWith("EXT")) {
+                    methodName = methodName.substring(0, methodName.length() - 3);
                 }
+                printStyle(ANSI_BRIGHT, methodName);
             }
+        } else if (methodName.equals("resetStats")) {
+            printStyle(ANSI_RED, "-- frame boundary --");
+        }
+    }
+    
+    private void printArgsClear(int mask) {
+        boolean needAPipe = false;
+        print("(");
+        if ((mask & GL.GL_COLOR_BUFFER_BIT) != 0) {
+            printStyle(ANSI_GREEN, "COLOR_BUFFER_BIT");
+            needAPipe = true;
+        }
+        if ((mask & GL.GL_DEPTH_BUFFER_BIT) != 0) {
+            if (needAPipe) {
+                print(" | ");
+            }
+            printStyle(ANSI_GREEN, "DEPTH_BUFFER_BIT");
+        }
+        if ((mask & GL.GL_STENCIL_BUFFER_BIT) != 0) {
+            if (needAPipe) {
+                print(" | ");
+            }
+            printStyle(ANSI_GREEN, "STENCIL_BUFFER_BIT");
+        }
+        print(")");
+    }
+    
+    private void printArgsTexParameter(Object[] args) {
+        print("(");
 
-            System.out.print(")");
+        int target = (Integer) args[0];
+        int param = (Integer) args[1];
+        int value = (Integer) args[2];
 
-            if (method.getReturnType() != void.class) {
-                if (result instanceof String) {
-                    System.out.println(" = " + translateString((String)result));
-                } else if (method.getReturnType() == int.class) {
-                    int val = (Integer)result;
-                    System.out.println(" = " + translateInteger(methodName, val, -1));
-                } else if (method.getReturnType() == boolean.class) {
-                    boolean val = (Boolean)result;
-                    if (val) System.out.println(" = GL_TRUE");
-                    else System.out.println(" = GL_FALSE");
+        printEnum(target);
+        print(", ");
+        printEnum(param);
+        print(", ");
+        
+        if (param == GL.GL_TEXTURE_BASE_LEVEL
+                || param == GL.GL_TEXTURE_MAX_LEVEL) {
+            printInt(value);
+        } else {
+            printEnum(value);
+        }
+        
+        print(")");
+    }
+    
+    private void printOut() {
+        printStyle(ANSI_CYAN, "out=");
+    }
+    
+    private void printResult(String methodName, Object result, Class<?> returnType) {
+        if (returnType != void.class) {
+            print(" = ");
+            if (result instanceof String) {
+                printString((String) result);
+            } else if (returnType == int.class) {
+                int val = (Integer) result;
+                printIntOrEnum(methodName, val, -1);
+            } else if (returnType == boolean.class) {
+                printBoolean((Boolean)result);
+            } else {
+                print(" = ???");
+            }
+        }
+    }
+    
+    private void printNull() {
+        printStyle(ANSI_BLUE, "null");
+    }
+    
+    private void printArgs(String methodName, Object[] args, Class<?>[] paramTypes) {
+        if (methodName.equals("glClear")) {
+            printArgsClear((Integer)args[0]);
+            return;
+        } else if (methodName.equals("glTexParameteri")) {
+            printArgsTexParameter(args);
+            return;
+        }
+        
+        if (args == null) {
+            print("()");
+            return;
+        }
+        
+        print("(");
+        for (int i = 0; i < args.length; i++) {
+            if (paramTypes[i] == int.class) {
+                int val = (Integer)args[i];
+                printIntOrEnum(methodName, val, i);
+            } else if (paramTypes[i] == boolean.class) {
+                printBoolean((Boolean)args[i]);
+            } else if (paramTypes[i] == String.class) {
+                printString((String)args[i]);
+            } else if (paramTypes[i] == String[].class) {
+                String[] arr = (String[]) args[i];
+                if (arr.length == 1) {
+                    printString(arr[0]);
                 } else {
-                    System.out.println(" = ???");
+                    print("string[" + arr.length + "]");
                 }
+            } else if (args[i] instanceof IntBuffer) {
+                IntBuffer buf = (IntBuffer) args[i];
+                if (buf.capacity() == 16) {
+                    int val = buf.get(0);
+                    printOut();
+                    printIntOrEnum(methodName, val, i);
+                } else if (buf.capacity() == 1) {
+                    printOut();
+                    print(Integer.toString(buf.get(0)));
+                } else {
+                    printBuffer(buf);
+                }
+            } else if (args[i] instanceof ByteBuffer) {
+                ByteBuffer bb = (ByteBuffer)args[i];
+                if (bb.capacity() == 250) {
+                    printOut();
+                    printBoolean(bb.get(0) != 0);
+                } else {
+                    printBuffer(bb);
+                }
+            } else if (args[i] instanceof Buffer) {
+                printBuffer((Buffer)args[i]);
+            } else if (args[i] != null) {
+                print(args[i].toString());
             } else {
-                System.out.println();
+                printNull();
+            }
+
+            if (i != args.length - 1) {
+                System.out.print(", ");
+            }
+        }
+        print(")");
+    }
+    
+    @Override
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        String methodName = method.getName();
+        printMethodName(methodName);
+        
+        if (methodName.startsWith("gl")) {
+            try {
+                // Try to evaluate result first, so we can see output values.
+                Object result = method.invoke(obj, args);
+                printArgs(methodName, args, method.getParameterTypes());
+                printResult(methodName, result, method.getReturnType());
+                printNewLine();
+                return result;
+            } catch (Throwable ex) {
+                // Execution failed, print args anyway
+                // but output values will be incorrect.
+                printArgs(methodName, args, method.getParameterTypes());
+                printNewLine();
+                System.out.println("\tException occurred!");
+                System.out.println(ex.toString());
+                throw ex;
             }
+        } else {
+            printNewLine();
+            return method.invoke(obj, args);
         }
-        return result;
     }
 }

+ 2 - 0
jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java

@@ -45,6 +45,7 @@ public final class LastTextureState {
     public Texture.WrapMode sWrap, tWrap, rWrap;
     public Texture.MagFilter magFilter;
     public Texture.MinFilter minFilter;
+    public int anisoFilter = 0;
     
     public LastTextureState() {
         // All parameters initialized to null (meaning unset).
@@ -56,5 +57,6 @@ public final class LastTextureState {
         rWrap = null;
         magFilter = null;
         minFilter = null;
+        anisoFilter = 0;
     }
 }

+ 2 - 1
jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md

@@ -1,3 +1,4 @@
+Exception This material definition is deprecated. Please use Unshaded.j3md instead.
 MaterialDef Colored Textured {
 
     MaterialParameters {
@@ -17,4 +18,4 @@ MaterialDef Colored Textured {
     Technique {
     }
 
-}
+}

+ 1 - 1
jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.frag

@@ -1,4 +1,4 @@
-#import "Common/ShaderLib/GLSL150Compat.glsllib"
+#import "Common/ShaderLib/GLSLCompat.glsllib"
 
 #if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
     #define NEED_TEXCOORD1

+ 1 - 1
jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.vert

@@ -1,4 +1,4 @@
-#import "Common/ShaderLib/GLSL150Compat.glsllib"
+#import "Common/ShaderLib/GLSLCompat.glsllib"
 #import "Common/ShaderLib/Skinning.glsllib"
 #import "Common/ShaderLib/Instancing.glsllib"
 

+ 0 - 14
jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib

@@ -1,14 +0,0 @@
-#if __VERSION__ >= 130
-out vec4 outFragColor;
-#  define texture1D texture
-#  define texture2D texture
-#  define texture3D texture
-#  define texture2DLod texture
-#  if defined VERTEX_SHADER
-#    define varying out
-#    define attribute in
-#  elif defined FRAGMENT_SHADER
-#    define varying in
-#    define gl_FragColor outFragColor
-#  endif
-#endif

+ 34 - 0
jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib

@@ -0,0 +1,34 @@
+#if defined _GL_ES_
+#  define hfloat highp float
+#  define hvec2  highp vec2
+#  define hvec3  highp vec3
+#  define hvec4  highp vec4
+#  define lfloat lowp float
+#  define lvec2 lowp vec2
+#  define lvec3 lowp vec3
+#  define lvec4 lowp vec4
+#else
+#  define hfloat float
+#  define hvec2  vec2
+#  define hvec3  vec3
+#  define hvec4  vec4
+#  define lfloat float
+#  define lvec2  vec2
+#  define lvec3  vec3
+#  define lvec4  vec4
+#endif
+
+#if __VERSION__ >= 130
+out vec4 outFragColor;
+#  define texture1D texture
+#  define texture2D texture
+#  define texture3D texture
+#  define texture2DLod texture
+#  if defined VERTEX_SHADER
+#    define varying out
+#    define attribute in
+#  elif defined FRAGMENT_SHADER
+#    define varying in
+#    define gl_FragColor outFragColor
+#  endif
+#endif

+ 5 - 12
jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java

@@ -158,7 +158,7 @@ public class J3MLoader implements AssetLoader {
             final String value = values.get(i);
             final TextureOption textureOption = TextureOption.getTextureOption(value);
 
-            if (textureOption == null && !value.contains("\\") && !value.contains("/")) {
+            if (textureOption == null && !value.contains("\\") && !value.contains("/") && !values.get(0).equals("Flip") && !values.get(0).equals("Repeat")) {
                 logger.log(Level.WARNING, "Unknown texture option \"{0}\" encountered for \"{1}\" in material \"{2}\"", new Object[]{value, key, material.getKey().getName()});
             } else if (textureOption != null){
                 final String option = textureOption.getOptionValue(value);
@@ -179,27 +179,25 @@ public class J3MLoader implements AssetLoader {
         final List<TextureOptionValue> textureOptionValues = parseTextureOptions(textureValues);
 
         TextureKey textureKey = null;
-        boolean repeat = false;
 
         // If there is only one token on the value, it must be the path to the texture.
         if (textureValues.size() == 1) {
-            textureKey = new TextureKey(textureValues.get(0));
+            textureKey = new TextureKey(textureValues.get(0), false);
         } else {
             String texturePath = value.trim();
-            boolean flipY = false;
 
             // If there are no valid "new" texture options specified but the path is split into several parts, lets parse the old way.
             if (isTexturePathDeclaredTheTraditionalWay(textureValues.size(), textureOptionValues.size(), texturePath)) {
+                boolean flipY = false;
+
                 if (texturePath.startsWith("Flip Repeat ") || texturePath.startsWith("Repeat Flip ")) {
                     texturePath = texturePath.substring(12).trim();
                     flipY = true;
-                    repeat = true;
                 } else if (texturePath.startsWith("Flip ")) {
                     texturePath = texturePath.substring(5).trim();
                     flipY = true;
                 } else if (texturePath.startsWith("Repeat ")) {
                     texturePath = texturePath.substring(7).trim();
-                    repeat = true;
                 }
 
                 // Support path starting with quotes (double and single)
@@ -216,7 +214,7 @@ public class J3MLoader implements AssetLoader {
             }
 
             if (textureKey == null) {
-                textureKey = new TextureKey(textureValues.get(textureValues.size() - 1));
+                textureKey = new TextureKey(textureValues.get(textureValues.size() - 1), false);
             }
 
             // Apply texture options to the texture key
@@ -256,11 +254,6 @@ public class J3MLoader implements AssetLoader {
             texture.setName(textureKey.getName());
         }
 
-        // This is here for backwards compatibility, we need to do this after the texture has been instantiated.
-        if (repeat) {
-            texture.setWrap(Texture.WrapMode.Repeat);
-        }
-
         // Apply texture options to the texture
         if (!textureOptionValues.isEmpty()) {
             for (final TextureOptionValue textureOptionValue : textureOptionValues) {

+ 117 - 0
jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java

@@ -0,0 +1,117 @@
+package com.jme3.material.plugins;
+
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.TextureKey;
+import com.jme3.material.MatParamTexture;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialDef;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Texture;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author Daniel Johansson
+ * @since 2015-07-20
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class J3MLoaderTest {
+
+    private J3MLoader j3MLoader;
+
+    @Mock
+    private AssetInfo assetInfo;
+
+    @Mock
+    private AssetManager assetManager;
+
+    @Mock
+    private AssetKey<Material> assetKey;
+
+    @Mock
+    private MaterialDef materialDef;
+
+    @Before
+    public void setUp() throws Exception {
+        when(assetKey.getExtension()).thenReturn(".j3m");
+        when(assetInfo.getManager()).thenReturn(assetManager);
+        when(assetInfo.getKey()).thenReturn(assetKey);
+        when(assetManager.loadAsset(any(AssetKey.class))).thenReturn(materialDef);
+
+        j3MLoader = new J3MLoader();
+    }
+
+    @Test
+    public void oldStyleTextureParameters_shouldBeSupported() throws Exception {
+        when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-oldstyle.j3m"));
+
+        final Texture textureOldStyle = Mockito.mock(Texture.class);
+        final Texture textureOldStyleUsingQuotes = Mockito.mock(Texture.class);
+
+        final TextureKey textureKeyUsingQuotes = setupMockForTexture("OldStyleUsingQuotes", "old style using quotes/texture.png", true, textureOldStyleUsingQuotes);
+        final TextureKey textureKeyOldStyle = setupMockForTexture("OldStyle", "old style/texture.png", true, textureOldStyle);
+
+        j3MLoader.load(assetInfo);
+
+        verify(assetManager).loadTexture(textureKeyUsingQuotes);
+        verify(assetManager).loadTexture(textureKeyOldStyle);
+        verify(textureOldStyle).setWrap(Texture.WrapMode.Repeat);
+        verify(textureOldStyleUsingQuotes).setWrap(Texture.WrapMode.Repeat);
+    }
+
+    @Test
+    public void newStyleTextureParameters_shouldBeSupported() throws Exception {
+        when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-newstyle.j3m"));
+
+        final Texture textureNoParameters = Mockito.mock(Texture.class);
+        final Texture textureFlip = Mockito.mock(Texture.class);
+        final Texture textureRepeat = Mockito.mock(Texture.class);
+        final Texture textureRepeatAxis = Mockito.mock(Texture.class);
+        final Texture textureMin = Mockito.mock(Texture.class);
+        final Texture textureMag = Mockito.mock(Texture.class);
+        final Texture textureCombined = Mockito.mock(Texture.class);
+
+        final TextureKey textureKeyNoParameters = setupMockForTexture("Empty", "empty.png", false, textureNoParameters);
+        final TextureKey textureKeyFlip = setupMockForTexture("Flip", "flip.png", true, textureFlip);
+        setupMockForTexture("Repeat", "repeat.png", false, textureRepeat);
+        setupMockForTexture("RepeatAxis", "repeat-axis.png", false, textureRepeatAxis);
+        setupMockForTexture("Min", "min.png", false, textureMin);
+        setupMockForTexture("Mag", "mag.png", false, textureMag);
+        setupMockForTexture("Combined", "combined.png", true, textureCombined);
+
+        j3MLoader.load(assetInfo);
+
+        verify(assetManager).loadTexture(textureKeyNoParameters);
+        verify(assetManager).loadTexture(textureKeyFlip);
+
+        verify(textureRepeat).setWrap(Texture.WrapMode.Repeat);
+        verify(textureRepeatAxis).setWrap(Texture.WrapAxis.T, Texture.WrapMode.Repeat);
+        verify(textureMin).setMinFilter(Texture.MinFilter.Trilinear);
+        verify(textureMag).setMagFilter(Texture.MagFilter.Bilinear);
+
+        verify(textureCombined).setMagFilter(Texture.MagFilter.Nearest);
+        verify(textureCombined).setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
+        verify(textureCombined).setWrap(Texture.WrapMode.Repeat);
+    }
+
+    private TextureKey setupMockForTexture(final String paramName, final String path, final boolean flipY, final Texture texture) {
+        when(materialDef.getMaterialParam(paramName)).thenReturn(new MatParamTexture(VarType.Texture2D, paramName, texture, 0, null));
+
+        final TextureKey textureKey = new TextureKey(path, flipY);
+        textureKey.setGenerateMips(true);
+
+        when(assetManager.loadTexture(textureKey)).thenReturn(texture);
+
+        return textureKey;
+    }
+}

+ 11 - 0
jme3-core/src/test/resources/texture-parameters-newstyle.j3m

@@ -0,0 +1,11 @@
+Material Test : matdef.j3md {
+     MaterialParameters {
+         Empty: "empty.png"
+         Flip: Flip "flip.png"
+         Repeat: WrapRepeat "repeat.png"
+         Min: MinTrilinear "min.png"
+         Mag: MagBilinear "mag.png"
+         RepeatAxis: WrapRepeat_T "repeat-axis.png"
+         Combined: MagNearest MinBilinearNoMipMaps Flip WrapRepeat "combined.png"
+     }
+}

+ 6 - 0
jme3-core/src/test/resources/texture-parameters-oldstyle.j3m

@@ -0,0 +1,6 @@
+Material Test : matdef.j3md {
+     MaterialParameters {
+         OldStyle: Flip Repeat old style/texture.png
+         OldStyleUsingQuotes: Repeat Flip "old style using quotes/texture.png"
+     }
+}

+ 1 - 1
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java

@@ -13,7 +13,7 @@ import java.nio.ShortBuffer;
 import com.jme3.renderer.opengl.GL4;
 import org.lwjgl.opengl.*;
 
-public class LwjglGL implements GL, GL2, GL3, GL4 {
+public final class LwjglGL implements GL, GL2, GL3, GL4 {
     
     private static void checkLimit(Buffer buffer) {
         if (buffer == null) {

+ 1 - 1
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java

@@ -13,7 +13,7 @@ import org.lwjgl.opengl.GL15;
 import org.lwjgl.opengl.GL20;
 import org.lwjgl.opengl.GLSync;
 
-public class LwjglGLExt implements GLExt {
+public final class LwjglGLExt implements GLExt {
 
     private static void checkLimit(Buffer buffer) {
         if (buffer == null) {

+ 1 - 1
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java

@@ -13,7 +13,7 @@ import org.lwjgl.opengl.EXTFramebufferObject;
  * 
  * @author Kirill Vainer
  */
-public class LwjglGLFboEXT implements GLFbo {
+public final class LwjglGLFboEXT implements GLFbo {
 
     private static void checkLimit(Buffer buffer) {
         if (buffer == null) {

+ 1 - 1
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java

@@ -11,7 +11,7 @@ import org.lwjgl.opengl.GL30;
  * 
  * @author Kirill Vainer
  */
-public class LwjglGLFboGL3 implements GLFbo {
+public final class LwjglGLFboGL3 implements GLFbo {
 
     private static void checkLimit(Buffer buffer) {
         if (buffer == null) {

+ 13 - 0
jme3-niftygui/build.gradle

@@ -14,3 +14,16 @@ dependencies {
     compile 'lessvoid:nifty-default-controls:1.4.1'
     compile 'lessvoid:nifty-style-black:1.4.1'
 }
+
+uploadArchives {
+    repositories.mavenDeployer {
+        pom.project {
+            repositories {
+                repository {
+                    id "nifty-maven-repo.sourceforge.net"
+                    url "http://nifty-gui.sourceforge.net/nifty-maven-repo"
+                }
+            }
+        }
+    }
+}

+ 3 - 0
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/SceneEditTool.java

@@ -453,6 +453,9 @@ public abstract class SceneEditTool {
         Geometry arrowX = new Geometry("arrowX", new Arrow(new Vector3f(arrowSize, 0, 0)));
         Geometry arrowY = new Geometry("arrowY", new Arrow(new Vector3f(0, arrowSize, 0)));
         Geometry arrowZ = new Geometry("arrowZ", new Arrow(new Vector3f(0, 0, arrowSize)));
+        arrowX.getMesh().setLineWidth(2f);
+        arrowY.getMesh().setLineWidth(2f);
+        arrowZ.getMesh().setLineWidth(2f);   
         axis.attachChild(arrowX);
         axis.attachChild(arrowY);
         axis.attachChild(arrowZ);

+ 16 - 3
sdk/jme3-scenecomposer/src/com/jme3/gde/scenecomposer/tools/SelectTool.java

@@ -10,6 +10,7 @@ import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
 import com.jme3.gde.core.sceneviewer.SceneViewerTopComponent;
 import com.jme3.gde.scenecomposer.SceneEditTool;
 import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import com.jme3.terrain.Terrain;
@@ -33,7 +34,7 @@ import org.openide.loaders.DataObject;
  */
 public class SelectTool extends SceneEditTool {
 
-    private boolean wasDraggingR = false;
+    private boolean wasDraggingR, wasDraggingL = false;
     private boolean wasDownR = false;
 
     /**
@@ -52,13 +53,24 @@ public class SelectTool extends SceneEditTool {
      */
     @Override
     public void actionPrimary(Vector2f screenCoord, boolean pressed, final JmeNode rootNode, DataObject dataObject) {
-
+        if (!pressed){
+            if (!wasDraggingL) {
+                Vector3f result = pickWorldLocation(getCamera(), screenCoord, rootNode);
+                if (result != null) {
+                    if (toolController.isSnapToGrid()) {
+                        result.set(Math.round(result.x), result.y, Math.round(result.z));
+                    }
+                    toolController.doSetCursorLocation(result);
+                }
+            }
+            wasDraggingL = false;
+        }        
     }
 
     @Override
     public void actionSecondary(final Vector2f screenCoord, boolean pressed, final JmeNode rootNode, DataObject dataObject) {
         if (pressed) {
-            Spatial selected = toolController.getSelectedSpatial();
+            Spatial selected;// = toolController.getSelectedSpatial();
             // mouse down
 
             if (!wasDraggingR && !wasDownR) { // wasn't dragging and was not down already
@@ -137,6 +149,7 @@ public class SelectTool extends SceneEditTool {
 
     @Override
     public void draggedPrimary(Vector2f screenCoord, boolean pressed, JmeNode rootNode, DataObject currentDataObject) {
+        wasDraggingL = pressed;
     }
 
     @Override

+ 1 - 1
sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/Bundle.properties

@@ -124,7 +124,7 @@ TerrainEditorTopComponent.AbsoluteCheckbox.tooltip=Define the height to adjust t
 TerrainEditorTopComponent.slopeLockCheckbox.tooltip=Contains the slope between the two slope nodes
 TerrainEditorTopComponent.borderDistanceLabel.tooltip=Distance means how far from the terrain's edge the border will be raised (thickness of the border).
 TerrainEditorTopComponent.borderHeightLAbel.tooltip=Height means how high the border will go (also accept negative values).
-TerrainEditorTopComponent.paintButton.toolTipText=Erase a texture from the terrain
+TerrainEditorTopComponent.paintButton.toolTipText=Paint a texture on the terrain. RMB to Erase.
 TerrainEditorTopComponent.paintButton.text=
 RenameTerrainVisualPanel1.ranemeLabel.text=Rename Terrain Alphamaps
 RenameTerrainVisualPanel1.renameField.text=

+ 2 - 0
sdk/nbproject/platform.properties

@@ -1,4 +1,6 @@
 branding.token=jmonkeyplatform
+keystore=../nbproject/private/keystore.jks
+nbm_alias=jmeupdates
 cluster.path=\
     ${nbplatform.active.dir}/extide:\
     ${nbplatform.active.dir}/harness:\

+ 1 - 1
settings.gradle

@@ -37,7 +37,7 @@ include 'jme3-testdata'
 include 'jme3-examples'
 
 if(buildAndroidExamples == "true"){
-	include 'jme3-android-examples'
+    include 'jme3-android-examples'
 }
 
 if(buildSdkProject == "true"){