Преглед изворни кода

Merge remote-tracking branch 'upstream/master' into fix_fb_depth24stencil8

David Bernard пре 11 година
родитељ
комит
4fdde38bc2
48 измењених фајлова са 2192 додато и 895 уклоњено
  1. BIN
      jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so
  2. BIN
      jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so
  3. BIN
      jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so
  4. BIN
      jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so
  5. BIN
      jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so
  6. BIN
      jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so
  7. BIN
      jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so
  8. BIN
      jme3-android-native/libs/stb_image/arm64-v8a/libstbijme.so
  9. BIN
      jme3-android-native/libs/stb_image/armeabi-v7a/libstbijme.so
  10. BIN
      jme3-android-native/libs/stb_image/armeabi/libstbijme.so
  11. BIN
      jme3-android-native/libs/stb_image/mips/libstbijme.so
  12. BIN
      jme3-android-native/libs/stb_image/mips64/libstbijme.so
  13. BIN
      jme3-android-native/libs/stb_image/x86/libstbijme.so
  14. BIN
      jme3-android-native/libs/stb_image/x86_64/libstbijme.so
  15. 10 13
      jme3-android-native/openalsoft.gradle
  16. 59 49
      jme3-android-native/src/native/jme_openalsoft/Android.mk
  17. 2 2
      jme3-android-native/src/native/jme_openalsoft/Application.mk
  18. 131 75
      jme3-android-native/src/native/jme_openalsoft/config.h
  19. 9 7
      jme3-android-native/src/native/jme_stbi/Android.mk
  20. 2 2
      jme3-android-native/src/native/jme_stbi/Application.mk
  21. 3 2
      jme3-android-native/src/native/jme_stbi/com_jme3_texture_plugins_AndroidNativeImageLoader.c
  22. 12 11
      jme3-android-native/stb_image.gradle
  23. 2 2
      jme3-android/src/main/java/com/jme3/app/AndroidHarness.java
  24. 3 3
      jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java
  25. 0 26
      jme3-android/src/main/java/com/jme3/renderer/android/AndroidGLSurfaceView.java
  26. 0 96
      jme3-android/src/main/java/com/jme3/system/android/AndroidTimer.java
  27. 15 38
      jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
  28. 96 55
      jme3-core/src/main/java/com/jme3/material/Material.java
  29. 9 2
      jme3-core/src/main/java/com/jme3/material/Technique.java
  30. 16 1
      jme3-core/src/main/java/com/jme3/renderer/Caps.java
  31. 17 0
      jme3-core/src/main/java/com/jme3/renderer/RenderContext.java
  32. 30 0
      jme3-core/src/main/java/com/jme3/renderer/RenderManager.java
  33. 31 109
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag
  34. 45 36
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md
  35. 26 87
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert
  36. 218 0
      jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag
  37. 172 0
      jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert
  38. 22 40
      jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib
  39. 29 0
      jme3-core/src/main/resources/Common/ShaderLib/PhongLighting.glsllib
  40. 255 0
      jme3-examples/src/main/java/jme3test/light/TestManyLightsSingle.java
  41. 2 2
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java
  42. 194 142
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java
  43. 14 13
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/TextureUtil.java
  44. 615 0
      jme3-terrain/src/main/resources/Common/MatDefs/Terrain/SPTerrainLighting.frag
  45. 66 0
      jme3-terrain/src/main/resources/Common/MatDefs/Terrain/SPTerrainLighting.vert
  46. 5 51
      jme3-terrain/src/main/resources/Common/MatDefs/Terrain/TerrainLighting.frag
  47. 64 0
      jme3-terrain/src/main/resources/Common/MatDefs/Terrain/TerrainLighting.j3md
  48. 18 31
      jme3-terrain/src/main/resources/Common/MatDefs/Terrain/TerrainLighting.vert

BIN
jme3-android-native/libs/openalsoft/arm64-v8a/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/armeabi-v7a/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/armeabi/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/mips/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/mips64/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/x86/libopenalsoftjme.so


BIN
jme3-android-native/libs/openalsoft/x86_64/libopenalsoftjme.so


BIN
jme3-android-native/libs/stb_image/arm64-v8a/libstbijme.so


BIN
jme3-android-native/libs/stb_image/armeabi-v7a/libstbijme.so


BIN
jme3-android-native/libs/stb_image/armeabi/libstbijme.so


BIN
jme3-android-native/libs/stb_image/mips/libstbijme.so


BIN
jme3-android-native/libs/stb_image/mips64/libstbijme.so


BIN
jme3-android-native/libs/stb_image/x86/libstbijme.so


BIN
jme3-android-native/libs/stb_image/x86_64/libstbijme.so


+ 10 - 13
jme3-android-native/openalsoft.gradle

@@ -1,9 +1,5 @@
-// OpenAL Soft r1.15.1
-//String openALSoftUrl = 'http://repo.or.cz/w/openal-soft.git/snapshot/9b6a226da55a987cb883f425eeb568776ea12c8d.zip'
-// OpenAL Soft r1.15.1 + Android OpenSL Support
-String openALSoftUrl = 'http://repo.or.cz/w/openal-soft.git/snapshot/be25e6802dacad78876c6fa1d6a5c63797b8a9ed.zip'
-// OpenAL Soft r1.15.1 latest build (at the time)
-//String openALSoftUrl = 'http://repo.or.cz/w/openal-soft.git/snapshot/3f5914e0949ee12b504ee7254990e007ff8057ef.zip'
+// OpenAL Soft r1.16
+String openALSoftUrl = 'http://repo.or.cz/w/openal-soft.git/snapshot/e5016f814a265ed592a88acea95cf912c4bfdf12.zip'
 String openALSoftZipFile = 'OpenALSoft.zip'
 
 // OpenAL Soft directory the download is extracted into
@@ -81,22 +77,23 @@ task generateOpenAlSoftHeaders(dependsOn: copyJmeOpenALSoft) << {
     String classes = ""
             .concat("com.jme3.audio.android.AndroidOpenALSoftAudioRenderer, ")
 //    println "openalsoft classes = " + classes
-//    println "openalsoft destDir = " + destDir
+//    println "openalsoft destDir = " + destDirPath
 //    println "openalsoft classpath = " + project.projectClassPath
 
-    ant.javah(
-        classpath: project.projectClassPath,
-        destdir: destDirPath,
-        class: classes
-    )
+    exec {
+        executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
+        args '-d', destDirPath
+        args '-classpath', project.projectClassPath
+        args "com.jme3.audio.android.AndroidOpenALSoftAudioRenderer"
+    }
 }
 
 task buildOpenAlSoftNativeLib(type: Exec, dependsOn: generateOpenAlSoftHeaders) {
 //    println "openalsoft build dir: " + openalsoftBuildDir
 //    println "ndkCommandPath: " + project.ndkCommandPath
-    args 'TARGET_PLATFORM=android-9'
     workingDir openalsoftBuildDir
     executable rootProject.ndkCommandPath
+    args '-j8'
 }
 
 task updatePreCompiledOpenAlSoftLibs(type: Copy, dependsOn: buildOpenAlSoftNativeLib) {

+ 59 - 49
jme3-android-native/src/native/jme_openalsoft/Android.mk

@@ -1,58 +1,68 @@
 TARGET_PLATFORM := android-9
 
-ROOT_PATH := $(call my-dir)
-
-########################################################################################################
+LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
 LOCAL_MODULE     := openalsoftjme
-LOCAL_ARM_MODE   := arm
-LOCAL_PATH       := $(ROOT_PATH)
-LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/include $(LOCAL_PATH)/OpenAL32/Include
-
-LOCAL_CFLAGS     := -ffast-math -DAL_BUILD_LIBRARY -DAL_ALEXT_PROTOTYPES
-LOCAL_LDLIBS     := -llog -Wl,-s
-LOCAL_LDLIBS    += -lOpenSLES
-#  LOCAL_CFLAGS +=   -DPOST_FROYO #-I$(ANDROID_NDK_ROOT)/platforms/android-9/arch-arm/usr/include/
-#  LOCAL_LDLIBS += -ldl -L$(ANDROID_NDK_ROOT)/platforms/android-9/arch-arm/usr/lib/
-
-LOCAL_SRC_FILES  := OpenAL32/alAuxEffectSlot.c \
-                    OpenAL32/alBuffer.c        \
-                    OpenAL32/alEffect.c        \
-                    OpenAL32/alError.c         \
-                    OpenAL32/alExtension.c     \
-                    OpenAL32/alFilter.c        \
-                    OpenAL32/alListener.c      \
-                    OpenAL32/alSource.c        \
-                    OpenAL32/alState.c         \
-                    OpenAL32/alThunk.c         \
-                    Alc/ALc.c                  \
-                    Alc/ALu.c                  \
-                    Alc/alcConfig.c            \
-                    Alc/alcDedicated.c         \
-                    Alc/alcEcho.c              \
-                    Alc/alcModulator.c         \
-                    Alc/alcReverb.c            \
-                    Alc/alcRing.c              \
-                    Alc/alcThread.c            \
-                    Alc/bs2b.c                 \
-                    Alc/helpers.c              \
-                    Alc/panning.c              \
-                    Alc/hrtf.c                 \
-                    Alc/mixer.c                \
-                    Alc/mixer_c.c                \
-                    Alc/backends/loopback.c    \
-                    Alc/backends/null.c        \
-                    Alc/backends/opensl.c      \
-                    com_jme3_audio_android_AndroidOpenALSoftAudioRenderer.cpp
-#                    Alc/backends/alsa.c        \
-#                    Alc/backends/android.c     \
-#                    Alc/alcChorus.c            \
-#                    Alc/alcFlanger.c           \
-#                    Alc/mixer_c.c              \
-#                    Alc/backends/loopback.c    \
-#                    Alc/backends/null.c        \
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/include \
+		    $(LOCAL_PATH)/OpenAL32/Include $(LOCAL_PATH)/Alc
+
+LOCAL_CFLAGS     := -std=c99 -ffast-math -DAL_BUILD_LIBRARY -DAL_ALEXT_PROTOTYPES
+LOCAL_LDLIBS     := -lOpenSLES -llog -Wl,-s
+
+LOCAL_SRC_FILES  :=   Alc/backends/opensl.c \
+                      Alc/backends/loopback.c \
+                      Alc/backends/wave.c \
+                      Alc/backends/base.c \
+                      Alc/backends/null.c \
+                      Alc/ALc.c \
+                      Alc/helpers.c \
+                      Alc/bs2b.c \
+                      Alc/alcRing.c \
+                      Alc/effects/chorus.c \
+                      Alc/effects/flanger.c \
+                      Alc/effects/dedicated.c \
+                      Alc/effects/reverb.c \
+                      Alc/effects/distortion.c \
+                      Alc/effects/autowah.c \
+                      Alc/effects/equalizer.c \
+                      Alc/effects/modulator.c \
+                      Alc/effects/echo.c \
+                      Alc/effects/compressor.c \
+                      Alc/effects/null.c \
+                      Alc/alcConfig.c \
+                      Alc/ALu.c \
+                      Alc/mixer_c.c \
+                      Alc/panning.c \
+                      Alc/hrtf.c \
+                      Alc/mixer.c \
+                      Alc/midi/soft.c \
+                      Alc/midi/sf2load.c \
+                      Alc/midi/dummy.c \
+                      Alc/midi/fluidsynth.c \
+                      Alc/midi/base.c \
+                      common/uintmap.c \
+                      common/atomic.c \
+                      common/threads.c \
+                      common/rwlock.c \
+                      OpenAL32/alBuffer.c \
+                      OpenAL32/alPreset.c \
+                      OpenAL32/alListener.c \
+                      OpenAL32/alEffect.c \
+                      OpenAL32/alExtension.c \
+                      OpenAL32/alThunk.c \
+                      OpenAL32/alMidi.c \
+                      OpenAL32/alSoundfont.c \
+                      OpenAL32/alFontsound.c \
+                      OpenAL32/alAuxEffectSlot.c \
+                      OpenAL32/alError.c \
+                      OpenAL32/alFilter.c \
+                      OpenAL32/alSource.c \
+                      OpenAL32/alState.c \
+                      OpenAL32/sample_cvt.c \
+		      com_jme3_audio_android_AndroidOpenALSoftAudioRenderer.cpp
 
 include $(BUILD_SHARED_LIBRARY)
 

+ 2 - 2
jme3-android-native/src/native/jme_openalsoft/Application.mk

@@ -1,3 +1,3 @@
+APP_PLATFORM := android-9
 APP_OPTIM := release
-APP_ABI := all
-#APP_ABI := armeabi-v7a
+APP_ABI := all

+ 131 - 75
jme3-android-native/src/native/jme_openalsoft/config.h

@@ -1,147 +1,203 @@
-#ifndef CONFIG_H
-#define CONFIG_H
+/* API declaration export attribute */
+#define AL_API  __attribute__((visibility("protected")))
+#define ALC_API __attribute__((visibility("protected")))
 
 /* Define to the library version */
-#define ALSOFT_VERSION "1.15.1"
+#define ALSOFT_VERSION "1.16.0"
 
-#define ALIGN(x) __attribute__  ((aligned(x)))
+#ifdef IN_IDE_PARSER
+/* KDevelop's parser doesn't recognize the C99-standard restrict keyword, but
+ * recent versions (at least 4.5.1) do recognize GCC's __restrict. */
+#define restrict __restrict
+#endif 
 
-/* Define if we have the Android backend */
-/* #define HAVE_ANDROID 1 */
+/* Define any available alignment declaration */
+#define ALIGN(x) __attribute__((aligned(x)))
+
+/* Define if we have the C11 aligned_alloc function */
+/* #undef HAVE_ALIGNED_ALLOC */
+
+/* Define if we have the posix_memalign function */
+/* #undef HAVE_POSIX_MEMALIGN */
+
+/* Define if we have the _aligned_malloc function */
+/* #undef HAVE__ALIGNED_MALLOC */
+
+/* Define if we have SSE CPU extensions */
+/* #undef HAVE_SSE */
+/* #undef HAVE_SSE2 */
+/* #undef HAVE_SSE4_1 */
+
+/* Define if we have ARM Neon CPU extensions */
+/* #undef HAVE_NEON */
+
+/* Define if we have FluidSynth support */
+/* #undef HAVE_FLUIDSYNTH */
 
 /* Define if we have the ALSA backend */
-/* #define HAVE_ALSA */
+/* #undef HAVE_ALSA */
 
 /* Define if we have the OSS backend */
-/* #cmakedefine HAVE_OSS */
+/* #undef HAVE_OSS */
 
 /* Define if we have the Solaris backend */
-/* #cmakedefine HAVE_SOLARIS */
+/* #undef HAVE_SOLARIS */
 
 /* Define if we have the SndIO backend */
-/* #cmakedefine HAVE_SNDIO */
+/* #undef HAVE_SNDIO */
+
+/* Define if we have the QSA backend */
+/* #undef HAVE_QSA */
 
 /* Define if we have the MMDevApi backend */
-/* #cmakedefine HAVE_MMDEVAPI */
+/* #undef HAVE_MMDEVAPI */
 
 /* Define if we have the DSound backend */
-/* #cmakedefine HAVE_DSOUND */
+/* #undef HAVE_DSOUND */
 
 /* Define if we have the Windows Multimedia backend */
-/* #cmakedefine HAVE_WINMM */
+/* #undef HAVE_WINMM */
 
 /* Define if we have the PortAudio backend */
-/* #cmakedefine HAVE_PORTAUDIO */
+/* #undef HAVE_PORTAUDIO */
 
 /* Define if we have the PulseAudio backend */
-/* #cmakedefine HAVE_PULSEAUDIO */
+/* #undef HAVE_PULSEAUDIO */
 
 /* Define if we have the CoreAudio backend */
-/* #cmakedefine HAVE_COREAUDIO */
+/* #undef HAVE_COREAUDIO */
 
 /* Define if we have the OpenSL backend */
-#define HAVE_OPENSL /* THIS BACKEND WORKS ON >=2.3 Android!! */
+#define HAVE_OPENSL
 
 /* Define if we have the Wave Writer backend */
-/* #cmakedefine HAVE_WAVE */
-
-/* Define if we have dlfcn.h */
-#define HAVE_DLFCN_H
+#define HAVE_WAVE
 
 /* Define if we have the stat function */
 #define HAVE_STAT
 
-/* Define if we have the powf function */
-/* #define HAVE_POWF 1 */
+/* Define if we have the lrintf function */
+#define HAVE_LRINTF
 
-/* Define if we have the sqrtf function */
-/* #define HAVE_SQRTF 1 */
+/* Define if we have the strtof function */
+/* #undef HAVE_STRTOF */
 
-/* Define if we have the cosf function */
-/* #define HAVE_COSF 1 */
+/* Define if we have the __int64 type */
+/* #undef HAVE___INT64 */
 
-/* Define if we have the sinf function */
-/* #define HAVE_SINF 1 */
+/* Define to the size of a long int type */
+#define SIZEOF_LONG 4
 
-/* Define if we have the acosf function */
-/* #define HAVE_ACOSF 1 */
+/* Define to the size of a long long int type */
+#define SIZEOF_LONG_LONG 8
 
-/* Define if we have the asinf function */
-/* #define  HAVE_ASINF 1 */
+/* Define if we have C99 variable-length array support */
+#define HAVE_C99_VLA
 
-/* Define if we have the atanf function */
-/* #define HAVE_ATANF 1 */
+/* Define if we have C99 _Bool support */
+#define HAVE_C99_BOOL
 
-/* Define if we have the atan2f function */
-/* #define HAVE_ATAN2F 1 */
+/* Define if we have C11 _Static_assert support */
+#define HAVE_C11_STATIC_ASSERT
 
-/* Define if we have the fabsf function */
-/* #define HAVE_FABSF 1 */
+/* Define if we have C11 _Alignas support */
+/* #undef HAVE_C11_ALIGNAS */
 
-/* Define if we have the log10f function */
-/* #define HAVE_LOG10F 1 */
+/* Define if we have C11 _Atomic support */
+/* #undef HAVE_C11_ATOMIC */
 
-/* Define if we have the floorf function */
-/* #define HAVE_FLOORF 1 */
+/* Define if we have GCC's destructor attribute */
+#define HAVE_GCC_DESTRUCTOR
 
-/* Define if we have the strtof function */
-#define HAVE_STRTOF
+/* Define if we have GCC's format attribute */
+#define HAVE_GCC_FORMAT
 
 /* Define if we have stdint.h */
 #define HAVE_STDINT_H
 
-/* Define if we have the __int64 type */
-/* #cmakedefine HAVE___INT64 */
+/* Define if we have stdbool.h */
+#define HAVE_STDBOOL_H
 
-/* Define to the size of a long int type */
-#define SIZEOF_LONG 4
+/* Define if we have stdalign.h */
+/* #undef HAVE_STDALIGN_H */
 
-/* Define to the size of a long long int type */
-#define SIZEOF_LONG_LONG 8
-
-/* Define if we have GCC's destructor attribute */
-#define HAVE_GCC_DESTRUCTOR
+/* Define if we have windows.h */
+/* #undef HAVE_WINDOWS_H */
 
-/* Define if we have GCC's format attribute */
-#define HAVE_GCC_FORMAT
+/* Define if we have dlfcn.h */
+#define HAVE_DLFCN_H
 
 /* Define if we have pthread_np.h */
-/* #cmakedefine HAVE_PTHREAD_NP_H */
+/* #undef HAVE_PTHREAD_NP_H */
 
-/* Define if we have arm_neon.h */
-/* #cmakedefine HAVE_ARM_NEON_H */
+/* Define if we have alloca.h */
+/* #undef HAVE_ALLOCA_H */
 
-/* Define if we have guiddef.h */
-/* #cmakedefine HAVE_GUIDDEF_H */
+/* Define if we have malloc.h */
+#define HAVE_MALLOC_H
+
+/* Define if we have ftw.h */
+/* #undef HAVE_FTW_H */
+
+/* Define if we have io.h */
+/* #undef HAVE_IO_H */
+
+/* Define if we have strings.h */
+#define HAVE_STRINGS_H
+
+/* Define if we have cpuid.h */
+/* #undef HAVE_CPUID_H */
+
+/* Define if we have intrin.h */
+/* #undef HAVE_INTRIN_H */
+
+/* Define if we have sys/sysconf.h */
+#define HAVE_SYS_SYSCONF_H
 
 /* Define if we have guiddef.h */
-/* #cmakedefine HAVE_INITGUID_H */
+/* #undef HAVE_GUIDDEF_H */
+
+/* Define if we have initguid.h */
+/* #undef HAVE_INITGUID_H */
 
 /* Define if we have ieeefp.h */
-/* #cmakedefine HAVE_IEEEFP_H */
+/* #undef HAVE_IEEEFP_H */
 
 /* Define if we have float.h */
-/* #cmakedefine HAVE_FLOAT_H */
-
-/* Define if we have fpu_control.h */
-/* #cmakedefine HAVE_FPU_CONTROL_H */
+#define HAVE_FLOAT_H
 
 /* Define if we have fenv.h */
 #define HAVE_FENV_H
 
-/* Define if we have fesetround() */
-/* #cmakedefine HAVE_FESETROUND */
+/* Define if we have GCC's __get_cpuid() */
+/* #undef HAVE_GCC_GET_CPUID */
+
+/* Define if we have the __cpuid() intrinsic */
+/* #undef HAVE_CPUID_INTRINSIC */
 
 /* Define if we have _controlfp() */
-/* #cmakedefine HAVE__CONTROLFP */
+/* #undef HAVE__CONTROLFP */
+
+/* Define if we have __control87_2() */
+/* #undef HAVE___CONTROL87_2 */
+
+/* Define if we have ftw() */
+/* #undef HAVE_FTW */
+
+/* Define if we have _wfindfirst() */
+/* #undef HAVE__WFINDFIRST */
 
 /* Define if we have pthread_setschedparam() */
 #define HAVE_PTHREAD_SETSCHEDPARAM
 
-/* Define if we have the restrict keyword */
-/* #cmakedefine  HAVE_RESTRICT 1 */
+/* Define if we have pthread_setname_np() */
+#define HAVE_PTHREAD_SETNAME_NP
+
+/* Define if we have pthread_set_name_np() */
+/* #undef HAVE_PTHREAD_SET_NAME_NP */
 
-/* Define if we have the __restrict keyword */
-#define RESTRICT __restrict
+/* Define if we have pthread_mutexattr_setkind_np() */
+/* #undef HAVE_PTHREAD_MUTEXATTR_SETKIND_NP */
 
-#endif
+/* Define if we have pthread_mutex_timedlock() */
+/* #undef HAVE_PTHREAD_MUTEX_TIMEDLOCK */

+ 9 - 7
jme3-android-native/src/native/jme_stbi/Android.mk

@@ -1,13 +1,15 @@
+TARGET_PLATFORM := android-9
+
 LOCAL_PATH := $(call my-dir)
+	
 include $(CLEAR_VARS)
 
 LOCAL_MODULE    := stbijme
-LOCAL_C_INCLUDES  := $(LOCAL_PATH)
-LOCAL_CFLAGS += -O2
-LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
-LOCAL_SRC_FILES := $(subst $(LOCAL_PATH)/,, $(wildcard $(LOCAL_PATH)/*.c))
-
-#adds zlib
-LOCAL_LDLIBS    += -lz -llog
+	
+LOCAL_C_INCLUDES  += $(LOCAL_PATH)
+	
+LOCAL_LDLIBS := -lz -llog -Wl,-s
+	
+LOCAL_SRC_FILES := com_jme3_texture_plugins_AndroidNativeImageLoader.c
 
 include $(BUILD_SHARED_LIBRARY)

+ 2 - 2
jme3-android-native/src/native/jme_stbi/Application.mk

@@ -1,3 +1,3 @@
+APP_PLATFORM := android-9
 APP_OPTIM := release
-APP_ABI := all
-#APP_ABI := armeabi-v7a
+APP_ABI := all

+ 3 - 2
jme3-android-native/src/native/jme_stbi/com_jme3_texture_plugins_AndroidNativeImageLoader.c

@@ -6,8 +6,9 @@
 #include <assert.h>
 #include <string.h>
 #include <time.h>
-#define STBI_HEADER_FILE_ONLY
-#include "stb_image.c"
+
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
 
 typedef unsigned int    uint32;
 

+ 12 - 11
jme3-android-native/stb_image.gradle

@@ -1,6 +1,6 @@
 // stb_image url for download
-String stbiUrl = 'http://www.nothings.org/stb_image.c'
-String stbiDownloadTarget = 'stb_image.c'
+String stbiUrl = 'https://raw.githubusercontent.com/nothings/stb/master/stb_image.h'
+String stbiDownloadTarget = 'stb_image.h'
 
 // stb_image is not downloaded.  The single source file is included in the repo
 String stbiFolder = 'stb_image'
@@ -63,20 +63,21 @@ task generateStbiHeaders(dependsOn: copyStbiJmeFiles) << {
 //    println "stb_image destDir = " + destDir
 //    println "stb_image classpath = " + project.projectClassPath
 
-    ant.javah(
-        classpath: project.projectClassPath,
-        destdir: destDirPath,
-        class: classes
-    )
-
+    exec {
+        executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
+        args '-d', destDirPath
+        args '-classpath', project.projectClassPath
+        args "com.jme3.texture.plugins.AndroidNativeImageLoader"
+    }
 }
 
 task buildStbiNativeLib(type: Exec, dependsOn: generateStbiHeaders) {
-//    println "stb_image build dir: " + buildLibDir
-//    println "ndkCommandPath: " + project.ndkCommandPath
-    args 'TARGET_PLATFORM=android-9'
+//    println "stb_image build dir: " + stbiBuildDir
+//    println "ndkCommandPath: " + rootProject.ndkCommandPath
+    
     workingDir stbiBuildDir
     executable rootProject.ndkCommandPath
+    args '-j8'
 }
 
 task updatePreCompiledStbiLibs(type: Copy, dependsOn: buildStbiNativeLib) {

+ 2 - 2
jme3-android/src/main/java/com/jme3/app/AndroidHarness.java

@@ -6,6 +6,7 @@ import android.content.DialogInterface;
 import android.content.pm.ActivityInfo;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.NinePatchDrawable;
+import android.opengl.GLSurfaceView;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.*;
@@ -21,7 +22,6 @@ import com.jme3.input.android.AndroidSensorJoyInput;
 import com.jme3.input.controls.TouchListener;
 import com.jme3.input.controls.TouchTrigger;
 import com.jme3.input.event.TouchEvent;
-import com.jme3.renderer.android.AndroidGLSurfaceView;
 import com.jme3.system.AppSettings;
 import com.jme3.system.SystemListener;
 import com.jme3.system.android.AndroidConfigChooser.ConfigType;
@@ -195,7 +195,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
      */
     protected int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
     protected OGLESContext ctx;
-    protected AndroidGLSurfaceView view = null;
+    protected GLSurfaceView view = null;
     protected boolean isGLThreadPaused = true;
     protected ImageView splashImageView = null;
     protected FrameLayout frameLayout = null;

+ 3 - 3
jme3-android/src/main/java/com/jme3/input/android/AndroidInputHandler.java

@@ -32,6 +32,7 @@
 
 package com.jme3.input.android;
 
+import android.opengl.GLSurfaceView;
 import android.os.Build;
 import android.view.View;
 import com.jme3.input.RawInputListener;
@@ -41,7 +42,6 @@ import com.jme3.input.event.KeyInputEvent;
 import com.jme3.input.event.MouseButtonEvent;
 import com.jme3.input.event.MouseMotionEvent;
 import com.jme3.input.event.TouchEvent;
-import com.jme3.renderer.android.AndroidGLSurfaceView;
 import com.jme3.system.AppSettings;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.logging.Level;
@@ -67,7 +67,7 @@ public class AndroidInputHandler implements TouchInput {
     
     
     // Internal
-    private AndroidGLSurfaceView view;
+    private GLSurfaceView view;
     private AndroidTouchHandler touchHandler;
     private AndroidKeyHandler keyHandler;
     private AndroidGestureHandler gestureHandler;
@@ -112,7 +112,7 @@ public class AndroidInputHandler implements TouchInput {
         if (gestureHandler != null) {
             gestureHandler.setView(view);
         }
-        this.view = (AndroidGLSurfaceView)view;
+        this.view = (GLSurfaceView)view;
     }
     
     public View getView() {

+ 0 - 26
jme3-android/src/main/java/com/jme3/renderer/android/AndroidGLSurfaceView.java

@@ -1,26 +0,0 @@
-package com.jme3.renderer.android;
- 
-import android.content.Context;
-import android.opengl.GLSurfaceView;
-import android.util.AttributeSet;
-import java.util.logging.Logger;
- 
-/**
- * <code>AndroidGLSurfaceView</code> is derived from GLSurfaceView
- * @author iwgeric
- *
- */
-public class AndroidGLSurfaceView extends GLSurfaceView {
- 
-    private final static Logger logger = Logger.getLogger(AndroidGLSurfaceView.class.getName());
- 
-    public AndroidGLSurfaceView(Context ctx, AttributeSet attribs) {
-        super(ctx, attribs);
-    }
- 
-    public AndroidGLSurfaceView(Context ctx) {
-        super(ctx);
-    }
- 
- 
-}

+ 0 - 96
jme3-android/src/main/java/com/jme3/system/android/AndroidTimer.java

@@ -1,96 +0,0 @@
-/*
- * 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.system.android;
-
-import com.jme3.system.Timer;
-
-/**
- * <code>AndroidTimer</code> is a System.nanoTime implementation of <code>Timer</code>.
- */
-public class AndroidTimer extends Timer {
-    
-    //private static final long TIMER_RESOLUTION = 1000L;
-    //private static final float INVERSE_TIMER_RESOLUTION = 1f/1000L;
-    private static final long TIMER_RESOLUTION = 1000000000L;
-    private static final float INVERSE_TIMER_RESOLUTION = 1f/1000000000L;
-    
-    private long startTime;
-    private long previousTime;
-    private float tpf;
-    private float fps;
-    
-    public AndroidTimer() {
-        //startTime = System.currentTimeMillis();
-        startTime = System.nanoTime();
-    }
-
-    /**
-     * Returns the time in seconds. The timer starts
-     * at 0.0 seconds.
-     *
-     * @return the current time in seconds
-     */
-    @Override
-    public float getTimeInSeconds() {
-        return getTime() * INVERSE_TIMER_RESOLUTION;
-    }
-
-    public long getTime() {
-        //return System.currentTimeMillis() - startTime;
-        return System.nanoTime() - startTime;
-    }
-
-    public long getResolution() {
-        return TIMER_RESOLUTION;
-    }
-
-    public float getFrameRate() {
-        return fps;
-    }
-
-    public float getTimePerFrame() {
-        return tpf;
-    }
-
-    public void update() {
-        tpf = (getTime() - previousTime) * (1.0f / TIMER_RESOLUTION);
-        fps = 1.0f / tpf;
-        previousTime = getTime();
-    }
-    
-    public void reset() {
-        //startTime = System.currentTimeMillis();
-        startTime = System.nanoTime();
-        previousTime = getTime();
-    }
-}

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

@@ -47,13 +47,11 @@ import android.view.ViewGroup.LayoutParams;
 import android.widget.EditText;
 import android.widget.FrameLayout;
 import com.jme3.input.*;
-import com.jme3.input.android.AndroidInput;
 import com.jme3.input.android.AndroidSensorJoyInput;
 import com.jme3.input.android.AndroidInputHandler;
 import com.jme3.input.controls.SoftTextDialogInputListener;
 import com.jme3.input.dummy.DummyKeyInput;
 import com.jme3.input.dummy.DummyMouseInput;
-import com.jme3.renderer.android.AndroidGLSurfaceView;
 import com.jme3.renderer.android.OGLESShaderRenderer;
 import com.jme3.system.*;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -80,11 +78,6 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
     protected AndroidInputHandler androidInput;
     protected int minFrameDuration = 0;                   // No FPS cap
     protected JoyInput androidSensorJoyInput = null;
-    /**
-     * EGL_RENDERABLE_TYPE: EGL_OPENGL_ES_BIT = OpenGL ES 1.0 |
-     * EGL_OPENGL_ES2_BIT = OpenGL ES 2.0
-     */
-    protected int clientOpenGLESVersion = 1;
 
     public OGLESContext() {
     }
@@ -103,12 +96,17 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
      *
      * @return GLSurfaceView The newly created view
      */
-    public AndroidGLSurfaceView createView() {
-        AndroidGLSurfaceView view;
-        int buildVersion = Build.VERSION.SDK_INT;
-
+    public GLSurfaceView createView() {
+        Context appContext = JmeAndroidSystem.getActivity().getApplication();
+        
+        ActivityManager am = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
+        ConfigurationInfo info = am.getDeviceConfigurationInfo();
+        if (info.reqGlEsVersion < 0x20000) {
+            throw new UnsupportedOperationException("OpenGL ES 2.0 is not supported on this device");
+        }
+        
         // Start to set up the view
-        view = new AndroidGLSurfaceView(JmeAndroidSystem.getActivity().getApplication());
+        GLSurfaceView view = new GLSurfaceView(appContext);
         if (androidInput == null) {
             androidInput = new AndroidInputHandler();
         }
@@ -117,20 +115,11 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
 
         // setEGLContextClientVersion must be set before calling setRenderer
         // this means it cannot be set in AndroidConfigChooser (too late)
-        int rawOpenGLESVersion = getOpenGLESVersion();
-//        logger.log(Level.FINE, "clientOpenGLESVersion {0}.{1}",
-//                new Object[]{clientOpenGLESVersion>>16, clientOpenGLESVersion<<16});
-        if (rawOpenGLESVersion < 0x20000) {
-            throw new UnsupportedOperationException("OpenGL ES 2.0 is not supported on this device");
-        } else {
-            clientOpenGLESVersion = 2;
-            view.setEGLContextClientVersion(clientOpenGLESVersion);
-        }
+        view.setEGLContextClientVersion(2);
 
         view.setFocusableInTouchMode(true);
         view.setFocusable(true);
-        view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU);
-
+        
         // setFormat must be set before AndroidConfigChooser is called by the surfaceview.
         // if setFormat is called after ConfigChooser is called, then execution
         // stops at the setFormat call without a crash.
@@ -160,25 +149,13 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
         // Not destroying and recreating the EGL context 
         // will help with resume time by reusing the existing context to avoid
         // reloading all the OpenGL objects.
-        if (buildVersion >= 11) {
+        if (Build.VERSION.SDK_INT >= 11) {
             view.setPreserveEGLContextOnPause(true);
         }
 
         return view;
     }
-    /**
-     * Get the  OpenGL ES version
-     * @return version returns the int value of the GLES version
-     */
-    public int getOpenGLESVersion() {
-        ActivityManager am =
-                (ActivityManager) JmeAndroidSystem.getActivity().getApplication().getSystemService(Context.ACTIVITY_SERVICE);
-        ConfigurationInfo info = am.getDeviceConfigurationInfo();
-        logger.log(Level.FINE, "OpenGL Version {0}:", info.getGlEsVersion());
-        return info.reqGlEsVersion;
-//        return (info.reqGlEsVersion >= 0x20000);
-    }
-
+    
     // renderer:initialize
     @Override
     public void onSurfaceCreated(GL10 gl, EGLConfig cfg) {
@@ -207,7 +184,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
             }
         });
 
-        timer = new AndroidTimer();
+        timer = new NanoTimer();
         renderer = new OGLESShaderRenderer();
 
         renderer.initialize();

+ 96 - 55
jme3-core/src/main/java/com/jme3/material/Material.java

@@ -42,7 +42,6 @@ import com.jme3.material.TechniqueDef.LightMode;
 import com.jme3.material.TechniqueDef.ShadowMode;
 import com.jme3.math.*;
 import com.jme3.renderer.Caps;
-import com.jme3.renderer.GL1Renderer;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.RendererException;
@@ -52,7 +51,6 @@ import com.jme3.scene.Mesh;
 import com.jme3.scene.instancing.InstancedGeometry;
 import com.jme3.shader.Shader;
 import com.jme3.shader.Uniform;
-import com.jme3.shader.UniformBindingManager;
 import com.jme3.shader.VarType;
 import com.jme3.texture.Texture;
 import com.jme3.texture.image.ColorSpace;
@@ -697,12 +695,15 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         setParam(name, VarType.Vector4, value);
     }
 
-    private ColorRGBA getAmbientColor(LightList lightList) {
+    private ColorRGBA getAmbientColor(LightList lightList, boolean removeLights) {
         ambientLightColor.set(0, 0, 0, 1);
         for (int j = 0; j < lightList.size(); j++) {
             Light l = lightList.get(j);
             if (l instanceof AmbientLight) {
                 ambientLightColor.addLocal(l.getColor());
+                if(removeLights){
+                    lightList.remove(l);
+                }
             }
         }
         ambientLightColor.a = 1.0f;
@@ -741,75 +742,106 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
      * g_LightPosition.w is the inverse radius (1/r) of the light (for
      * attenuation) <br/> </p>
      */
-    protected void updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights) {
+    protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex) {
         if (numLights == 0) { // this shader does not do lighting, ignore.
-            return;
+            return 0;
         }
 
-        Uniform lightColor = shader.getUniform("g_LightColor");
-        Uniform lightPos = shader.getUniform("g_LightPosition");
-        Uniform lightDir = shader.getUniform("g_LightDirection");
-        lightColor.setVector4Length(numLights);
-        lightPos.setVector4Length(numLights);
-        lightDir.setVector4Length(numLights);
-
+        Uniform lightData = shader.getUniform("g_LightData");        
+        lightData.setVector4Length(numLights * 3);//8 lights * max 3        
         Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
-        ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
-
-        int lightIndex = 0;
+        
 
-        for (int i = 0; i < numLights; i++) {
-            if (lightList.size() <= i) {
-                lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
-                lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
-            } else {
-                Light l = lightList.get(i);
+        if (startIndex != 0) {        
+            // apply additive blending for 2nd and future passes
+            rm.getRenderer().applyRenderState(additiveLight);
+            ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);            
+        }else{
+            ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList,true));
+        }
+        
+        int lightDataIndex = 0;
+        TempVars vars = TempVars.get();
+        Vector4f tmpVec = vars.vect4f1;
+        int curIndex;
+        int endIndex = numLights + startIndex;
+        for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
+                
+                
+                Light l = lightList.get(curIndex);              
+                if(l.getType() == Light.Type.Ambient){
+                    endIndex++;    
+                    continue;
+                }
                 ColorRGBA color = l.getColor();
-                lightColor.setVector4InArray(color.getRed(),
+                //Color
+                lightData.setVector4InArray(color.getRed(),
                         color.getGreen(),
                         color.getBlue(),
                         l.getType().getId(),
-                        i);
-
+                        lightDataIndex);
+                lightDataIndex++;
+                
                 switch (l.getType()) {
                     case Directional:
                         DirectionalLight dl = (DirectionalLight) l;
-                        Vector3f dir = dl.getDirection();
-                        lightPos.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), -1, lightIndex);
+                        Vector3f dir = dl.getDirection();                        
+                        //Data directly sent in view space to avoid a matrix mult for each pixel
+                        tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
+                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);      
+//                        tmpVec.divideLocal(tmpVec.w);
+//                        tmpVec.normalizeLocal();
+                        lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
+                        lightDataIndex++;
+                        //PADDING
+                        lightData.setVector4InArray(0,0,0,0, lightDataIndex);
+                        lightDataIndex++;
                         break;
                     case Point:
                         PointLight pl = (PointLight) l;
                         Vector3f pos = pl.getPosition();
                         float invRadius = pl.getInvRadius();
-                        lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightIndex);
+                        tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
+                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);    
+                        //tmpVec.divideLocal(tmpVec.w);
+                        lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
+                        lightDataIndex++;
+                        //PADDING
+                        lightData.setVector4InArray(0,0,0,0, lightDataIndex);
+                        lightDataIndex++;
                         break;
-                    case Spot:
+                    case Spot:                      
                         SpotLight sl = (SpotLight) l;
                         Vector3f pos2 = sl.getPosition();
                         Vector3f dir2 = sl.getDirection();
                         float invRange = sl.getInvSpotRange();
                         float spotAngleCos = sl.getPackedAngleCos();
-
-                        lightPos.setVector4InArray(pos2.getX(), pos2.getY(), pos2.getZ(), invRange, lightIndex);
-                        lightDir.setVector4InArray(dir2.getX(), dir2.getY(), dir2.getZ(), spotAngleCos, lightIndex);
-                        break;
-                    case Ambient:
-                        // skip this light. Does not increase lightIndex
-                        continue;
+                        tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(),  1.0f);
+                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);   
+                       // tmpVec.divideLocal(tmpVec.w);
+                        lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
+                        lightDataIndex++;
+                        
+                        //We transform the spot direction in view space here to save 5 varying later in the lighting shader
+                        //one vec4 less and a vec4 that becomes a vec3
+                        //the downside is that spotAngleCos decoding happens now in the frag shader.
+                        tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(),  0.0f);
+                        rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);                           
+                        tmpVec.normalizeLocal();
+                        lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
+                        lightDataIndex++;
+                        break;                    
                     default:
                         throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
                 }
-            }
-
-            lightIndex++;
-        }
-
-        while (lightIndex < numLights) {
-            lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
-            lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
-
-            lightIndex++;
         }
+        vars.release();        
+        //Padding of unsued buffer space
+        while(lightDataIndex < numLights * 3) {
+            lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
+            lightDataIndex++;             
+        } 
+        return curIndex;
     }
 
     protected void renderMultipassLighting(Shader shader, Geometry g, LightList lightList, RenderManager rm) {
@@ -830,7 +862,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
 
             if (isFirstLight) {
                 // set ambient color for first light only
-                ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
+                ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, false));
                 isFirstLight = false;
                 isSecondLight = true;
             } else if (isSecondLight) {
@@ -885,9 +917,9 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                     tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
                     lightPos.setValue(VarType.Vector4, tmpLightPosition);
 
-                    //We transform the spot directoin in view space here to save 5 varying later in the lighting shader
+                    //We transform the spot direction in view space here to save 5 varying later in the lighting shader
                     //one vec4 less and a vec4 that becomes a vec3
-                    //the downside is that spotAngleCos decoding happen now in the frag shader.
+                    //the downside is that spotAngleCos decoding happens now in the frag shader.
                     tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
                     rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
                     tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
@@ -906,7 +938,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         if (isFirstLight && lightList.size() > 0) {
             // There are only ambient lights in the scene. Render
             // a dummy "normal light" so we can see the ambient
-            ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
+            ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, false));
             lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
             lightPos.setValue(VarType.Vector4, nullDirLight);
             r.setShader(shader);
@@ -955,9 +987,12 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                 for (TechniqueDef techDef : techDefs) {
                     if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
                         // use the first one that supports all the caps
-                        tech = new Technique(this, techDef);
+                        tech = new Technique(this, techDef);                        
                         techniques.put(name, tech);
-                        break;
+                        if(tech.getDef().getLightMode() == renderManager.getPreferredLightMode() ||
+                               tech.getDef().getLightMode() == LightMode.Disable){
+                            break;  
+                        }
                     }
                     lastTech = techDef;
                 }
@@ -990,7 +1025,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         }
 
         technique = tech;
-        tech.makeCurrent(def.getAssetManager(), true, rendererCaps);
+        tech.makeCurrent(def.getAssetManager(), true, rendererCaps, renderManager);
 
         // shader was changed
         sortingId = -1;
@@ -1000,7 +1035,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         if (technique == null) {
             selectTechnique("Default", rm);
         } else {
-            technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps());
+            technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps(), rm);
         }
     }
 
@@ -1162,8 +1197,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                 r.setLighting(null);
                 break;
             case SinglePass:
-                updateLightListUniforms(shader, geom, lights, 4);
-                break;
+                int nbRenderedLights = 0;
+                resetUniformsNotSetByCurrent(shader);
+                while(nbRenderedLights < lights.size()){
+                    nbRenderedLights = updateLightListUniforms(shader, geom, lights, rm.getSinglePassLightBatchSize(), rm, nbRenderedLights);
+                    r.setShader(shader);
+                    renderMeshFromGeometry(r, geom);
+                }
+                return;
             case FixedPipeline:
                 r.setLighting(lights);
                 break;

+ 9 - 2
jme3-core/src/main/java/com/jme3/material/Technique.java

@@ -33,8 +33,8 @@ package com.jme3.material;
 
 import com.jme3.asset.AssetManager;
 import com.jme3.renderer.Caps;
+import com.jme3.renderer.RenderManager;
 import com.jme3.shader.*;
-import com.jme3.util.ListMap;
 import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
@@ -172,7 +172,7 @@ public class Technique /* implements Savable */ {
      * 
      * @param assetManager The asset manager to use for loading shaders.
      */
-    public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet<Caps> rendererCaps) {
+    public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet<Caps> rendererCaps, RenderManager rm) {
         if (!def.isUsingShaders()) {
             // No shaders are used, no processing is neccessary. 
             return;
@@ -182,6 +182,13 @@ public class Technique /* implements Savable */ {
             if (defines.update(owner.getParamsMap(), def)) {
                 needReload = true;
             }
+            if(getDef().getLightMode()== TechniqueDef.LightMode.SinglePass){
+                defines.set("SINGLE_PASS_LIGHTING", VarType.Boolean, true);
+                defines.set("NB_LIGHTS", VarType.Int, rm.getSinglePassLightBatchSize()*3 );
+            }else{
+                defines.set("SINGLE_PASS_LIGHTING", VarType.Boolean, null);
+            }
+            
         }
 
         if (needReload) {

+ 16 - 1
jme3-core/src/main/java/com/jme3/renderer/Caps.java

@@ -236,7 +236,22 @@ public enum Caps {
     /**
      * Supports sRGB framebuffers and sRGB texture format
      */
-    Srgb;
+    Srgb,
+    
+    /**
+     * Supports blitting framebuffers.
+     */
+    FrameBufferBlit,
+    
+    /**
+     * Supports {@link Format#DXT1} and sister formats.
+     */
+    TextureCompressionS3TC,
+    
+    /**
+     * Supports anisotropic texture filtering.
+     */
+    TextureFilterAnisotropic;
 
     /**
      * Returns true if given the renderer capabilities, the texture

+ 17 - 0
jme3-core/src/main/java/com/jme3/renderer/RenderContext.java

@@ -35,6 +35,7 @@ import com.jme3.material.RenderState;
 import com.jme3.math.ColorRGBA;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
+import com.jme3.shader.Shader;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Image;
 
@@ -138,11 +139,21 @@ public class RenderContext {
      * @see Renderer#setShader(com.jme3.shader.Shader) 
      */
     public int boundShaderProgram;
+    
+    /**
+     * @see Renderer#setShader(com.jme3.shader.Shader) 
+     */
+    public Shader boundShader;
 
     /**
      * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer) 
      */
     public int boundFBO = 0;
+    
+    /**
+     * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer) 
+     */
+    public FrameBuffer boundFB;
 
     /**
      * Currently bound Renderbuffer
@@ -279,6 +290,10 @@ public class RenderContext {
      */
     public RenderState.TestFunction alphaFunc = RenderState.TestFunction.Greater;
 
+    
+    public int initialDrawBuf;
+    public int initialReadBuf;
+    
     /**
      * Reset the RenderContext to default GL state
      */
@@ -298,7 +313,9 @@ public class RenderContext {
         blendMode = RenderState.BlendMode.Off;
         wireframe = false;
         boundShaderProgram = 0;
+        boundShader = null;
         boundFBO = 0;
+        boundFB = null;
         boundRB = 0;
         boundDrawBuf = -1; 
         boundReadBuf = -1;

+ 30 - 0
jme3-core/src/main/java/com/jme3/renderer/RenderManager.java

@@ -38,6 +38,7 @@ import com.jme3.material.Material;
 import com.jme3.material.MaterialDef;
 import com.jme3.material.RenderState;
 import com.jme3.material.Technique;
+import com.jme3.material.TechniqueDef;
 import com.jme3.math.*;
 import com.jme3.post.SceneProcessor;
 import com.jme3.profile.AppProfiler;
@@ -89,6 +90,8 @@ public class RenderManager {
     private boolean handleTranlucentBucket = true;
     private AppProfiler prof;
     private LightFilter lightFilter = new DefaultLightFilter();
+    private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass;
+    private int singlePassLightBatchSize = 1;
 
     /**
      * Create a high-level rendering interface over the
@@ -780,6 +783,33 @@ public class RenderManager {
         vp.getQueue().clear();
     }
 
+    /**
+     * Sets the light filter to use when rendering Lighted Geometries
+     * 
+     * @see LightFilter
+     * @param lightFilter The light filter tose. Set it to null if you want all lights to be rendered
+     */
+    public void setLightFilter(LightFilter lightFilter) {
+        this.lightFilter = lightFilter;
+    }
+
+    public void setPreferredLightMode(TechniqueDef.LightMode preferredLightMode) {
+        this.preferredLightMode = preferredLightMode;
+    }
+
+    public TechniqueDef.LightMode getPreferredLightMode() {
+        return preferredLightMode;
+    }
+
+    public int getSinglePassLightBatchSize() {
+        return singlePassLightBatchSize;
+    }
+
+    public void setSinglePassLightBatchSize(int singlePassLightBatchSize) {
+        this.singlePassLightBatchSize = singlePassLightBatchSize;
+    }
+    
+    
     /**
      * Render the given viewport queues.
      * <p>

+ 31 - 109
jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag

@@ -1,7 +1,9 @@
 #import "Common/ShaderLib/Parallax.glsllib"
 #import "Common/ShaderLib/Optics.glsllib"
-#define ATTENUATION
-//#define HQ_ATTENUATION
+#ifndef VERTEX_LIGHTING
+    #import "Common/ShaderLib/PhongLighting.glsllib"
+    #import "Common/ShaderLib/Lighting.glsllib"
+#endif
 
 varying vec2 texCoord;
 #ifdef SEPARATE_TEXCOORD
@@ -58,82 +60,14 @@ varying vec3 SpecularSum;
 uniform float m_AlphaDiscardThreshold;
 
 #ifndef VERTEX_LIGHTING
-uniform float m_Shininess;
-
-#ifdef HQ_ATTENUATION
-uniform vec4 g_LightPosition;
-#endif
+    uniform float m_Shininess;
+    #ifdef USE_REFLECTION 
+        uniform float m_ReflectionPower;
+        uniform float m_ReflectionIntensity;
+        varying vec4 refVec;
 
-#ifdef USE_REFLECTION 
-    uniform float m_ReflectionPower;
-    uniform float m_ReflectionIntensity;
-    varying vec4 refVec;
-
-    uniform ENVMAP m_EnvMap;
-#endif
-
-float tangDot(in vec3 v1, in vec3 v2){
-    float d = dot(v1,v2);
-    #ifdef V_TANGENT
-        d = 1.0 - d*d;
-        return step(0.0, d) * sqrt(d);
-    #else
-        return d;
+        uniform ENVMAP m_EnvMap;
     #endif
-}
-
-float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
-    #ifdef MINNAERT
-        float NdotL = max(0.0, dot(norm, lightdir));
-        float NdotV = max(0.0, dot(norm, viewdir));
-        return NdotL * pow(max(NdotL * NdotV, 0.1), -1.0) * 0.5;
-    #else
-        return max(0.0, dot(norm, lightdir));
-    #endif
-}
-
-float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
-    // NOTE: check for shiny <= 1 removed since shininess is now 
-    // 1.0 by default (uses matdefs default vals)
-    #ifdef LOW_QUALITY
-       // Blinn-Phong
-       // Note: preferably, H should be computed in the vertex shader
-       vec3 H = (viewdir + lightdir) * vec3(0.5);
-       return pow(max(tangDot(H, norm), 0.0), shiny);
-    #elif defined(WARDISO)
-        // Isotropic Ward
-        vec3 halfVec = normalize(viewdir + lightdir);
-        float NdotH  = max(0.001, tangDot(norm, halfVec));
-        float NdotV  = max(0.001, tangDot(norm, viewdir));
-        float NdotL  = max(0.001, tangDot(norm, lightdir));
-        float a      = tan(acos(NdotH));
-        float p      = max(shiny/128.0, 0.001);
-        return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL)));
-    #else
-       // Standard Phong
-       vec3 R = reflect(-lightdir, norm);
-       return pow(max(tangDot(R, viewdir), 0.0), shiny);
-    #endif
-}
-
-vec2 computeLighting(in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){
-   float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);
-   float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess);
-
-   #ifdef HQ_ATTENUATION
-    float att = clamp(1.0 - g_LightPosition.w * length(lightVec), 0.0, 1.0);
-   #else
-    float att = vLightDir.w;
-   #endif
-
-   if (m_Shininess <= 1.0) {
-       specularFactor = 0.0; // should be one instruction on most cards ..
-   }
-
-   specularFactor *= diffuseFactor;
-
-   return vec2(diffuseFactor, specularFactor) * vec2(att);
-}
 #endif
 
 void main(){
@@ -172,40 +106,13 @@ void main(){
     #ifdef ALPHAMAP
        alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r;
     #endif
-    if(alpha < m_AlphaDiscardThreshold){
-        discard;
-    }
-
-    #ifndef VERTEX_LIGHTING
-        float spotFallOff = 1.0;
-
-        #if __VERSION__ >= 110
-          // allow use of control flow
-          if(g_LightDirection.w != 0.0){
-        #endif
+    #ifdef DISCARD_ALPHA
+        if(alpha < m_AlphaDiscardThreshold){
+            discard;
+        }
+    #endif
 
-          vec3 L       = normalize(lightVec.xyz);
-          vec3 spotdir = normalize(g_LightDirection.xyz);
-          float curAngleCos = dot(-L, spotdir);             
-          float innerAngleCos = floor(g_LightDirection.w) * 0.001;
-          float outerAngleCos = fract(g_LightDirection.w);
-          float innerMinusOuter = innerAngleCos - outerAngleCos;
-          spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter;
 
-          #if __VERSION__ >= 110
-              if(spotFallOff <= 0.0){
-                  gl_FragColor.rgb = AmbientSum * diffuseColor.rgb;
-                  gl_FragColor.a   = alpha;
-                  return;
-              }else{
-                  spotFallOff = clamp(spotFallOff, 0.0, 1.0);
-              }
-             }
-          #else
-             spotFallOff = clamp(spotFallOff, step(g_LightDirection.w, 0.001), 1.0);
-          #endif
-     #endif
- 
     // ***********************
     // Read from textures
     // ***********************
@@ -257,8 +164,23 @@ void main(){
        vec4 lightDir = vLightDir;
        lightDir.xyz = normalize(lightDir.xyz);
        vec3 viewDir = normalize(vViewDir);
+       float spotFallOff = 1.0;
+
+       #if __VERSION__ >= 110
+        // allow use of control flow
+        if(g_LightDirection.w != 0.0){
+       #endif
+          spotFallOff =  computeSpotFalloff(g_LightDirection, lightVec);
+       #if __VERSION__ >= 110
+          if(spotFallOff <= 0.0){
+              gl_FragColor.rgb = AmbientSum * diffuseColor.rgb;
+              gl_FragColor.a   = alpha;
+              return;
+          }
+         }        
+       #endif
 
-       vec2   light = computeLighting(normal, viewDir, lightDir.xyz) * spotFallOff;
+       vec2   light = computeLighting(normal, viewDir, lightDir.xyz, lightDir.w * spotFallOff, m_Shininess) ;
        #ifdef COLORRAMP
            diffuseColor.rgb  *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
            specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;

+ 45 - 36
jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md

@@ -6,34 +6,12 @@ MaterialDef Phong Lighting {
         // For better performance
         Boolean VertexLighting
 
-        // Use more efficent algorithms to improve performance
-        Boolean LowQuality
-
-        // Improve quality at the cost of performance
-        Boolean HighQuality
-
-        // Output alpha from the diffuse map
-        Boolean UseAlpha
-
         // Alpha threshold for fragment discarding
         Float AlphaDiscardThreshold (AlphaTestFallOff)
 
-        // Normal map is in BC5/ATI2n/LATC/3Dc compression format
-        Boolean LATC
-
         // Use the provided ambient, diffuse, and specular colors
         Boolean UseMaterialColors
 
-        // Activate shading along the tangent, instead of the normal
-        // Requires tangent data to be available on the model.
-        Boolean VTangent
-
-        // Use minnaert diffuse instead of lambert
-        Boolean Minnaert
-
-        // Use ward specular instead of phong
-        Boolean WardIso
-
         // Use vertex color as an additional diffuse color.
         Boolean UseVertexColor
 
@@ -133,9 +111,48 @@ MaterialDef Phong Lighting {
         Int NumberOfBones
         Matrix4Array BoneMatrices
                 
+        //For instancing
         Boolean UseInstancing
     }
 
+ Technique {
+        LightMode SinglePass
+        
+        VertexShader GLSL100:   Common/MatDefs/Light/SPLighting.vert
+        FragmentShader GLSL100: Common/MatDefs/Light/SPLighting.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            NormalMatrix
+            WorldViewMatrix
+            ViewMatrix
+            CameraPosition
+            WorldMatrix
+            ViewProjectionMatrix            
+        }
+
+        Defines {           
+            VERTEX_COLOR : UseVertexColor
+            VERTEX_LIGHTING : VertexLighting           
+            MATERIAL_COLORS : UseMaterialColors         
+            DIFFUSEMAP : DiffuseMap
+            NORMALMAP : NormalMap
+            SPECULARMAP : SpecularMap
+            PARALLAXMAP : ParallaxMap
+            NORMALMAP_PARALLAX : PackedNormalParallax
+            STEEP_PARALLAX : SteepParallax
+            ALPHAMAP : AlphaMap
+            COLORRAMP : ColorRamp
+            LIGHTMAP : LightMap
+            SEPARATE_TEXCOORD : SeparateTexCoord
+            DISCARD_ALPHA : AlphaDiscardThreshold
+            USE_REFLECTION : EnvMap
+            SPHERE_MAP : SphereMap  
+            NUM_BONES : NumberOfBones                        
+            INSTANCING : UseInstancing
+        }
+    }
+
     Technique {
 
         LightMode MultiPass
@@ -154,17 +171,9 @@ MaterialDef Phong Lighting {
         }
 
         Defines {
-            LATC : LATC
             VERTEX_COLOR : UseVertexColor
-            VERTEX_LIGHTING : VertexLighting
-            ATTENUATION : Attenuation
+            VERTEX_LIGHTING : VertexLighting            
             MATERIAL_COLORS : UseMaterialColors
-            V_TANGENT : VTangent
-            MINNAERT  : Minnaert
-            WARDISO   : WardIso
-            LOW_QUALITY : LowQuality
-            HQ_ATTENUATION : HighQuality
-
             DIFFUSEMAP : DiffuseMap
             NORMALMAP : NormalMap
             SPECULARMAP : SpecularMap
@@ -175,16 +184,16 @@ MaterialDef Phong Lighting {
             COLORRAMP : ColorRamp
             LIGHTMAP : LightMap
             SEPARATE_TEXCOORD : SeparateTexCoord
-
+            DISCARD_ALPHA : AlphaDiscardThreshold
             USE_REFLECTION : EnvMap
             SPHERE_MAP : SphereMap  
-
-            NUM_BONES : NumberOfBones
-                        
+            NUM_BONES : NumberOfBones                        
             INSTANCING : UseInstancing
         }
     }
 
+   
+
     Technique PreShadow {
 
         VertexShader GLSL100 :   Common/MatDefs/Shadow/PreShadow.vert
@@ -373,4 +382,4 @@ MaterialDef Phong Lighting {
         }
     }
 
-}
+}

+ 26 - 87
jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert

@@ -1,8 +1,10 @@
 #import "Common/ShaderLib/Instancing.glsllib"
-#define ATTENUATION
-//#define HQ_ATTENUATION
-
 #import "Common/ShaderLib/Skinning.glsllib"
+#import "Common/ShaderLib/Lighting.glsllib"
+#ifdef VERTEX_LIGHTING
+    #import "Common/ShaderLib/PhongLighting.glsllib"    
+#endif
+
 
 uniform vec4 m_Ambient;
 uniform vec4 m_Diffuse;
@@ -28,7 +30,6 @@ attribute vec2 inTexCoord;
 attribute vec3 inNormal;
 
 varying vec3 lightVec;
-//varying vec4 spotVec;
 
 #ifdef VERTEX_COLOR
   attribute vec4 inColor;
@@ -39,8 +40,7 @@ varying vec3 lightVec;
 
   #ifndef NORMALMAP
     varying vec3 vNormal;
-  #endif
-  //varying vec3 vPosition;
+  #endif  
   varying vec3 vViewDir;
   varying vec4 vLightDir;
 #else
@@ -77,57 +77,6 @@ varying vec3 lightVec;
     }
 #endif
 
-// JME3 lights in world space
-void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
-    float posLight = step(0.5, color.w);
-    vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
-    lightVec = tempVec;  
-    #ifdef ATTENUATION
-     float dist = length(tempVec);
-     lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
-     lightDir.xyz = tempVec / vec3(dist);
-    #else
-     lightDir = vec4(normalize(tempVec), 1.0);
-    #endif
-}
-
-#ifdef VERTEX_LIGHTING
-  float lightComputeDiffuse(in vec3 norm, in vec3 lightdir){
-      return max(0.0, dot(norm, lightdir));
-  }
-
-  float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
-      if (shiny <= 1.0){
-          return 0.0;
-      }
-      #ifndef LOW_QUALITY
-        vec3 H = (viewdir + lightdir) * vec3(0.5);
-        return pow(max(dot(H, norm), 0.0), shiny);
-      #else
-        return 0.0;
-      #endif
-  }
-
-vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightPos){
-     vec4 lightDir;
-     lightComputeDir(wvPos, g_LightColor, wvLightPos, lightDir);
-     float spotFallOff = 1.0;
-     if(g_LightDirection.w != 0.0){
-          vec3 L=normalize(lightVec.xyz);
-          vec3 spotdir = normalize(g_LightDirection.xyz);
-          float curAngleCos = dot(-L, spotdir);    
-          float innerAngleCos = floor(g_LightDirection.w) * 0.001;
-          float outerAngleCos = fract(g_LightDirection.w);
-          float innerMinusOuter = innerAngleCos - outerAngleCos;
-          spotFallOff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0);
-     }
-     float diffuseFactor = lightComputeDiffuse(wvNorm, lightDir.xyz);
-     float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, lightDir.xyz, m_Shininess);
-     //specularFactor *= step(0.01, diffuseFactor);
-     return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w)*spotFallOff;
-  }
-#endif
-
 void main(){
    vec4 modelSpacePos = vec4(inPosition, 1.0);
    vec3 modelSpaceNorm = inNormal;
@@ -154,11 +103,6 @@ void main(){
    vec3 wvNormal  = normalize(TransformNormal(modelSpaceNorm));//normalize(g_NormalMatrix * modelSpaceNorm);
    vec3 viewDir = normalize(-wvPosition);
   
-       //vec4 lightColor = g_LightColor[gl_InstanceID];
-       //vec4 lightPos   = g_LightPosition[gl_InstanceID];
-       //vec4 wvLightPos = (g_ViewMatrix * vec4(lightPos.xyz, lightColor.w));
-       //wvLightPos.w = lightPos.w;
-
    vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz,clamp(g_LightColor.w,0.0,1.0)));
    wvLightPos.w = g_LightPosition.w;
    vec4 lightColor = g_LightColor;
@@ -166,41 +110,24 @@ void main(){
    #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
      vec3 wvTangent = normalize(TransformNormal(modelSpaceTan));
      vec3 wvBinormal = cross(wvNormal, wvTangent);
-
      mat3 tbnMat = mat3(wvTangent, wvBinormal * inTangent.w,wvNormal);
-     
-     //vPosition = wvPosition * tbnMat;
-     //vViewDir  = viewDir * tbnMat;
+ 
      vViewDir  = -wvPosition * tbnMat;
-     lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
+     lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec);
      vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz;
    #elif !defined(VERTEX_LIGHTING)
      vNormal = wvNormal;
-
-     //vPosition = wvPosition;
      vViewDir = viewDir;
-
-     lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
-
-     #ifdef V_TANGENT
-        vNormal = normalize(TransformNormal(inTangent.xyz));
-        vNormal = -cross(cross(vLightDir.xyz, vNormal), vNormal);
-     #endif
+     lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec);
    #endif
 
-   //computing spot direction in view space and unpacking spotlight cos
-//   spotVec = (g_ViewMatrix * vec4(g_LightDirection.xyz, 0.0) );
-//   spotVec.w  = floor(g_LightDirection.w) * 0.001;
-//   lightVec.w = fract(g_LightDirection.w);
-
-   lightColor.w = 1.0;
    #ifdef MATERIAL_COLORS
       AmbientSum  = (m_Ambient  * g_AmbientLightColor).rgb;
-      DiffuseSum  =  m_Diffuse  * lightColor;
+      DiffuseSum  =  m_Diffuse  * vec4(lightColor.rgb, 1.0);
       SpecularSum = (m_Specular * lightColor).rgb;
     #else
-      AmbientSum  = vec3(0.2, 0.2, 0.2) * g_AmbientLightColor.rgb; // Default: ambient color is dark gray
-      DiffuseSum  = lightColor;
+      AmbientSum  = g_AmbientLightColor.rgb; // Default: ambient color is dark gray
+      DiffuseSum  =  vec4(lightColor.rgb, 1.0);
       SpecularSum = vec3(0.0);
     #endif
 
@@ -210,10 +137,22 @@ void main(){
     #endif
 
     #ifdef VERTEX_LIGHTING
-       vertexLightValues = computeLighting(wvPosition, wvNormal, viewDir, wvLightPos);
+        float spotFallOff = 1.0;
+        vec4 vLightDir;
+        lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec);
+        #if __VERSION__ >= 110
+            // allow use of control flow
+        if(lightColor.w > 1.0){
+        #endif           
+           spotFallOff = computeSpotFalloff(g_LightDirection, lightVec);
+        #if __VERSION__ >= 110           
+        }
+        #endif
+        
+        vertexLightValues = computeLighting(wvNormal, viewDir, vLightDir.xyz, vLightDir.w * spotFallOff, m_Shininess);
     #endif
 
-    #ifdef USE_REFLECTION
+    #ifdef USE_REFLECTION 
         computeRef(modelSpacePos);
     #endif 
 }

+ 218 - 0
jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag

@@ -0,0 +1,218 @@
+#import "Common/ShaderLib/Parallax.glsllib"
+#import "Common/ShaderLib/Optics.glsllib"
+#ifndef VERTEX_LIGHTING
+    #import "Common/ShaderLib/PhongLighting.glsllib"
+    #import "Common/ShaderLib/Lighting.glsllib"
+#endif
+
+varying vec2 texCoord;
+#ifdef SEPARATE_TEXCOORD
+  varying vec2 texCoord2;
+#endif
+
+varying vec3 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec3 SpecularSum;
+
+#ifndef VERTEX_LIGHTING
+    uniform mat4 g_ViewMatrix;
+    uniform vec4 g_LightData[NB_LIGHTS];
+    varying vec3 vPos;    
+#else
+    varying vec3 specularAccum;
+    varying vec4 diffuseAccum;
+#endif
+
+#ifdef DIFFUSEMAP
+  uniform sampler2D m_DiffuseMap;
+#endif
+
+#ifdef SPECULARMAP
+  uniform sampler2D m_SpecularMap;
+#endif
+
+#ifdef PARALLAXMAP
+  uniform sampler2D m_ParallaxMap;  
+#endif
+#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) 
+    uniform float m_ParallaxHeight;
+#endif
+
+#ifdef LIGHTMAP
+  uniform sampler2D m_LightMap;
+#endif
+  
+#ifdef NORMALMAP
+  uniform sampler2D m_NormalMap;   
+  varying vec3 vTangent;
+  varying vec3 vBinormal;
+#endif
+varying vec3 vNormal;
+
+#ifdef ALPHAMAP
+  uniform sampler2D m_AlphaMap;
+#endif
+
+#ifdef COLORRAMP
+  uniform sampler2D m_ColorRamp;
+#endif
+
+uniform float m_AlphaDiscardThreshold;
+
+#ifndef VERTEX_LIGHTING
+uniform float m_Shininess;
+
+    #ifdef USE_REFLECTION 
+        uniform float m_ReflectionPower;
+        uniform float m_ReflectionIntensity;
+        varying vec4 refVec;
+
+        uniform ENVMAP m_EnvMap;
+    #endif
+#endif
+
+void main(){
+    vec2 newTexCoord;
+     
+    #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) 
+     
+       #ifdef STEEP_PARALLAX
+           #ifdef NORMALMAP_PARALLAX
+               //parallax map is stored in the alpha channel of the normal map         
+               newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+           #else
+               //parallax map is a texture
+               newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);         
+           #endif
+       #else
+           #ifdef NORMALMAP_PARALLAX
+               //parallax map is stored in the alpha channel of the normal map         
+               newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+           #else
+               //parallax map is a texture
+               newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
+           #endif
+       #endif
+    #else
+       newTexCoord = texCoord;    
+    #endif
+    
+   #ifdef DIFFUSEMAP
+      vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord);
+    #else
+      vec4 diffuseColor = vec4(1.0);
+    #endif
+
+    float alpha = DiffuseSum.a * diffuseColor.a;
+
+    #ifdef ALPHAMAP
+       alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r;
+    #endif
+
+    #ifdef DISCARD_ALPHA
+        if(alpha < m_AlphaDiscardThreshold){
+            discard;
+        }
+    #endif
+ 
+    // ***********************
+    // Read from textures
+    // ***********************
+    #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+      vec4 normalHeight = texture2D(m_NormalMap, newTexCoord);
+      //Note the -2.0 and -1.0. We invert the green channel of the normal map, 
+      //as it's complient with normal maps generated with blender.
+      //see http://hub.jmonkeyengine.org/forum/topic/parallax-mapping-fundamental-bug/#post-256898
+      //for more explanation.
+      vec3 normal = normalize((normalHeight.xyz * vec3(2.0,-2.0,2.0) - vec3(1.0,-1.0,1.0)));
+    #elif !defined(VERTEX_LIGHTING)
+      vec3 normal = normalize(vNormal);            
+    #endif
+
+    #ifdef SPECULARMAP
+      vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
+    #else
+      vec4 specularColor = vec4(1.0);
+    #endif
+
+    #ifdef LIGHTMAP
+       vec3 lightMapColor;
+       #ifdef SEPARATE_TEXCOORD
+          lightMapColor = texture2D(m_LightMap, texCoord2).rgb;
+       #else
+          lightMapColor = texture2D(m_LightMap, texCoord).rgb;
+       #endif
+       specularColor.rgb *= lightMapColor;
+       diffuseColor.rgb  *= lightMapColor;
+    #endif
+
+    #ifdef VERTEX_LIGHTING
+        gl_FragColor.rgb = AmbientSum  * diffuseColor.rgb 
+                            +diffuseAccum.rgb *diffuseColor.rgb
+                            +specularAccum.rgb * specularColor.rgb;
+        gl_FragColor.a=1.0;                           
+    #else       
+        
+        int i = 0;
+        gl_FragColor.rgb = AmbientSum * diffuseColor.rgb;
+
+        #ifdef USE_REFLECTION
+             vec4 refColor = Optics_GetEnvColor(m_EnvMap, refVec.xyz);
+        #endif
+
+        #ifdef NORMALMAP   
+            mat3 tbnMat = mat3(normalize(vTangent.xyz) , normalize(vBinormal.xyz) , normalize(vNormal.xyz));
+        #endif
+
+        for( int i = 0;i < NB_LIGHTS; i+=3){
+            vec4 lightColor = g_LightData[i];
+            vec4 lightData1 = g_LightData[i+1];                
+            vec4 lightDir;
+            vec3 lightVec;            
+            lightComputeDir(vPos, lightColor.w, lightData1, lightDir,lightVec);
+
+            float spotFallOff = 1.0;
+            #if __VERSION__ >= 110
+                // allow use of control flow
+            if(lightColor.w > 1.0){
+            #endif
+                spotFallOff =  computeSpotFalloff(g_LightData[i+2], lightVec);
+            #if __VERSION__ >= 110
+            }
+            #endif
+         
+            #ifdef NORMALMAP         
+                //Normal map -> lighting is computed in tangent space
+                lightDir.xyz = normalize(lightDir.xyz * tbnMat);
+                vec3 viewDir = normalize(-vPos.xyz * tbnMat);
+            #else
+                //no Normal map -> lighting is computed in view space
+                lightDir.xyz = normalize(lightDir.xyz);
+                vec3 viewDir = normalize(-vPos.xyz);
+            #endif
+
+            vec2 light = computeLighting(normal, viewDir, lightDir.xyz, lightDir.w * spotFallOff , m_Shininess);
+
+            #ifdef COLORRAMP
+                diffuseColor.rgb  *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
+                specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
+            #endif
+
+            // Workaround, since it is not possible to modify varying variables
+            vec4 SpecularSum2 = vec4(SpecularSum, 1.0);
+            #ifdef USE_REFLECTION                    
+                 // Interpolate light specularity toward reflection color
+                 // Multiply result by specular map
+                 specularColor = mix(SpecularSum2 * light.y, refColor, refVec.w) * specularColor;
+
+                 SpecularSum2 = vec4(1.0);
+                 light.y = 1.0;
+            #endif
+
+            gl_FragColor.rgb += DiffuseSum.rgb * lightColor.rgb * diffuseColor.rgb  * vec3(light.x) +
+                                SpecularSum2.rgb * specularColor.rgb * vec3(light.y);
+        }
+           
+     #endif
+    gl_FragColor.a = alpha;
+}

+ 172 - 0
jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert

@@ -0,0 +1,172 @@
+#import "Common/ShaderLib/Instancing.glsllib"
+#import "Common/ShaderLib/Skinning.glsllib"
+#import "Common/ShaderLib/Lighting.glsllib"
+#ifdef VERTEX_LIGHTING
+    #import "Common/ShaderLib/PhongLighting.glsllib"
+#endif
+
+
+uniform vec4 m_Ambient;
+uniform vec4 m_Diffuse;
+uniform vec4 m_Specular;
+uniform float m_Shininess;
+
+#if defined(VERTEX_LIGHTING)
+    uniform vec4 g_LightData[NB_LIGHTS];
+#endif
+uniform vec4 g_AmbientLightColor;
+varying vec2 texCoord;
+
+#ifdef SEPARATE_TEXCOORD
+  varying vec2 texCoord2;
+  attribute vec2 inTexCoord2;
+#endif
+
+varying vec3 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec3 SpecularSum;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+attribute vec3 inNormal;
+
+#ifdef VERTEX_COLOR
+  attribute vec4 inColor;
+#endif
+
+#ifndef VERTEX_LIGHTING
+    varying vec3 vNormal;
+    varying vec3 vPos;
+    #ifdef NORMALMAP
+        attribute vec4 inTangent;
+        varying vec3 vTangent;
+        varying vec3 vBinormal;
+    #endif
+#else
+    varying vec3 specularAccum;
+    varying vec4 diffuseAccum;
+#endif
+
+#ifdef USE_REFLECTION
+    uniform vec3 g_CameraPosition;
+    uniform vec3 m_FresnelParams;
+    varying vec4 refVec;
+
+    /**
+     * Input:
+     * attribute inPosition
+     * attribute inNormal
+     * uniform g_WorldMatrix
+     * uniform g_CameraPosition
+     *
+     * Output:
+     * varying refVec
+     */
+    void computeRef(in vec4 modelSpacePos){
+        // vec3 worldPos = (g_WorldMatrix * modelSpacePos).xyz;
+        vec3 worldPos = TransformWorld(modelSpacePos).xyz;
+
+        vec3 I = normalize( g_CameraPosition - worldPos  ).xyz;
+        // vec3 N = normalize( (g_WorldMatrix * vec4(inNormal, 0.0)).xyz );
+        vec3 N = normalize( TransformWorld(vec4(inNormal, 0.0)).xyz );
+
+        refVec.xyz = reflect(I, N);
+        refVec.w   = m_FresnelParams.x + m_FresnelParams.y * pow(1.0 + dot(I, N), m_FresnelParams.z);
+    }
+#endif
+
+void main(){
+   vec4 modelSpacePos = vec4(inPosition, 1.0);
+   vec3 modelSpaceNorm = inNormal;
+   
+   #if  defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+        vec3 modelSpaceTan  = inTangent.xyz;
+   #endif
+
+   #ifdef NUM_BONES
+        #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+        Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
+        #else
+        Skinning_Compute(modelSpacePos, modelSpaceNorm);
+        #endif
+   #endif
+
+   gl_Position = TransformWorldViewProjection(modelSpacePos);
+   texCoord = inTexCoord;
+   #ifdef SEPARATE_TEXCOORD
+      texCoord2 = inTexCoord2;
+   #endif
+
+   vec3 wvPosition = TransformWorldView(modelSpacePos).xyz;
+   vec3 wvNormal  = normalize(TransformNormal(modelSpaceNorm));
+   vec3 viewDir = normalize(-wvPosition);
+  
+       
+    #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+      vTangent = TransformNormal(modelSpaceTan);
+      vBinormal = cross(wvNormal, vTangent)* inTangent.w;      
+      vNormal = wvNormal;         
+      vPos = wvPosition;
+    #elif !defined(VERTEX_LIGHTING)
+      vNormal = wvNormal;          
+      vPos = wvPosition;
+    #endif
+   
+    #ifdef MATERIAL_COLORS
+        AmbientSum  = m_Ambient.rgb * g_AmbientLightColor.rgb; 
+        SpecularSum = m_Specular.rgb;
+        DiffuseSum = m_Diffuse;                   
+    #else
+        AmbientSum  = g_AmbientLightColor.rgb; 
+        SpecularSum = vec3(0.0);
+        DiffuseSum = vec4(1.0);
+    #endif
+    #ifdef VERTEX_COLOR               
+        AmbientSum *= inColor.rgb;
+        DiffuseSum *= inColor;
+    #endif
+    #ifdef VERTEX_LIGHTING
+        int i = 0;
+        diffuseAccum = vec4(0.0);
+        specularAccum = vec3(0.0);
+        vec4 diffuseColor;
+        vec3 specularColor;
+        for (int i =0;i < NB_LIGHTS; i+=3){
+            vec4 lightColor = g_LightData[i];            
+            vec4 lightData1 = g_LightData[i+1];            
+            DiffuseSum = vec4(1.0);
+            #ifdef MATERIAL_COLORS
+              diffuseColor  = m_Diffuse * vec4(lightColor.rgb, 1.0);                
+              specularColor = m_Specular.rgb * lightColor.rgb;
+            #else                
+              diffuseColor  = vec4(lightColor.rgb, 1.0);
+              specularColor = vec3(0.0);
+            #endif
+
+            vec4 lightDir;
+            vec3 lightVec;
+            lightComputeDir(wvPosition, lightColor.w, lightData1, lightDir, lightVec);
+          //  lightDir = normalize(lightDir);
+          //  lightVec = normalize(lightVec);
+            
+            float spotFallOff = 1.0;
+            #if __VERSION__ >= 110
+                // allow use of control flow
+            if(lightColor.w > 1.0){
+            #endif
+               vec4 lightDirection = g_LightData[i+2];
+               spotFallOff = computeSpotFalloff(lightDirection, lightVec);
+            #if __VERSION__ >= 110
+            }
+            #endif
+            vec2 v = computeLighting(wvNormal, viewDir, lightDir.xyz, lightDir.w  * spotFallOff, m_Shininess);
+            diffuseAccum +=v.x * diffuseColor;
+            specularAccum += v.y * specularColor;
+        }
+    #endif
+    
+
+    #ifdef USE_REFLECTION
+        computeRef(modelSpacePos);
+    #endif 
+}

+ 22 - 40
jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib

@@ -1,48 +1,30 @@
-#ifndef NUM_LIGHTS
-    #define NUM_LIGHTS 4
-#endif
+/*Common function for light calculations*/
 
-uniform mat4 g_ViewMatrix;
-uniform vec4 g_LightPosition[NUM_LIGHTS];
-uniform vec4 g_g_LightColor[NUM_LIGHTS];
-uniform float m_Shininess;
 
-float Lighting_Diffuse(vec3 norm, vec3 lightdir){
-    return max(0.0, dot(norm, lightdir));
-}
-
-float Lighting_Specular(vec3 norm, vec3 viewdir, vec3 lightdir, float shiny){
-    vec3 refdir = reflect(-lightdir, norm);
-    return pow(max(dot(refdir, viewdir), 0.0), shiny);
-}
-
-void Lighting_Direction(vec3 worldPos, vec4 color, vec4 position, out vec4 lightDir){
-    float posLight = step(0.5, color.w);
+/*
+* Computes light direction 
+* lightType should be 0.0,1.0,2.0, repectively for Directional, point and spot lights.
+* Outputs the light direction and the light half vector. 
+*/
+void lightComputeDir(in vec3 worldPos, in float ligthType, in vec4 position, out vec4 lightDir, out vec3 lightVec){
+    float posLight = step(0.5, ligthType);    
     vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
+    lightVec = tempVec;          
     float dist = length(tempVec);
-
     lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
-    lightDir.xyz = tempVec / dist;
+    lightDir.xyz = tempVec / vec3(dist);
 }
 
-void Lighting_ComputePS(vec3 tanNormal, mat3 tbnMat,
-                     int lightCount, out vec3 outDiffuse, out vec3 outSpecular){
-   // find tangent view dir & vert pos
-   vec3 tanViewDir = viewDir * tbnMat;
-
-   for (int i = 0; i < lightCount; i++){
-       // find light dir in tangent space, works for point & directional lights
-       vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition[i].xyz, g_LightColor[i].w));
-       wvLightPos.w = g_LightPosition[i].w;
-
-       vec4 tanLightDir;
-       Lighting_Direction(wvPosition, g_LightColor[i], wvLightPos, tanLightDir);
-       tanLightDir.xyz = tanLightDir.xyz * tbnMat;
-
-       vec3 lightScale = g_LightColor[i].rgb * tanLightDir.w;
-       float specular = Lighting_Specular(tanNormal, tanViewDir, tanLightDir.xyz, m_Shininess);
-       float diffuse = Lighting_Diffuse(tanNormal, tanLightDir.xyz);
-       outSpecular += specular * lightScale * step(0.01, diffuse) * g_LightColor[i].rgb;
-       outDiffuse += diffuse * lightScale * g_LightColor[i].rgb;
-   }
+/*
+* Computes the spot falloff for a spotlight
+*/
+float computeSpotFalloff(in vec4 lightDirection, in vec3 lightVector){
+    vec3 L=normalize(lightVector);
+    vec3 spotdir = normalize(lightDirection.xyz);
+    float curAngleCos = dot(-L, spotdir);    
+    float innerAngleCos = floor(lightDirection.w) * 0.001;
+    float outerAngleCos = fract(lightDirection.w);
+    float innerMinusOuter = innerAngleCos - outerAngleCos;
+    return  clamp((curAngleCos - outerAngleCos) / innerMinusOuter, step(lightDirection.w, 0.001), 1.0);
 }
+

+ 29 - 0
jme3-core/src/main/resources/Common/ShaderLib/PhongLighting.glsllib

@@ -0,0 +1,29 @@
+/*Standard Phong ligting*/
+
+/*
+* Computes diffuse factor
+*/
+float lightComputeDiffuse(in vec3 norm, in vec3 lightdir){
+    return max(0.0, dot(norm, lightdir));
+}
+
+/*
+* Computes specular factor    
+*/
+float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
+    vec3 R = reflect(-lightdir, norm);
+    return pow(max(dot(R, viewdir), 0.0), shiny);
+}
+
+/*
+* Computes diffuse and specular factors and pack them in a vec2 (x=diffuse, y=specular)
+*/
+vec2 computeLighting(in vec3 norm, in vec3 viewDir, in vec3 lightDir, in float attenuation, in float shininess){
+   float diffuseFactor = lightComputeDiffuse(norm, lightDir);
+   float specularFactor = lightComputeSpecular(norm, viewDir, lightDir, shininess);      
+   if (shininess <= 1.0) {
+       specularFactor = 0.0; // should be one instruction on most cards ..
+   }
+   specularFactor *= diffuseFactor;
+   return vec2(diffuseFactor, specularFactor) * vec2(attenuation);
+}

+ 255 - 0
jme3-examples/src/main/java/jme3test/light/TestManyLightsSingle.java

@@ -0,0 +1,255 @@
+/*
+ * 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 jme3test.light;
+
+import com.jme3.app.BasicProfilerState;
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.Light;
+import com.jme3.light.LightList;
+import com.jme3.light.PointLight;
+import com.jme3.light.SpotLight;
+import com.jme3.material.Material;
+import com.jme3.material.TechniqueDef;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.LightNode;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.shape.Box;
+
+public class TestManyLightsSingle extends SimpleApplication {
+
+    public static void main(String[] args) {
+        TestManyLightsSingle app = new TestManyLightsSingle();
+        app.start();
+    }
+    TechniqueDef.LightMode lm = TechniqueDef.LightMode.MultiPass;
+    int lightNum = 6 ;
+
+    @Override
+    public void simpleInitApp() {
+        renderManager.setPreferredLightMode(lm);
+        renderManager.setSinglePassLightBatchSize(lightNum);
+
+
+        flyCam.setMoveSpeed(10);
+
+        Node scene = (Node) assetManager.loadModel("Scenes/ManyLights/Main.scene");
+        rootNode.attachChild(scene);
+        Node n = (Node) rootNode.getChild(0);
+        LightList lightList = n.getWorldLightList();
+        Geometry g = (Geometry) n.getChild("Grid-geom-1");
+
+        g.getMaterial().setColor("Ambient", new ColorRGBA(0.2f, 0.2f, 0.2f, 1f));
+
+        /* A colored lit cube. Needs light source! */
+        Box boxMesh = new Box(1f, 1f, 1f);
+        Geometry boxGeo = new Geometry("Colored Box", boxMesh);
+        Material boxMat = g.getMaterial().clone();
+        boxMat.setBoolean("UseMaterialColors", true);
+        boxMat.setColor("Ambient", new ColorRGBA(0.2f, 0.2f, 0.2f, 1f));
+        boxMat.setColor("Diffuse", ColorRGBA.Blue);
+        boxGeo.setMaterial(boxMat);
+
+        int nb = 0;
+        for (Light light : lightList) {
+            nb++;
+            PointLight p = (PointLight) light;
+            if (nb >60) {
+                n.removeLight(light);
+            } else {
+
+                LightNode ln = new LightNode("l", light);
+                n.attachChild(ln);
+                ln.setLocalTranslation(p.getPosition());
+                int rand = FastMath.nextRandomInt(0, 3);
+                switch (rand) {
+                    case 0:
+                        light.setColor(ColorRGBA.Red);
+                        //   ln.addControl(new MoveControl(5f));
+                        break;
+                    case 1:
+                        light.setColor(ColorRGBA.Yellow);
+                        //    ln.addControl(new MoveControl(5f));
+                        break;
+                    case 2:
+                        light.setColor(ColorRGBA.Green);
+                        //ln.addControl(new MoveControl(-5f));
+                        break;
+                    case 3:
+                        light.setColor(ColorRGBA.Orange);
+                        //ln.addControl(new MoveControl(-5f));
+                        break;
+                }
+            }
+            Geometry b = boxGeo.clone();
+            n.attachChild(b);
+            b.setLocalTranslation(p.getPosition().x, 2, p.getPosition().z);
+
+        }
+
+
+//        cam.setLocation(new Vector3f(3.1893547f, 17.977385f, 30.8378f));
+//        cam.setRotation(new Quaternion(0.14317635f, 0.82302624f, -0.23777823f, 0.49557027f));
+
+        cam.setLocation(new Vector3f(-1.8901939f, 29.34097f, 73.07533f));
+        cam.setRotation(new Quaternion(0.0021000702f, 0.971012f, -0.23886925f, 0.008527749f));
+
+
+        BasicProfilerState profiler = new BasicProfilerState(true);
+        profiler.setGraphScale(1000f);
+
+        //  getStateManager().attach(profiler);
+//        guiNode.setCullHint(CullHint.Always);
+
+
+
+       
+        flyCam.setDragToRotate(true);
+        flyCam.setMoveSpeed(50);
+       
+
+        inputManager.addListener(new ActionListener() {
+            public void onAction(String name, boolean isPressed, float tpf) {
+                if (name.equals("toggle") && isPressed) {
+                    if (lm == TechniqueDef.LightMode.SinglePass) {
+                        lm = TechniqueDef.LightMode.MultiPass;
+                    } else {
+                        lm = TechniqueDef.LightMode.SinglePass;
+                    }
+                    renderManager.setPreferredLightMode(lm);
+                }
+                if (name.equals("lightsUp") && isPressed) {
+                    lightNum++;
+                    renderManager.setSinglePassLightBatchSize(lightNum);
+                    helloText.setText("nb lights per batch : " + lightNum);
+                }
+                if (name.equals("lightsDown") && isPressed) {
+                    lightNum--;
+                    renderManager.setSinglePassLightBatchSize(lightNum);
+                    helloText.setText("nb lights per batch : " + lightNum);
+                }
+            }
+        }, "toggle", "lightsUp", "lightsDown");
+
+        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+        inputManager.addMapping("lightsUp", new KeyTrigger(KeyInput.KEY_UP));
+        inputManager.addMapping("lightsDown", new KeyTrigger(KeyInput.KEY_DOWN));
+
+
+        SpotLight spot = new SpotLight();
+        spot.setDirection(new Vector3f(-1f, -1f, -1f).normalizeLocal());
+        spot.setColor(ColorRGBA.Blue.mult(5));
+        spot.setSpotOuterAngle(FastMath.DEG_TO_RAD * 20);
+        spot.setSpotInnerAngle(FastMath.DEG_TO_RAD * 5);
+        spot.setPosition(new Vector3f(10, 10, 20));
+        rootNode.addLight(spot);
+
+        DirectionalLight dl = new DirectionalLight();
+        dl.setDirection(new Vector3f(-1, -1, 1));
+        rootNode.addLight(dl);
+
+        AmbientLight al = new AmbientLight();
+        al.setColor(new ColorRGBA(0.2f, 0.2f, 0.2f, 1f));
+        rootNode.addLight(al);
+
+
+        /**
+         * Write text on the screen (HUD)
+         */
+        guiNode.detachAllChildren();
+        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        helloText = new BitmapText(guiFont, false);
+        helloText.setSize(guiFont.getCharSet().getRenderedSize());
+        helloText.setText("nb lights per batch : " + lightNum);
+        helloText.setLocalTranslation(300, helloText.getLineHeight(), 0);
+        guiNode.attachChild(helloText);
+
+
+    }
+    BitmapText helloText;
+    long time;
+    long nbFrames;
+    long startTime = 0;
+
+    @Override
+    public void simpleUpdate(float tpf) {
+//        if (nbFrames == 4000) {
+//            startTime = System.nanoTime();
+//        }
+//        if (nbFrames > 4000) {
+//            time = System.nanoTime();
+//            float average = ((float) time - (float) startTime) / ((float) nbFrames - 4000f);
+//            helloText.setText("Average = " + average);
+//        }
+//        nbFrames++;
+    }
+
+    class MoveControl extends AbstractControl {
+
+        float direction;
+        Vector3f origPos = new Vector3f();
+
+        public MoveControl(float direction) {
+            this.direction = direction;
+        }
+
+        @Override
+        public void setSpatial(Spatial spatial) {
+            super.setSpatial(spatial); //To change body of generated methods, choose Tools | Templates.
+            origPos.set(spatial.getLocalTranslation());
+        }
+        float time = 0;
+
+        @Override
+        protected void controlUpdate(float tpf) {
+            time += tpf;
+            spatial.setLocalTranslation(origPos.x + FastMath.cos(time) * direction, origPos.y, origPos.z + FastMath.sin(time) * direction);
+        }
+
+        @Override
+        protected void controlRender(RenderManager rm, ViewPort vp) {
+        }
+    }
+}

+ 2 - 2
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java

@@ -802,7 +802,7 @@ public class LwjglGL1Renderer implements GL1Renderer {
         TextureUtil.uploadTexture(img, target, i, 0, tdc);
         }
         } else {*/
-        TextureUtil.uploadTexture(ctxCaps, img, target, 0, 0, false);
+        TextureUtil.uploadTexture(caps, img, target, 0, 0, false);
         //}
 
         img.clearUpdateNeeded();
@@ -853,7 +853,7 @@ public class LwjglGL1Renderer implements GL1Renderer {
 
     public void modifyTexture(Texture tex, Image pixels, int x, int y) {
       setTexture(0, tex);
-      TextureUtil.uploadSubTexture(ctxCaps, pixels, convertTextureType(tex.getType()), 0, x, y, false);
+      TextureUtil.uploadSubTexture(caps, pixels, convertTextureType(tex.getType()), 0, x, y, false);
     }
 
     private void clearTextureUnits() {

+ 194 - 142
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java

@@ -57,7 +57,9 @@ import com.jme3.util.BufferUtils;
 import com.jme3.util.ListMap;
 import com.jme3.util.NativeObjectManager;
 import java.nio.*;
+import java.util.ArrayList;
 import java.util.EnumSet;
+import java.util.HashSet;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -70,7 +72,6 @@ import static org.lwjgl.opengl.ARBInstancedArrays.*;
 import static org.lwjgl.opengl.ARBMultisample.*;
 import static org.lwjgl.opengl.ARBTextureMultisample.*;
 import static org.lwjgl.opengl.ARBVertexArrayObject.*;
-import org.lwjgl.opengl.ContextCapabilities;
 import static org.lwjgl.opengl.EXTFramebufferBlit.*;
 import static org.lwjgl.opengl.EXTFramebufferMultisample.*;
 import static org.lwjgl.opengl.EXTFramebufferObject.*;
@@ -84,7 +85,7 @@ import static org.lwjgl.opengl.GL13.*;
 import static org.lwjgl.opengl.GL14.*;
 import static org.lwjgl.opengl.GL15.*;
 import static org.lwjgl.opengl.GL20.*;
-import org.lwjgl.opengl.GLContext;
+import org.lwjgl.opengl.GL30;
 //import static org.lwjgl.opengl.GL21.*;
 //import static org.lwjgl.opengl.GL30.*;
 
@@ -101,10 +102,7 @@ public class LwjglRenderer implements Renderer {
     private final RenderContext context = new RenderContext();
     private final NativeObjectManager objManager = new NativeObjectManager();
     private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
-    // current state
-    private Shader boundShader;
-    private int initialDrawBuf, initialReadBuf;
-    private int glslVer;
+
     private int vertexTextureUnits;
     private int fragTextureUnits;
     private int vertexUniforms;
@@ -120,13 +118,12 @@ public class LwjglRenderer implements Renderer {
     private int maxTriCount;
     private int maxColorTexSamples;
     private int maxDepthTexSamples;
-    private FrameBuffer lastFb = null;
     private FrameBuffer mainFbOverride = null;
     private final Statistics statistics = new Statistics();
     private int vpX, vpY, vpW, vpH;
     private int clipX, clipY, clipW, clipH;
     private boolean linearizeSrgbImages;
-    private ContextCapabilities ctxCaps;
+    private HashSet<String> extensions;
 
     public LwjglRenderer() {
     }
@@ -153,18 +150,67 @@ public class LwjglRenderer implements Renderer {
         return caps;
     }
 
-    @SuppressWarnings("fallthrough")
-    public void initialize() {
-        ctxCaps = GLContext.getCapabilities();
-        if (ctxCaps.OpenGL20) {
+    private static HashSet<String> loadExtensions(String extensions) {
+        HashSet<String> extensionSet = new HashSet<String>(64);
+        for (String extension : extensions.split(" ")) {
+            extensionSet.add(extension);
+        }
+        return extensionSet;
+    }
+    
+    private static int extractVersion(String prefixStr, String versionStr) {
+        if (versionStr != null) {
+            int spaceIdx = versionStr.indexOf(" ", prefixStr.length());
+            if (spaceIdx >= 1) {
+                versionStr = versionStr.substring(prefixStr.length(), spaceIdx).trim();
+            } else {
+                versionStr = versionStr.substring(prefixStr.length()).trim();
+            }
+            // Some device have ":" at the end of the version.
+            versionStr = versionStr.replaceAll("\\:", "");
+            
+            // Pivot on first point.
+            int firstPoint = versionStr.indexOf(".");
+            
+            // Remove everything after second point.
+            int secondPoint = versionStr.indexOf(".", firstPoint + 1);
+            
+            if (secondPoint != -1) {
+                versionStr = versionStr.substring(0, secondPoint);
+            }
+            
+            String majorVerStr = versionStr.substring(0, firstPoint);
+            String minorVerStr = versionStr.substring(firstPoint + 1);
+            
+            if (minorVerStr.endsWith("0") && minorVerStr.length() > 1) {
+                minorVerStr = minorVerStr.substring(0, minorVerStr.length() - 1);
+            }
+            
+            int majorVer = Integer.parseInt(majorVerStr);
+            int minorVer = Integer.parseInt(minorVerStr);
+            
+            return majorVer * 100 + minorVer * 10;
+        } else {
+            return -1;
+        }
+    }
+    
+    private boolean hasExtension(String extensionName) {
+        return extensions.contains(extensionName);
+    }
+    
+    private void loadCapabilities() {
+        int oglVer = extractVersion("", glGetString(GL_VERSION));
+        
+        if (oglVer >= 200) {
             caps.add(Caps.OpenGL20);
-            if (ctxCaps.OpenGL21) {
+            if (oglVer >= 210) {
                 caps.add(Caps.OpenGL21);
-                if (ctxCaps.OpenGL30) {
+                if (oglVer >= 300) {
                     caps.add(Caps.OpenGL30);
-                    if (ctxCaps.OpenGL31) {
+                    if (oglVer >= 310) {
                         caps.add(Caps.OpenGL31);
-                        if (ctxCaps.OpenGL32) {
+                        if (oglVer >= 320) {
                             caps.add(Caps.OpenGL32);
                         }
                     }
@@ -172,49 +218,15 @@ public class LwjglRenderer implements Renderer {
             }
         }
         
-        //workaround, always assume we support GLSL100
-        //some cards just don't report this correctly
-        caps.add(Caps.GLSL100);
+        int glslVer = extractVersion("", glGetString(GL_SHADING_LANGUAGE_VERSION));
         
-        String versionStr = null;
-        if (ctxCaps.OpenGL20) {
-            versionStr = glGetString(GL_SHADING_LANGUAGE_VERSION);
-        }
-        if (versionStr == null || versionStr.equals("")) {
-            glslVer = -1;
-            throw new UnsupportedOperationException("GLSL and OpenGL2 is "
-                    + "required for the LWJGL "
-                    + "renderer!");
-        }
-
-        // Fix issue in TestRenderToMemory when GL_FRONT is the main
-        // buffer being used.
-        initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
-        initialReadBuf = glGetInteger(GL_READ_BUFFER);
-
-        // XXX: This has to be GL_BACK for canvas on Mac
-        // Since initialDrawBuf is GL_FRONT for pbuffer, gotta
-        // change this value later on ...
-//        initialDrawBuf = GL_BACK;
-//        initialReadBuf = GL_BACK;
-
-        int spaceIdx = versionStr.indexOf(" ");
-        if (spaceIdx >= 1) {
-            versionStr = versionStr.substring(0, spaceIdx);
-        }
-
-        float version = Float.parseFloat(versionStr);
-        glslVer = (int) (version * 100);
-
         switch (glslVer) {
             default:
                 if (glslVer < 400) {
                     break;
                 }
-
-            // so that future OpenGL revisions wont break jme3
-
-            // fall through intentional
+                // so that future OpenGL revisions wont break jme3
+                // fall through intentional
             case 400:
             case 330:
             case 150:
@@ -231,11 +243,28 @@ public class LwjglRenderer implements Renderer {
                 caps.add(Caps.GLSL100);
                 break;
         }
+        
+        // Workaround, always assume we support GLSL100.
+        // Some cards just don't report this correctly.
+        caps.add(Caps.GLSL100);
+        
+        extensions = loadExtensions(glGetString(GL_EXTENSIONS));
+    }
+    
+    @SuppressWarnings("fallthrough")
+    public void initialize() {
+        loadCapabilities();
+        
+        // Fix issue in TestRenderToMemory when GL_FRONT is the main
+        // buffer being used.
+        context.initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
+        context.initialReadBuf = glGetInteger(GL_READ_BUFFER);
 
-        if (!caps.contains(Caps.GLSL100)) {
-            logger.log(Level.WARNING, "Force-adding GLSL100 support, since OpenGL2 is supported.");
-            caps.add(Caps.GLSL100);
-        }
+        // XXX: This has to be GL_BACK for canvas on Mac
+        // Since initialDrawBuf is GL_FRONT for pbuffer, gotta
+        // change this value later on ...
+//        initialDrawBuf = GL_BACK;
+//        initialReadBuf = GL_BACK;
 
         glGetInteger(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16);
         vertexTextureUnits = intBuf16.get(0);
@@ -280,44 +309,41 @@ public class LwjglRenderer implements Renderer {
         maxCubeTexSize = intBuf16.get(0);
         logger.log(Level.FINER, "Maximum CubeMap Resolution: {0}", maxCubeTexSize);
 
-        if (ctxCaps.GL_ARB_color_buffer_float) {
+        // ctxCaps = GLContext.getCapabilities();
+        
+        if (hasExtension("GL_ARB_color_buffer_float") &&
+            hasExtension("GL_ARB_half_float_pixel")) {
             // XXX: Require both 16 and 32 bit float support for FloatColorBuffer.
-            if (ctxCaps.GL_ARB_half_float_pixel) {
-                caps.add(Caps.FloatColorBuffer);
-            }
+            caps.add(Caps.FloatColorBuffer);
         }
 
-        if (ctxCaps.GL_ARB_depth_buffer_float) {
+        if (hasExtension("GL_ARB_depth_buffer_float")) {
             caps.add(Caps.FloatDepthBuffer);
         }
 
-        if (ctxCaps.OpenGL30) {
+        if (caps.contains(Caps.OpenGL30)) {
             caps.add(Caps.PackedDepthStencilBuffer);
         }
 
-        if (ctxCaps.GL_ARB_draw_instanced && ctxCaps.GL_ARB_instanced_arrays) {
+        if (hasExtension("GL_ARB_draw_instanced") && 
+            hasExtension("GL_ARB_instanced_arrays")) {
             caps.add(Caps.MeshInstancing);
         }
 
-        if (ctxCaps.GL_ARB_fragment_program) {
-            caps.add(Caps.ARBprogram);
-        }
-
-        if (ctxCaps.GL_ARB_texture_buffer_object) {
+        if (hasExtension("GL_ARB_texture_buffer_object")) {
             caps.add(Caps.TextureBuffer);
         }
 
-        if (ctxCaps.GL_ARB_texture_float) {
-            if (ctxCaps.GL_ARB_half_float_pixel) {
-                caps.add(Caps.FloatTexture);
-            }
+        if (hasExtension("GL_ARB_texture_float") &&
+            hasExtension("GL_ARB_half_float_pixel")) {
+            caps.add(Caps.FloatTexture);
         }
 
-        if (ctxCaps.GL_ARB_vertex_array_object) {
+        if (hasExtension("GL_ARB_vertex_array_object") || caps.contains(Caps.OpenGL30)) {
             caps.add(Caps.VertexBufferArray);
         }
 
-        if (ctxCaps.GL_ARB_texture_non_power_of_two) {
+        if (hasExtension("GL_ARB_texture_non_power_of_two") || caps.contains(Caps.OpenGL30)) {
             caps.add(Caps.NonPowerOfTwoTextures);
         } else {
             logger.log(Level.WARNING, "Your graphics card does not "
@@ -325,30 +351,34 @@ public class LwjglRenderer implements Renderer {
                     + "Some features might not work.");
         }
 
-        boolean latc = ctxCaps.GL_EXT_texture_compression_latc;
-        if (latc) {
+        if (hasExtension("GL_EXT_texture_compression_latc")) {
             caps.add(Caps.TextureCompressionLATC);
         }
 
-        if (ctxCaps.GL_EXT_packed_float || ctxCaps.OpenGL30) {
+        if (hasExtension("GL_EXT_packed_float") || caps.contains(Caps.OpenGL30)) {
             // This format is part of the OGL3 specification
             caps.add(Caps.PackedFloatColorBuffer);
-            if (ctxCaps.GL_ARB_half_float_pixel) {
+            
+            if (hasExtension("GL_ARB_half_float_pixel")) {
                 // because textures are usually uploaded as RGB16F
                 // need half-float pixel
                 caps.add(Caps.PackedFloatTexture);
             }
         }
 
-        if (ctxCaps.GL_EXT_texture_array || ctxCaps.OpenGL30) {
+        if (hasExtension("GL_EXT_texture_array") || caps.contains(Caps.OpenGL30)) {
             caps.add(Caps.TextureArray);
         }
 
-        if (ctxCaps.GL_EXT_texture_shared_exponent || ctxCaps.OpenGL30) {
+        if (hasExtension("GL_EXT_texture_shared_exponent") || caps.contains(Caps.OpenGL30)) {
             caps.add(Caps.SharedExponentTexture);
         }
+        
+        if (hasExtension("GL_EXT_texture_filter_anisotropic")) {
+            caps.add(Caps.TextureFilterAnisotropic);
+        }
 
-        if (ctxCaps.GL_EXT_framebuffer_object) {
+        if (hasExtension("GL_EXT_framebuffer_object")) {
             caps.add(Caps.FrameBuffer);
 
             glGetInteger(GL_MAX_RENDERBUFFER_SIZE_EXT, intBuf16);
@@ -359,7 +389,7 @@ public class LwjglRenderer implements Renderer {
             maxFBOAttachs = intBuf16.get(0);
             logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs);
 
-            if (ctxCaps.GL_EXT_framebuffer_multisample) {
+            if (hasExtension("GL_EXT_framebuffer_multisample")) {
                 caps.add(Caps.FrameBufferMultisample);
 
                 glGetInteger(GL_MAX_SAMPLES_EXT, intBuf16);
@@ -367,7 +397,7 @@ public class LwjglRenderer implements Renderer {
                 logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples);
             }
 
-            if (ctxCaps.GL_ARB_texture_multisample) {
+            if (hasExtension("GL_ARB_texture_multisample")) {
                 caps.add(Caps.TextureMultisample);
 
                 glGetInteger(GL_MAX_COLOR_TEXTURE_SAMPLES, intBuf16);
@@ -387,7 +417,7 @@ public class LwjglRenderer implements Renderer {
             }
         }
 
-        if (ctxCaps.GL_ARB_multisample) {
+        if (hasExtension("GL_ARB_multisample")) {
             glGetInteger(GL_SAMPLE_BUFFERS_ARB, intBuf16);
             boolean available = intBuf16.get(0) != 0;
             glGetInteger(GL_SAMPLES_ARB, intBuf16);
@@ -401,7 +431,8 @@ public class LwjglRenderer implements Renderer {
         }
         
         // Supports sRGB pipeline.
-        if ( (ctxCaps.GL_ARB_framebuffer_sRGB && ctxCaps.GL_EXT_texture_sRGB ) || ctxCaps.OpenGL30 ) {
+        if ( (hasExtension("GL_ARB_framebuffer_sRGB") && hasExtension("GL_EXT_texture_sRGB")) 
+           || caps.contains(Caps.OpenGL30) ) {
             caps.add(Caps.Srgb);
         }
 
@@ -410,11 +441,8 @@ public class LwjglRenderer implements Renderer {
 
     public void invalidateState() {
         context.reset();
-        boundShader = null;
-        lastFb = null;
-
-        initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
-        initialReadBuf = glGetInteger(GL_READ_BUFFER);
+        context.initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
+        context.initialReadBuf = glGetInteger(GL_READ_BUFFER);
     }
 
     public void resetGLObjects() {
@@ -479,7 +507,7 @@ public class LwjglRenderer implements Renderer {
     }
 
     public void setAlphaToCoverage(boolean value) {
-        if (ctxCaps.GL_ARB_multisample) {
+        if (caps.contains(Caps.Multisample)) {
             if (value) {
                 glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
             } else {
@@ -822,7 +850,7 @@ public class LwjglRenderer implements Renderer {
         if (context.boundShaderProgram != shaderId) {
             glUseProgram(shaderId);
             statistics.onShaderUse(shader, true);
-            boundShader = shader;
+            context.boundShader = shader;
             context.boundShaderProgram = shaderId;
         } else {
             statistics.onShaderUse(shader, false);
@@ -1081,12 +1109,12 @@ public class LwjglRenderer implements Renderer {
             glAttachShader(id, source.getId());
         }
 
-        if (ctxCaps.GL_EXT_gpu_shader4) {
+        if (caps.contains(Caps.OpenGL30)) {
             // Check if GLSL version is 1.5 for shader
-            glBindFragDataLocationEXT(id, 0, "outFragColor");
+            GL30.glBindFragDataLocation(id, 0, "outFragColor");
             // For MRT
             for (int i = 0; i < maxMRTFBOAttachs; i++) {
-                glBindFragDataLocationEXT(id, i, "outFragData[" + i + "]");
+                GL30.glBindFragDataLocation(id, i, "outFragData[" + i + "]");
             }
         }
 
@@ -1191,7 +1219,7 @@ public class LwjglRenderer implements Renderer {
     }
 
     public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
-        if (ctxCaps.GL_EXT_framebuffer_blit) {
+        if (caps.contains(Caps.FrameBufferBlit)) {
             int srcX0 = 0;
             int srcY0 = 0;
             int srcX1;
@@ -1292,11 +1320,11 @@ public class LwjglRenderer implements Renderer {
 
         int attachment = convertAttachmentSlot(rb.getSlot());
 
-        int type = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT,
+        int type = glGetFramebufferAttachmentParameteriEXT(GL_DRAW_FRAMEBUFFER_EXT,
                 attachment,
                 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT);
 
-        int rbName = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT,
+        int rbName = glGetFramebufferAttachmentParameteriEXT(GL_DRAW_FRAMEBUFFER_EXT,
                 attachment,
                 GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT);
 
@@ -1397,9 +1425,9 @@ public class LwjglRenderer implements Renderer {
                     + ":" + fb.getHeight() + " is not supported.");
         }
 
-        TextureUtil.GLImageFormat glFmt = TextureUtil.getImageFormatWithError(ctxCaps, rb.getFormat(), fb.isSrgb());
+        TextureUtil.GLImageFormat glFmt = TextureUtil.getImageFormatWithError(caps, rb.getFormat(), fb.isSrgb());
 
-        if (fb.getSamples() > 1 && ctxCaps.GL_EXT_framebuffer_multisample) {
+        if (fb.getSamples() > 1 && caps.contains(Caps.FrameBufferMultisample)) {
             int samples = fb.getSamples();
             if (maxFBOSamples < samples) {
                 samples = maxFBOSamples;
@@ -1503,7 +1531,7 @@ public class LwjglRenderer implements Renderer {
         if (fb.getSamples() <= 1) {
             throw new IllegalArgumentException("Framebuffer must be multisampled");
         }
-        if (!ctxCaps.GL_ARB_texture_multisample) {
+        if (!caps.contains(Caps.TextureMultisample)) {
             throw new RendererException("Multisampled textures are not supported");
         }
 
@@ -1525,7 +1553,7 @@ public class LwjglRenderer implements Renderer {
     }
 
     public void setFrameBuffer(FrameBuffer fb) {
-        if (!ctxCaps.GL_EXT_framebuffer_object) {
+        if (!caps.contains(Caps.FrameBuffer)) {
             throw new RendererException("Framebuffer objects are not supported" +
                                         " by the video hardware");
         }
@@ -1534,16 +1562,16 @@ public class LwjglRenderer implements Renderer {
             fb = mainFbOverride;
         }
 
-        if (lastFb == fb) {
+        if (context.boundFB == fb) {
             if (fb == null || !fb.isUpdateNeeded()) {
                 return;
             }
         }
 
         // generate mipmaps for last FB if needed
-        if (lastFb != null) {
-            for (int i = 0; i < lastFb.getNumColorBuffers(); i++) {
-                RenderBuffer rb = lastFb.getColorBuffer(i);
+        if (context.boundFB != null) {
+            for (int i = 0; i < context.boundFB.getNumColorBuffers(); i++) {
+                RenderBuffer rb = context.boundFB.getColorBuffer(i);
                 Texture tex = rb.getTexture();
                 if (tex != null
                         && tex.getMinFilter().usesMipMapLevels()) {
@@ -1567,15 +1595,15 @@ public class LwjglRenderer implements Renderer {
             }
             // select back buffer
             if (context.boundDrawBuf != -1) {
-                glDrawBuffer(initialDrawBuf);
+                glDrawBuffer(context.initialDrawBuf);
                 context.boundDrawBuf = -1;
             }
             if (context.boundReadBuf != -1) {
-                glReadBuffer(initialReadBuf);
+                glReadBuffer(context.initialReadBuf);
                 context.boundReadBuf = -1;
             }
 
-            lastFb = null;
+            context.boundFB = null;
         } else {
             if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) {
                 throw new IllegalArgumentException("The framebuffer: " + fb
@@ -1644,7 +1672,7 @@ public class LwjglRenderer implements Renderer {
             assert fb.getId() >= 0;
             assert context.boundFBO == fb.getId();
 
-            lastFb = fb;
+            context.boundFB = fb;
 
             try {
                 checkFrameBufferError();
@@ -1707,7 +1735,7 @@ public class LwjglRenderer implements Renderer {
     |* Textures                                                          *|
     \*********************************************************************/
     private int convertTextureType(Texture.Type type, int samples, int face) {
-        if (samples > 1 && !ctxCaps.GL_ARB_texture_multisample) {
+        if (samples > 1 && !caps.contains(Caps.TextureMultisample)) {
             throw new RendererException("Multisample textures are not supported" + 
                                         " by the video hardware.");
         }
@@ -1751,22 +1779,37 @@ public class LwjglRenderer implements Renderer {
         }
     }
 
-    private int convertMinFilter(Texture.MinFilter filter) {
-        switch (filter) {
-            case Trilinear:
-                return GL_LINEAR_MIPMAP_LINEAR;
-            case BilinearNearestMipMap:
-                return GL_LINEAR_MIPMAP_NEAREST;
-            case NearestLinearMipMap:
-                return GL_NEAREST_MIPMAP_LINEAR;
-            case NearestNearestMipMap:
-                return GL_NEAREST_MIPMAP_NEAREST;
-            case BilinearNoMipMaps:
-                return GL_LINEAR;
-            case NearestNoMipMaps:
-                return GL_NEAREST;
-            default:
-                throw new UnsupportedOperationException("Unknown min filter: " + filter);
+    private int convertMinFilter(Texture.MinFilter filter, boolean haveMips) {
+        if (haveMips){
+            switch (filter) {
+                case Trilinear:
+                    return GL_LINEAR_MIPMAP_LINEAR;
+                case BilinearNearestMipMap:
+                    return GL_LINEAR_MIPMAP_NEAREST;
+                case NearestLinearMipMap:
+                    return GL_NEAREST_MIPMAP_LINEAR;
+                case NearestNearestMipMap:
+                    return GL_NEAREST_MIPMAP_NEAREST;
+                case BilinearNoMipMaps:
+                    return GL_LINEAR;
+                case NearestNoMipMaps:
+                    return GL_NEAREST;
+                default:
+                    throw new UnsupportedOperationException("Unknown min filter: " + filter);
+            }
+        } else {
+            switch (filter) {
+                case Trilinear:
+                case BilinearNearestMipMap:
+                case BilinearNoMipMaps:
+                    return GL_LINEAR;
+                case NearestLinearMipMap:
+                case NearestNearestMipMap:
+                case NearestNoMipMaps:
+                    return GL_NEAREST;
+                default:
+                    throw new UnsupportedOperationException("Unknown min filter: " + filter);
+            }
         }
     }
 
@@ -1792,14 +1835,20 @@ public class LwjglRenderer implements Renderer {
         Image image = tex.getImage();
         int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1);
 
+        boolean haveMips = true;
+        
+        if (image != null) {
+            haveMips = image.isGeneratedMipmapsRequired() || image.hasMipmaps();
+        }
+        
         // filter things
-        int minFilter = convertMinFilter(tex.getMinFilter());
+        int minFilter = convertMinFilter(tex.getMinFilter(), haveMips);
         int magFilter = convertMagFilter(tex.getMagFilter());
         glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter);
         glTexParameteri(target, GL_TEXTURE_MAG_FILTER, magFilter);
 
         if (tex.getAnisotropicFilter() > 1) {
-            if (ctxCaps.GL_EXT_texture_filter_anisotropic) {
+            if (caps.contains(Caps.TextureFilterAnisotropic)) {
                 glTexParameterf(target,
                         GL_TEXTURE_MAX_ANISOTROPY_EXT,
                         tex.getAnisotropicFilter());
@@ -1881,7 +1930,7 @@ public class LwjglRenderer implements Renderer {
             // Image does not have mipmaps, but they are required.
             // Generate from base level.
 
-            if (!ctxCaps.OpenGL30) {
+            if (!caps.contains(Caps.OpenGL30)) {
                 glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE);
                 img.setMipmapsGenerated(true);
             } else {
@@ -1908,7 +1957,7 @@ public class LwjglRenderer implements Renderer {
         }
 
         // Yes, some OpenGL2 cards (GeForce 5) still dont support NPOT.
-        if (!ctxCaps.GL_ARB_texture_non_power_of_two && img.isNPOT()) {
+        if (!caps.contains(Caps.NonPowerOfTwoTextures) && img.isNPOT()) {
             if (img.getData(0) == null) {
                 throw new RendererException("non-power-of-2 framebuffer textures are not supported by the video hardware");
             } else {
@@ -1917,7 +1966,7 @@ public class LwjglRenderer implements Renderer {
         }
 
         // Check if graphics card doesn't support multisample textures
-        if (!ctxCaps.GL_ARB_texture_multisample) {
+        if (!caps.contains(Caps.TextureMultisample)) {
             if (img.getMultiSamples() > 1) {
                 throw new RendererException("Multisample textures not supported by graphics hardware");
             }
@@ -1928,6 +1977,9 @@ public class LwjglRenderer implements Renderer {
             if (img.getWidth() > maxCubeTexSize || img.getHeight() > maxCubeTexSize) {
                 throw new RendererException("Cannot upload cubemap " + img + ". The maximum supported cubemap resolution is " + maxCubeTexSize);
             }
+            if (img.getWidth() != img.getHeight()) {
+                throw new RendererException("Cubemaps must have square dimensions");
+            }
         } else {
             if (img.getWidth() > maxTexSize || img.getHeight() > maxTexSize) {
                 throw new RendererException("Cannot upload texture " + img + ". The maximum supported texture resolution is " + maxTexSize);
@@ -1942,7 +1994,7 @@ public class LwjglRenderer implements Renderer {
                 return;
             }
             for (int i = 0; i < 6; i++) {
-                TextureUtil.uploadTexture(ctxCaps, img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, linearizeSrgbImages);
+                TextureUtil.uploadTexture(caps, img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, linearizeSrgbImages);
             }
         } else if (target == GL_TEXTURE_2D_ARRAY_EXT) {
             if (!caps.contains(Caps.TextureArray)) {
@@ -1952,22 +2004,22 @@ public class LwjglRenderer implements Renderer {
             List<ByteBuffer> data = img.getData();
             
             // -1 index specifies prepare data for 2D Array
-            TextureUtil.uploadTexture(ctxCaps, img, target, -1, 0, linearizeSrgbImages);
+            TextureUtil.uploadTexture(caps, img, target, -1, 0, linearizeSrgbImages);
             
             for (int i = 0; i < data.size(); i++) {
                 // upload each slice of 2D array in turn
                 // this time with the appropriate index
-                TextureUtil.uploadTexture(ctxCaps, img, target, i, 0, linearizeSrgbImages);
+                TextureUtil.uploadTexture(caps, img, target, i, 0, linearizeSrgbImages);
             }
         } else {
-            TextureUtil.uploadTexture(ctxCaps, img, target, 0, 0, linearizeSrgbImages);
+            TextureUtil.uploadTexture(caps, img, target, 0, 0, linearizeSrgbImages);
         }
 
         if (img.getMultiSamples() != imageSamples) {
             img.setMultiSamples(imageSamples);
         }
 
-        if (ctxCaps.OpenGL30) {
+        if (caps.contains(Caps.OpenGL30)) {
             if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired() && img.getData() != null) {
                 // XXX: Required for ATI
                 glEnable(target);
@@ -2018,7 +2070,7 @@ public class LwjglRenderer implements Renderer {
 
     public void modifyTexture(Texture tex, Image pixels, int x, int y) {
       setTexture(0, tex);
-      TextureUtil.uploadSubTexture(ctxCaps, pixels, convertTextureType(tex.getType(), pixels.getMultiSamples(), -1), 0, x, y, linearizeSrgbImages);
+      TextureUtil.uploadSubTexture(caps, pixels, convertTextureType(tex.getType(), pixels.getMultiSamples(), -1), 0, x, y, linearizeSrgbImages);
     }
 
     public void clearTextureUnits() {
@@ -2213,8 +2265,9 @@ public class LwjglRenderer implements Renderer {
         }
 
         int programId = context.boundShaderProgram;
+
         if (programId > 0) {
-            Attribute attrib = boundShader.getAttribute(vb.getBufferType());
+            Attribute attrib = context.boundShader.getAttribute(vb.getBufferType());
             int loc = attrib.getLocation();
             if (loc == -1) {
                 return; // not defined
@@ -2236,8 +2289,7 @@ public class LwjglRenderer implements Renderer {
             }
             
             if (vb.isInstanced()) {
-                if (!ctxCaps.GL_ARB_instanced_arrays
-                 || !ctxCaps.GL_ARB_draw_instanced) {
+                if (!caps.contains(Caps.MeshInstancing)) {
                     throw new RendererException("Instancing is required, "
                             + "but not supported by the "
                             + "graphics hardware");

+ 14 - 13
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/TextureUtil.java

@@ -32,11 +32,13 @@
 
 package com.jme3.renderer.lwjgl;
 
+import com.jme3.renderer.Caps;
 import com.jme3.renderer.RendererException;
 import com.jme3.texture.Image;
 import com.jme3.texture.Image.Format;
 import com.jme3.texture.image.ColorSpace;
 import java.nio.ByteBuffer;
+import java.util.EnumSet;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import static org.lwjgl.opengl.ARBDepthBufferFloat.*;
@@ -55,7 +57,6 @@ import static org.lwjgl.opengl.GL11.*;
 import static org.lwjgl.opengl.GL12.*;
 import static org.lwjgl.opengl.GL13.*;
 import static org.lwjgl.opengl.GL14.*;
-import static org.lwjgl.opengl.GL20.*;
 
 class TextureUtil {
 
@@ -140,7 +141,7 @@ class TextureUtil {
     
         // LTC/LATC/3Dc formats
         setFormat(Format.LTC,  GL_COMPRESSED_LUMINANCE_LATC1_EXT,       GL_LUMINANCE,       GL_UNSIGNED_BYTE, true);
-        setFormat(Format.LATC, GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, true);   
+        setFormat(Format.LATC, GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, true);
     }
     
     //sRGB formats        
@@ -157,18 +158,18 @@ class TextureUtil {
     private static final GLImageFormat sRGB_DXT3 = new GLImageFormat(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_BYTE, true);
     private static final GLImageFormat sRGB_DXT5 = new GLImageFormat(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_BYTE, true);
 
-    public static GLImageFormat getImageFormat(ContextCapabilities caps, Format fmt, boolean isSrgb){
+    public static GLImageFormat getImageFormat(EnumSet<Caps> caps, Format fmt, boolean isSrgb){
         switch (fmt){
             case DXT1:
             case DXT1A:
             case DXT3:
             case DXT5:
-                if (!caps.GL_EXT_texture_compression_s3tc) {
+                if (!caps.contains(Caps.TextureCompressionS3TC)) {
                     return null;
                 }
                 break;
             case Depth24Stencil8:
-                if (!caps.OpenGL30 && !caps.GL_EXT_packed_depth_stencil){
+                if (!caps.contains(Caps.PackedDepthStencilBuffer)){
                     return null;
                 }
                 break;
@@ -179,30 +180,30 @@ class TextureUtil {
             case RGB32F:
             case RGBA16F:
             case RGBA32F:
-                if (!caps.OpenGL30 && !caps.GL_ARB_texture_float){
+                if (!caps.contains(Caps.FloatTexture)){
                     return null;
                 }
                 break;
             case Depth32F:
-                if (!caps.OpenGL30 && !caps.GL_NV_depth_buffer_float){
+                if (!caps.contains(Caps.FloatDepthBuffer)){
                     return null;
                 }
                 break;
             case LATC:
             case LTC:
-                if (!caps.GL_EXT_texture_compression_latc){
+                if (!caps.contains(Caps.TextureCompressionLATC)){
                     return null;
                 }
                 break;
             case RGB9E5:
             case RGB16F_to_RGB9E5:
-                if (!caps.OpenGL30 && !caps.GL_EXT_texture_shared_exponent){
+                if (!caps.contains(Caps.SharedExponentTexture)){
                     return null;
                 }
                 break;
             case RGB111110F:
             case RGB16F_to_RGB111110F:
-                if (!caps.OpenGL30 && !caps.GL_EXT_packed_float){
+                if (!caps.contains(Caps.PackedFloatTexture)){
                     return null;
                 }
                 break;
@@ -214,7 +215,7 @@ class TextureUtil {
         }
     }
     
-    public static GLImageFormat getImageFormatWithError(ContextCapabilities caps, Format fmt, boolean isSrgb) {
+    public static GLImageFormat getImageFormatWithError(EnumSet<Caps> caps, Format fmt, boolean isSrgb) {
         GLImageFormat glFmt = getImageFormat(caps, fmt, isSrgb);
         if (glFmt == null) {
             throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware.");
@@ -254,7 +255,7 @@ class TextureUtil {
         }
     }
     
-    public static void uploadTexture(ContextCapabilities caps,
+    public static void uploadTexture(EnumSet<Caps> caps,
                                      Image image,
                                      int target,
                                      int index,
@@ -412,7 +413,7 @@ class TextureUtil {
      * @param y the y position where to put the image in the texture
      */
     public static void uploadSubTexture(
-        ContextCapabilities caps,
+        EnumSet<Caps> caps,
         Image image,
         int target,
         int index,

+ 615 - 0
jme3-terrain/src/main/resources/Common/MatDefs/Terrain/SPTerrainLighting.frag

@@ -0,0 +1,615 @@
+#import "Common/ShaderLib/PhongLighting.glsllib"
+#import "Common/ShaderLib/Lighting.glsllib"
+
+uniform float m_Shininess;
+
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+uniform mat4 g_ViewMatrix;
+uniform vec4 g_LightData[NB_LIGHTS];
+varying vec3 vTangent;
+varying vec3 vBinormal;
+varying vec3 vPos;    
+varying vec3 vNormal;
+varying vec2 texCoord;
+
+
+#ifdef DIFFUSEMAP
+  uniform sampler2D m_DiffuseMap;
+#endif
+#ifdef DIFFUSEMAP_1
+  uniform sampler2D m_DiffuseMap_1;
+#endif
+#ifdef DIFFUSEMAP_2
+  uniform sampler2D m_DiffuseMap_2;
+#endif
+#ifdef DIFFUSEMAP_3
+  uniform sampler2D m_DiffuseMap_3;
+#endif
+#ifdef DIFFUSEMAP_4
+  uniform sampler2D m_DiffuseMap_4;
+#endif
+#ifdef DIFFUSEMAP_5
+  uniform sampler2D m_DiffuseMap_5;
+#endif
+#ifdef DIFFUSEMAP_6
+  uniform sampler2D m_DiffuseMap_6;
+#endif
+#ifdef DIFFUSEMAP_7
+  uniform sampler2D m_DiffuseMap_7;
+#endif
+#ifdef DIFFUSEMAP_8
+  uniform sampler2D m_DiffuseMap_8;
+#endif
+#ifdef DIFFUSEMAP_9
+  uniform sampler2D m_DiffuseMap_9;
+#endif
+#ifdef DIFFUSEMAP_10
+  uniform sampler2D m_DiffuseMap_10;
+#endif
+#ifdef DIFFUSEMAP_11
+  uniform sampler2D m_DiffuseMap_11;
+#endif
+
+
+#ifdef DIFFUSEMAP_0_SCALE
+  uniform float m_DiffuseMap_0_scale;
+#endif
+#ifdef DIFFUSEMAP_1_SCALE
+  uniform float m_DiffuseMap_1_scale;
+#endif
+#ifdef DIFFUSEMAP_2_SCALE
+  uniform float m_DiffuseMap_2_scale;
+#endif
+#ifdef DIFFUSEMAP_3_SCALE
+  uniform float m_DiffuseMap_3_scale;
+#endif
+#ifdef DIFFUSEMAP_4_SCALE
+  uniform float m_DiffuseMap_4_scale;
+#endif
+#ifdef DIFFUSEMAP_5_SCALE
+  uniform float m_DiffuseMap_5_scale;
+#endif
+#ifdef DIFFUSEMAP_6_SCALE
+  uniform float m_DiffuseMap_6_scale;
+#endif
+#ifdef DIFFUSEMAP_7_SCALE
+  uniform float m_DiffuseMap_7_scale;
+#endif
+#ifdef DIFFUSEMAP_8_SCALE
+  uniform float m_DiffuseMap_8_scale;
+#endif
+#ifdef DIFFUSEMAP_9_SCALE
+  uniform float m_DiffuseMap_9_scale;
+#endif
+#ifdef DIFFUSEMAP_10_SCALE
+  uniform float m_DiffuseMap_10_scale;
+#endif
+#ifdef DIFFUSEMAP_11_SCALE
+  uniform float m_DiffuseMap_11_scale;
+#endif
+
+
+#ifdef ALPHAMAP
+  uniform sampler2D m_AlphaMap;
+#endif
+#ifdef ALPHAMAP_1
+  uniform sampler2D m_AlphaMap_1;
+#endif
+#ifdef ALPHAMAP_2
+  uniform sampler2D m_AlphaMap_2;
+#endif
+
+#ifdef NORMALMAP
+  uniform sampler2D m_NormalMap;
+#endif
+#ifdef NORMALMAP_1
+  uniform sampler2D m_NormalMap_1;
+#endif
+#ifdef NORMALMAP_2
+  uniform sampler2D m_NormalMap_2;
+#endif
+#ifdef NORMALMAP_3
+  uniform sampler2D m_NormalMap_3;
+#endif
+#ifdef NORMALMAP_4
+  uniform sampler2D m_NormalMap_4;
+#endif
+#ifdef NORMALMAP_5
+  uniform sampler2D m_NormalMap_5;
+#endif
+#ifdef NORMALMAP_6
+  uniform sampler2D m_NormalMap_6;
+#endif
+#ifdef NORMALMAP_7
+  uniform sampler2D m_NormalMap_7;
+#endif
+#ifdef NORMALMAP_8
+  uniform sampler2D m_NormalMap_8;
+#endif
+#ifdef NORMALMAP_9
+  uniform sampler2D m_NormalMap_9;
+#endif
+#ifdef NORMALMAP_10
+  uniform sampler2D m_NormalMap_10;
+#endif
+#ifdef NORMALMAP_11
+  uniform sampler2D m_NormalMap_11;
+#endif
+
+
+#ifdef TRI_PLANAR_MAPPING
+  varying vec4 wVertex;
+  varying vec3 wNormal;
+#endif
+
+
+#ifdef ALPHAMAP
+
+  vec4 calculateDiffuseBlend(in vec2 texCoord) {
+    vec4 alphaBlend   = texture2D( m_AlphaMap, texCoord.xy );
+    
+    #ifdef ALPHAMAP_1
+      vec4 alphaBlend1   = texture2D( m_AlphaMap_1, texCoord.xy );
+    #endif
+    #ifdef ALPHAMAP_2
+      vec4 alphaBlend2   = texture2D( m_AlphaMap_2, texCoord.xy );
+    #endif
+
+    vec4 diffuseColor = texture2D(m_DiffuseMap, texCoord * m_DiffuseMap_0_scale);
+    diffuseColor *= alphaBlend.r;
+    #ifdef DIFFUSEMAP_1
+        vec4 diffuseColor1 = texture2D(m_DiffuseMap_1, texCoord * m_DiffuseMap_1_scale);
+        diffuseColor = mix( diffuseColor, diffuseColor1, alphaBlend.g );
+    #endif
+    #ifdef DIFFUSEMAP_2
+        vec4 diffuseColor2 = texture2D(m_DiffuseMap_2, texCoord * m_DiffuseMap_2_scale);
+        diffuseColor = mix( diffuseColor, diffuseColor2, alphaBlend.b );
+    #endif
+    #ifdef DIFFUSEMAP_3
+        vec4 diffuseColor3 = texture2D(m_DiffuseMap_3, texCoord * m_DiffuseMap_3_scale);
+        diffuseColor = mix( diffuseColor, diffuseColor3, alphaBlend.a );
+    #endif
+
+    #ifdef ALPHAMAP_1
+        #ifdef DIFFUSEMAP_4
+            vec4 diffuseColor4 = texture2D(m_DiffuseMap_4, texCoord * m_DiffuseMap_4_scale);
+            diffuseColor = mix( diffuseColor, diffuseColor4, alphaBlend1.r );
+        #endif
+        #ifdef DIFFUSEMAP_5
+            vec4 diffuseColor5 = texture2D(m_DiffuseMap_5, texCoord * m_DiffuseMap_5_scale);
+            diffuseColor = mix( diffuseColor, diffuseColor5, alphaBlend1.g );
+        #endif
+        #ifdef DIFFUSEMAP_6
+            vec4 diffuseColor6 = texture2D(m_DiffuseMap_6, texCoord * m_DiffuseMap_6_scale);
+            diffuseColor = mix( diffuseColor, diffuseColor6, alphaBlend1.b );
+        #endif
+        #ifdef DIFFUSEMAP_7
+            vec4 diffuseColor7 = texture2D(m_DiffuseMap_7, texCoord * m_DiffuseMap_7_scale);
+            diffuseColor = mix( diffuseColor, diffuseColor7, alphaBlend1.a );
+        #endif
+    #endif
+
+    #ifdef ALPHAMAP_2
+        #ifdef DIFFUSEMAP_8
+            vec4 diffuseColor8 = texture2D(m_DiffuseMap_8, texCoord * m_DiffuseMap_8_scale);
+            diffuseColor = mix( diffuseColor, diffuseColor8, alphaBlend2.r );
+        #endif
+        #ifdef DIFFUSEMAP_9
+            vec4 diffuseColor9 = texture2D(m_DiffuseMap_9, texCoord * m_DiffuseMap_9_scale);
+            diffuseColor = mix( diffuseColor, diffuseColor9, alphaBlend2.g );
+        #endif
+        #ifdef DIFFUSEMAP_10
+            vec4 diffuseColor10 = texture2D(m_DiffuseMap_10, texCoord * m_DiffuseMap_10_scale);
+            diffuseColor = mix( diffuseColor, diffuseColor10, alphaBlend2.b );
+        #endif
+        #ifdef DIFFUSEMAP_11
+            vec4 diffuseColor11 = texture2D(m_DiffuseMap_11, texCoord * m_DiffuseMap_11_scale);
+            diffuseColor = mix( diffuseColor, diffuseColor11, alphaBlend2.a );
+        #endif                   
+    #endif
+
+    return diffuseColor;
+  }
+
+  vec3 calculateNormal(in vec2 texCoord) {
+    vec3 normal = vec3(0,0,1);
+    vec3 n = vec3(0,0,0);
+
+    vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );
+
+    #ifdef ALPHAMAP_1
+      vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );
+    #endif
+    #ifdef ALPHAMAP_2
+      vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );
+    #endif
+
+    #ifdef NORMALMAP
+      n = texture2D(m_NormalMap, texCoord * m_DiffuseMap_0_scale).xyz;
+      normal += n * alphaBlend.r;
+    #else
+      normal += vec3(0.5,0.5,1) * alphaBlend.r;
+    #endif
+
+    #ifdef NORMALMAP_1
+      n = texture2D(m_NormalMap_1, texCoord * m_DiffuseMap_1_scale).xyz;
+      normal += n * alphaBlend.g;
+    #else
+      normal += vec3(0.5,0.5,1) * alphaBlend.g;
+    #endif
+
+    #ifdef NORMALMAP_2
+      n = texture2D(m_NormalMap_2, texCoord * m_DiffuseMap_2_scale).xyz;
+      normal += n * alphaBlend.b;
+    #else
+      normal += vec3(0.5,0.5,1) * alphaBlend.b;
+    #endif
+
+    #ifdef NORMALMAP_3
+      n = texture2D(m_NormalMap_3, texCoord * m_DiffuseMap_3_scale).xyz;
+      normal += n * alphaBlend.a;
+    #else
+      normal += vec3(0.5,0.5,1) * alphaBlend.a;
+    #endif
+
+    #ifdef ALPHAMAP_1
+        #ifdef NORMALMAP_4
+          n = texture2D(m_NormalMap_4, texCoord * m_DiffuseMap_4_scale).xyz;
+          normal += n * alphaBlend1.r;
+        #endif
+
+        #ifdef NORMALMAP_5
+          n = texture2D(m_NormalMap_5, texCoord * m_DiffuseMap_5_scale).xyz;
+          normal += n * alphaBlend1.g;
+        #endif
+
+        #ifdef NORMALMAP_6
+          n = texture2D(m_NormalMap_6, texCoord * m_DiffuseMap_6_scale).xyz;
+          normal += n * alphaBlend1.b;
+        #endif
+
+        #ifdef NORMALMAP_7
+          n = texture2D(m_NormalMap_7, texCoord * m_DiffuseMap_7_scale).xyz;
+          normal += n * alphaBlend1.a;
+        #endif
+    #endif
+
+    #ifdef ALPHAMAP_2
+        #ifdef NORMALMAP_8
+          n = texture2D(m_NormalMap_8, texCoord * m_DiffuseMap_8_scale).xyz;
+          normal += n * alphaBlend2.r;
+        #endif
+
+        #ifdef NORMALMAP_9
+          n = texture2D(m_NormalMap_9, texCoord * m_DiffuseMap_9_scale);
+          normal += n * alphaBlend2.g;
+        #endif
+
+        #ifdef NORMALMAP_10
+          n = texture2D(m_NormalMap_10, texCoord * m_DiffuseMap_10_scale);
+          normal += n * alphaBlend2.b;
+        #endif
+
+        #ifdef NORMALMAP_11
+          n = texture2D(m_NormalMap_11, texCoord * m_DiffuseMap_11_scale);
+          normal += n * alphaBlend2.a;
+        #endif
+    #endif
+
+    normal = (normal.xyz * vec3(2.0) - vec3(1.0));
+    return normalize(normal);
+  }
+
+  #ifdef TRI_PLANAR_MAPPING
+
+    vec4 getTriPlanarBlend(in vec4 coords, in vec3 blending, in sampler2D map, in float scale) {
+      vec4 col1 = texture2D( map, coords.yz * scale);
+      vec4 col2 = texture2D( map, coords.xz * scale);
+      vec4 col3 = texture2D( map, coords.xy * scale);
+      // blend the results of the 3 planar projections.
+      vec4 tex = col1 * blending.x + col2 * blending.y + col3 * blending.z;
+      return tex;
+    }
+
+    vec4 calculateTriPlanarDiffuseBlend(in vec3 wNorm, in vec4 wVert, in vec2 texCoord) {
+        // tri-planar texture bending factor for this fragment's normal
+        vec3 blending = abs( wNorm );
+        blending = (blending -0.2) * 0.7;
+        blending = normalize(max(blending, 0.00001));      // Force weights to sum to 1.0 (very important!)
+        float b = (blending.x + blending.y + blending.z);
+        blending /= vec3(b, b, b);
+
+        // texture coords
+        vec4 coords = wVert;
+
+        // blend the results of the 3 planar projections.
+        vec4 tex0 = getTriPlanarBlend(coords, blending, m_DiffuseMap, m_DiffuseMap_0_scale);
+
+        #ifdef DIFFUSEMAP_1
+          // blend the results of the 3 planar projections.
+          vec4 tex1 = getTriPlanarBlend(coords, blending, m_DiffuseMap_1, m_DiffuseMap_1_scale);
+        #endif
+        #ifdef DIFFUSEMAP_2
+          // blend the results of the 3 planar projections.
+          vec4 tex2 = getTriPlanarBlend(coords, blending, m_DiffuseMap_2, m_DiffuseMap_2_scale);
+        #endif
+        #ifdef DIFFUSEMAP_3
+          // blend the results of the 3 planar projections.
+          vec4 tex3 = getTriPlanarBlend(coords, blending, m_DiffuseMap_3, m_DiffuseMap_3_scale);
+        #endif
+        #ifdef DIFFUSEMAP_4
+          // blend the results of the 3 planar projections.
+          vec4 tex4 = getTriPlanarBlend(coords, blending, m_DiffuseMap_4, m_DiffuseMap_4_scale);
+        #endif
+        #ifdef DIFFUSEMAP_5
+          // blend the results of the 3 planar projections.
+          vec4 tex5 = getTriPlanarBlend(coords, blending, m_DiffuseMap_5, m_DiffuseMap_5_scale);
+        #endif
+        #ifdef DIFFUSEMAP_6
+          // blend the results of the 3 planar projections.
+          vec4 tex6 = getTriPlanarBlend(coords, blending, m_DiffuseMap_6, m_DiffuseMap_6_scale);
+        #endif
+        #ifdef DIFFUSEMAP_7
+          // blend the results of the 3 planar projections.
+          vec4 tex7 = getTriPlanarBlend(coords, blending, m_DiffuseMap_7, m_DiffuseMap_7_scale);
+        #endif
+        #ifdef DIFFUSEMAP_8
+          // blend the results of the 3 planar projections.
+          vec4 tex8 = getTriPlanarBlend(coords, blending, m_DiffuseMap_8, m_DiffuseMap_8_scale);
+        #endif
+        #ifdef DIFFUSEMAP_9
+          // blend the results of the 3 planar projections.
+          vec4 tex9 = getTriPlanarBlend(coords, blending, m_DiffuseMap_9, m_DiffuseMap_9_scale);
+        #endif
+        #ifdef DIFFUSEMAP_10
+          // blend the results of the 3 planar projections.
+          vec4 tex10 = getTriPlanarBlend(coords, blending, m_DiffuseMap_10, m_DiffuseMap_10_scale);
+        #endif
+        #ifdef DIFFUSEMAP_11
+          // blend the results of the 3 planar projections.
+          vec4 tex11 = getTriPlanarBlend(coords, blending, m_DiffuseMap_11, m_DiffuseMap_11_scale);
+        #endif
+
+        vec4 alphaBlend   = texture2D( m_AlphaMap, texCoord.xy );
+
+        #ifdef ALPHAMAP_1
+          vec4 alphaBlend1   = texture2D( m_AlphaMap_1, texCoord.xy );
+        #endif
+        #ifdef ALPHAMAP_2
+          vec4 alphaBlend2   = texture2D( m_AlphaMap_2, texCoord.xy );
+        #endif
+
+        vec4 diffuseColor = tex0 * alphaBlend.r;
+        #ifdef DIFFUSEMAP_1
+            diffuseColor = mix( diffuseColor, tex1, alphaBlend.g );
+        #endif
+        #ifdef DIFFUSEMAP_2
+            diffuseColor = mix( diffuseColor, tex2, alphaBlend.b );
+        #endif
+        #ifdef DIFFUSEMAP_3
+            diffuseColor = mix( diffuseColor, tex3, alphaBlend.a );
+        #endif
+        #ifdef ALPHAMAP_1
+            #ifdef DIFFUSEMAP_4
+                diffuseColor = mix( diffuseColor, tex4, alphaBlend1.r );
+            #endif
+            #ifdef DIFFUSEMAP_5
+                diffuseColor = mix( diffuseColor, tex5, alphaBlend1.g );
+            #endif
+            #ifdef DIFFUSEMAP_6
+                diffuseColor = mix( diffuseColor, tex6, alphaBlend1.b );
+            #endif
+            #ifdef DIFFUSEMAP_7
+                diffuseColor = mix( diffuseColor, tex7, alphaBlend1.a );
+            #endif
+        #endif
+        #ifdef ALPHAMAP_2
+            #ifdef DIFFUSEMAP_8
+                diffuseColor = mix( diffuseColor, tex8, alphaBlend2.r );
+            #endif
+            #ifdef DIFFUSEMAP_9
+                diffuseColor = mix( diffuseColor, tex9, alphaBlend2.g );
+            #endif
+            #ifdef DIFFUSEMAP_10
+                diffuseColor = mix( diffuseColor, tex10, alphaBlend2.b );
+            #endif
+            #ifdef DIFFUSEMAP_11
+                diffuseColor = mix( diffuseColor, tex11, alphaBlend2.a );
+            #endif
+        #endif
+
+        return diffuseColor;
+    }
+
+    vec3 calculateNormalTriPlanar(in vec3 wNorm, in vec4 wVert,in vec2 texCoord) {
+      // tri-planar texture bending factor for this fragment's world-space normal
+      vec3 blending = abs( wNorm );
+      blending = (blending -0.2) * 0.7;
+      blending = normalize(max(blending, 0.00001));      // Force weights to sum to 1.0 (very important!)
+      float b = (blending.x + blending.y + blending.z);
+      blending /= vec3(b, b, b);
+
+      // texture coords
+      vec4 coords = wVert;
+      vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );
+
+      #ifdef ALPHAMAP_1
+        vec4 alphaBlend1 = texture2D( m_AlphaMap_1, texCoord.xy );
+      #endif
+      #ifdef ALPHAMAP_2
+        vec4 alphaBlend2 = texture2D( m_AlphaMap_2, texCoord.xy );
+      #endif
+
+      vec3 normal = vec3(0,0,1);
+      vec3 n = vec3(0,0,0);
+
+      #ifdef NORMALMAP
+          n = getTriPlanarBlend(coords, blending, m_NormalMap, m_DiffuseMap_0_scale).xyz;
+          normal += n * alphaBlend.r;
+      #else
+          normal += vec3(0.5,0.5,1) * alphaBlend.r;
+      #endif
+
+      #ifdef NORMALMAP_1
+          n = getTriPlanarBlend(coords, blending, m_NormalMap_1, m_DiffuseMap_1_scale).xyz;
+          normal += n * alphaBlend.g;
+      #else
+          normal += vec3(0.5,0.5,1) * alphaBlend.g;
+      #endif
+
+      #ifdef NORMALMAP_2
+          n = getTriPlanarBlend(coords, blending, m_NormalMap_2, m_DiffuseMap_2_scale).xyz;
+          normal += n * alphaBlend.b;
+      #else
+          normal += vec3(0.5,0.5,1) * alphaBlend.b;
+      #endif
+
+      #ifdef NORMALMAP_3
+          n = getTriPlanarBlend(coords, blending, m_NormalMap_3, m_DiffuseMap_3_scale).xyz;
+          normal += n * alphaBlend.a;
+      #else
+          normal += vec3(0.5,0.5,1) * alphaBlend.a;
+      #endif
+
+      #ifdef ALPHAMAP_1
+          #ifdef NORMALMAP_4
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_4, m_DiffuseMap_4_scale).xyz;
+              normal += n * alphaBlend1.r;
+          #else
+              normal += vec3(0.5,0.5,1) * alphaBlend.r;
+          #endif
+
+          #ifdef NORMALMAP_5
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_5, m_DiffuseMap_5_scale).xyz;
+              normal += n * alphaBlend1.g;
+          #else
+              normal += vec3(0.5,0.5,1) * alphaBlend.g;
+          #endif
+
+          #ifdef NORMALMAP_6
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_6, m_DiffuseMap_6_scale).xyz;
+              normal += n * alphaBlend1.b;
+          #else
+              normal += vec3(0.5,0.5,1) * alphaBlend.b;
+          #endif
+
+          #ifdef NORMALMAP_7
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_7, m_DiffuseMap_7_scale).xyz;
+              normal += n * alphaBlend1.a;
+          #else
+              normal += vec3(0.5,0.5,1) * alphaBlend.a;
+          #endif
+      #endif
+
+      #ifdef ALPHAMAP_2
+          #ifdef NORMALMAP_8
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_8, m_DiffuseMap_8_scale).xyz;
+              normal += n * alphaBlend2.r;
+          #else
+              normal += vec3(0.5,0.5,1) * alphaBlend.r;
+          #endif
+
+          #ifdef NORMALMAP_9
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_9, m_DiffuseMap_9_scale).xyz;
+              normal += n * alphaBlend2.g;
+          #else
+              normal += vec3(0.5,0.5,1) * alphaBlend.g;
+          #endif
+
+          #ifdef NORMALMAP_10
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_10, m_DiffuseMap_10_scale).xyz;
+              normal += n * alphaBlend2.b;
+          #else
+              normal += vec3(0.5,0.5,1) * alphaBlend.b;
+          #endif
+
+          #ifdef NORMALMAP_11
+              n = getTriPlanarBlend(coords, blending, m_NormalMap_11, m_DiffuseMap_11_scale).xyz;
+              normal += n * alphaBlend2.a;
+          #else
+              normal += vec3(0.5,0.5,1) * alphaBlend.a;
+          #endif
+      #endif
+
+      normal = (normal.xyz * vec3(2.0) - vec3(1.0));
+      return normalize(normal);
+    }
+  #endif
+
+#endif
+ 
+void main(){
+
+    //----------------------
+    // diffuse calculations
+    //----------------------
+    #ifdef DIFFUSEMAP
+      #ifdef ALPHAMAP
+        #ifdef TRI_PLANAR_MAPPING
+            vec4 diffuseColor = calculateTriPlanarDiffuseBlend(wNormal, wVertex, texCoord);
+        #else
+            vec4 diffuseColor = calculateDiffuseBlend(texCoord);
+        #endif
+      #else
+        vec4 diffuseColor = texture2D(m_DiffuseMap, texCoord);
+      #endif
+    #else
+      vec4 diffuseColor = vec4(1.0);
+    #endif
+
+    
+    //---------------------
+    // normal calculations
+    //---------------------
+    #if defined(NORMALMAP) || defined(NORMALMAP_1) || defined(NORMALMAP_2) || defined(NORMALMAP_3) || defined(NORMALMAP_4) || defined(NORMALMAP_5) || defined(NORMALMAP_6) || defined(NORMALMAP_7) || defined(NORMALMAP_8) || defined(NORMALMAP_9) || defined(NORMALMAP_10) || defined(NORMALMAP_11)
+      #ifdef TRI_PLANAR_MAPPING
+        vec3 normal = calculateNormalTriPlanar(wNormal, wVertex, texCoord);
+      #else
+        vec3 normal = calculateNormal(texCoord);
+      #endif
+      mat3 tbnMat = mat3(normalize(vTangent.xyz) , normalize(vBinormal.xyz) , normalize(vNormal.xyz));
+    #else
+      vec3 normal = vNormal;
+    #endif
+
+
+    //-----------------------
+    // lighting calculations
+    //-----------------------
+    gl_FragColor = AmbientSum * diffuseColor;
+    for( int i = 0;i < NB_LIGHTS; i+=3){
+        vec4 lightColor = g_LightData[i];
+        vec4 lightData1 = g_LightData[i+1];                
+        vec4 lightDir;
+        vec3 lightVec;            
+        lightComputeDir(vPos, lightColor.w, lightData1, lightDir, lightVec);
+
+        float spotFallOff = 1.0;
+        #if __VERSION__ >= 110
+            // allow use of control flow
+        if(lightColor.w > 1.0){
+        #endif
+            spotFallOff = computeSpotFalloff(g_LightData[i+2], lightVec);
+        #if __VERSION__ >= 110
+        }
+        #endif
+
+        #ifdef NORMALMAP         
+            //Normal map -> lighting is computed in tangent space
+           lightDir.xyz = normalize(lightDir.xyz * tbnMat);
+           vec3 viewDir = normalize(-vPos.xyz * tbnMat);
+        #else
+            //no Normal map -> lighting is computed in view space
+            lightDir.xyz = normalize(lightDir.xyz);
+            vec3 viewDir = normalize(-vPos.xyz);
+        #endif
+
+        vec2 light = computeLighting(normal, viewDir, lightDir.xyz, lightDir.w  * spotFallOff, m_Shininess);       
+        gl_FragColor.rgb += DiffuseSum.rgb * lightColor.rgb * diffuseColor.rgb  * vec3(light.x) +
+                            SpecularSum.rgb * vec3(light.y);
+    }
+
+}

+ 66 - 0
jme3-terrain/src/main/resources/Common/MatDefs/Terrain/SPTerrainLighting.vert

@@ -0,0 +1,66 @@
+uniform mat4 g_WorldViewProjectionMatrix;
+uniform mat4 g_WorldViewMatrix;
+uniform mat3 g_NormalMatrix;
+uniform mat4 g_ViewMatrix;
+
+uniform vec4 g_AmbientLightColor;
+
+attribute vec3 inPosition;
+attribute vec3 inNormal;
+attribute vec2 inTexCoord;
+attribute vec4 inTangent;
+
+varying vec3 vNormal;
+varying vec2 texCoord;
+varying vec3 vPos;
+varying vec3 vTangent;
+varying vec3 vBinormal;
+
+varying vec4 AmbientSum;
+varying vec4 DiffuseSum;
+varying vec4 SpecularSum;
+
+#ifdef TRI_PLANAR_MAPPING
+  varying vec4 wVertex;
+  varying vec3 wNormal;
+#endif
+
+
+
+void main(){
+    vec4 pos = vec4(inPosition, 1.0);
+    gl_Position = g_WorldViewProjectionMatrix * pos;
+    #ifdef TERRAIN_GRID
+    texCoord = inTexCoord * 2.0;
+    #else
+    texCoord = inTexCoord;
+    #endif
+
+    vec3 wvPosition = (g_WorldViewMatrix * pos).xyz;
+    vec3 wvNormal  = normalize(g_NormalMatrix * inNormal);
+
+    //--------------------------
+    // specific to normal maps:
+    //--------------------------
+    #if defined(NORMALMAP) || defined(NORMALMAP_1) || defined(NORMALMAP_2) || defined(NORMALMAP_3) || defined(NORMALMAP_4) || defined(NORMALMAP_5) || defined(NORMALMAP_6) || defined(NORMALMAP_7) || defined(NORMALMAP_8) || defined(NORMALMAP_9) || defined(NORMALMAP_10) || defined(NORMALMAP_11)
+      vTangent = g_NormalMatrix * inTangent.xyz;
+      vBinormal = cross(wvNormal, vTangent)* inTangent.w;      
+    #endif 
+
+    //-------------------------
+    // general to all lighting
+    //-------------------------
+    vNormal = wvNormal;
+    vPos = wvPosition; 
+
+    AmbientSum  = g_AmbientLightColor; 
+    DiffuseSum  = vec4(1.0);
+    SpecularSum = vec4(0.0);
+
+
+#ifdef TRI_PLANAR_MAPPING
+    wVertex = vec4(inPosition,0.0);
+    wNormal = inNormal;
+#endif
+
+}

+ 5 - 51
jme3-terrain/src/main/resources/Common/MatDefs/Terrain/TerrainLighting.frag

@@ -1,3 +1,5 @@
+#import "Common/ShaderLib/PhongLighting.glsllib"
+#import "Common/ShaderLib/Lighting.glsllib"
 
 uniform float m_Shininess;
 uniform vec4 g_LightDirection;
@@ -145,54 +147,6 @@ varying vec3 lightVec;
   varying vec3 wNormal;
 #endif
 
-
-
-float tangDot(in vec3 v1, in vec3 v2){
-    float d = dot(v1,v2);
-    #ifdef V_TANGENT
-        d = 1.0 - d*d;
-        return step(0.0, d) * sqrt(d);
-    #else
-        return d;
-    #endif
-}
-
-
-float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
-    return max(0.0, dot(norm, lightdir));
-}
-
-float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
-    #ifdef WARDISO
-        // Isotropic Ward
-        vec3 halfVec = normalize(viewdir + lightdir);
-        float NdotH  = max(0.001, tangDot(norm, halfVec));
-        float NdotV  = max(0.001, tangDot(norm, viewdir));
-        float NdotL  = max(0.001, tangDot(norm, lightdir));
-        float a      = tan(acos(NdotH));
-        float p      = max(shiny/128.0, 0.001);
-        return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL)));
-    #else
-       // Standard Phong
-       vec3 R = reflect(-lightdir, norm);
-       return pow(max(tangDot(R, viewdir), 0.0), shiny);
-    #endif
-}
-
-vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){
-   float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);
-   float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess);
-
-   if (m_Shininess <= 1.0) {
-       specularFactor = 0.0; // should be one instruction on most cards ..
-   }
-
-   float att = vLightDir.w;
-
-   return vec2(diffuseFactor, specularFactor) * vec2(att);
-}
-
-
 #ifdef ALPHAMAP
 
   vec4 calculateDiffuseBlend(in vec2 texCoord) {
@@ -355,7 +309,7 @@ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 w
     vec4 getTriPlanarBlend(in vec4 coords, in vec3 blending, in sampler2D map, in float scale) {
       vec4 col1 = texture2D( map, coords.yz * scale);
       vec4 col2 = texture2D( map, coords.xz * scale);
-      vec4 col3 = texture2D( map, coords.xy * scale);
+      vec4 col3 = texture2D( map, coords.xy * scale); 
       // blend the results of the 3 planar projections.
       vec4 tex = col1 * blending.x + col2 * blending.y + col3 * blending.z;
       return tex;
@@ -627,7 +581,7 @@ void main(){
                   spotFallOff = clamp(spotFallOff, 0.0, 1.0);
               }
         }
-    
+
     //---------------------
     // normal calculations
     //---------------------
@@ -648,7 +602,7 @@ void main(){
     vec4 lightDir = vLightDir;
     lightDir.xyz = normalize(lightDir.xyz);
 
-    vec2 light = computeLighting(vPosition, normal, vViewDir.xyz, lightDir.xyz)*spotFallOff;
+    vec2 light = computeLighting(normal, vViewDir.xyz, lightDir.xyz,lightDir.w*spotFallOff,m_Shininess);
 
     vec4 specularColor = vec4(1.0);
 

+ 64 - 0
jme3-terrain/src/main/resources/Common/MatDefs/Terrain/TerrainLighting.j3md

@@ -163,6 +163,70 @@ MaterialDef Terrain Lighting {
         }
     }
 
+
+    Technique {
+
+        LightMode SinglePass
+
+        VertexShader GLSL100:   Common/MatDefs/Terrain/SPTerrainLighting.vert
+        FragmentShader GLSL100: Common/MatDefs/Terrain/SPTerrainLighting.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            NormalMatrix
+            WorldViewMatrix
+            ViewMatrix
+        }
+
+        Defines {
+            TRI_PLANAR_MAPPING : useTriPlanarMapping
+            TERRAIN_GRID : isTerrainGrid
+            WARDISO   : WardIso
+
+            DIFFUSEMAP : DiffuseMap
+            DIFFUSEMAP_1 : DiffuseMap_1
+            DIFFUSEMAP_2 : DiffuseMap_2
+            DIFFUSEMAP_3 : DiffuseMap_3
+            DIFFUSEMAP_4 : DiffuseMap_4
+            DIFFUSEMAP_5 : DiffuseMap_5
+            DIFFUSEMAP_6 : DiffuseMap_6
+            DIFFUSEMAP_7 : DiffuseMap_7
+            DIFFUSEMAP_8 : DiffuseMap_8
+            DIFFUSEMAP_9 : DiffuseMap_9
+            DIFFUSEMAP_10 : DiffuseMap_10
+            DIFFUSEMAP_11 : DiffuseMap_11
+            NORMALMAP : NormalMap
+            NORMALMAP_1 : NormalMap_1
+            NORMALMAP_2 : NormalMap_2
+            NORMALMAP_3 : NormalMap_3
+            NORMALMAP_4 : NormalMap_4
+            NORMALMAP_5 : NormalMap_5
+            NORMALMAP_6 : NormalMap_6
+            NORMALMAP_7 : NormalMap_7
+            NORMALMAP_8 : NormalMap_8
+            NORMALMAP_9 : NormalMap_9
+            NORMALMAP_10 : NormalMap_10
+            NORMALMAP_11 : NormalMap_11
+            SPECULARMAP : SpecularMap
+            ALPHAMAP : AlphaMap
+            ALPHAMAP_1 : AlphaMap_1
+            ALPHAMAP_2 : AlphaMap_2
+            DIFFUSEMAP_0_SCALE : DiffuseMap_0_scale
+            DIFFUSEMAP_1_SCALE : DiffuseMap_1_scale
+            DIFFUSEMAP_2_SCALE : DiffuseMap_2_scale
+            DIFFUSEMAP_3_SCALE : DiffuseMap_3_scale
+            DIFFUSEMAP_4_SCALE : DiffuseMap_4_scale
+            DIFFUSEMAP_5_SCALE : DiffuseMap_5_scale
+            DIFFUSEMAP_6_SCALE : DiffuseMap_6_scale
+            DIFFUSEMAP_7_SCALE : DiffuseMap_7_scale
+            DIFFUSEMAP_8_SCALE : DiffuseMap_8_scale
+            DIFFUSEMAP_9_SCALE : DiffuseMap_9_scale
+            DIFFUSEMAP_10_SCALE : DiffuseMap_10_scale
+            DIFFUSEMAP_11_SCALE : DiffuseMap_11_scale
+        }
+    }
+
+
     Technique PreShadow {
 
         VertexShader GLSL100 :   Common/MatDefs/Shadow/PreShadow.vert

+ 18 - 31
jme3-terrain/src/main/resources/Common/MatDefs/Terrain/TerrainLighting.vert

@@ -1,3 +1,5 @@
+#import "Common/ShaderLib/Lighting.glsllib"
+
 uniform mat4 g_WorldViewProjectionMatrix;
 uniform mat4 g_WorldViewMatrix;
 uniform mat3 g_NormalMatrix;
@@ -34,16 +36,6 @@ varying vec4 SpecularSum;
   varying vec3 wNormal;
 #endif
 
-// JME3 lights in world space
-void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
-    float posLight = step(0.5, color.w);
-    vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
-    lightVec.xyz = tempVec;  
-    float dist = length(tempVec);
-    lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
-    lightDir.xyz = tempVec / vec3(dist);
-}
-
 
 void main(){
     vec4 pos = vec4(inPosition, 1.0);
@@ -66,35 +58,30 @@ void main(){
     // specific to normal maps:
     //--------------------------
     #if defined(NORMALMAP) || defined(NORMALMAP_1) || defined(NORMALMAP_2) || defined(NORMALMAP_3) || defined(NORMALMAP_4) || defined(NORMALMAP_5) || defined(NORMALMAP_6) || defined(NORMALMAP_7) || defined(NORMALMAP_8) || defined(NORMALMAP_9) || defined(NORMALMAP_10) || defined(NORMALMAP_11)
-      vec3 wvTangent = normalize(g_NormalMatrix * inTangent.xyz);
-      vec3 wvBinormal = cross(wvNormal, wvTangent);
+        vec3 wvTangent = normalize(g_NormalMatrix * inTangent.xyz);
+        vec3 wvBinormal = cross(wvNormal, wvTangent);
 
-      mat3 tbnMat = mat3(wvTangent, wvBinormal * -inTangent.w,wvNormal);
+        mat3 tbnMat = mat3(wvTangent, wvBinormal * inTangent.w,wvNormal);
 
-      vPosition = wvPosition * tbnMat;
-      vViewDir  = viewDir * tbnMat;
-      lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
-      vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz;
-    #else
+        vPosition = wvPosition * tbnMat;
+        vViewDir  = viewDir * tbnMat;
 
-    //-------------------------
-    // general to all lighting
-    //-------------------------
-    vNormal = wvNormal;
+        lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec);
+        vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz;
+    #else
+        //-------------------------
+        // general to all lighting
+        //-------------------------
+        vNormal = wvNormal;
 
-    vPosition = wvPosition;
-    vViewDir = viewDir;
+        vPosition = wvPosition;
+        vViewDir = viewDir;
 
-    lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
+        lightComputeDir(wvPosition, lightColor.w, wvLightPos, vLightDir, lightVec);
 
     #endif
    
-      //computing spot direction in view space and unpacking spotlight cos
-  // spotVec=(g_ViewMatrix *vec4(g_LightDirection.xyz,0.0) );
-  // spotVec.w=floor(g_LightDirection.w)*0.001;
-  // lightVec.w = fract(g_LightDirection.w);
-
-    AmbientSum  = vec4(0.2, 0.2, 0.2, 1.0) * g_AmbientLightColor; // Default: ambient color is dark gray
+    AmbientSum  = g_AmbientLightColor; // Default: ambient color is dark gray
     DiffuseSum  = lightColor;
     SpecularSum = lightColor;