فهرست منبع

Merge branch 'master' of https://github.com/bob0bob/jmonkeyengine

# Conflicts:
#	jme3-core/src/main/java/com/jme3/app/LegacyApplication.java
KEVIN-DESKTOP\kevinba 2 سال پیش
والد
کامیت
80881090e7
100فایلهای تغییر یافته به همراه1636 افزوده شده و 1290 حذف شده
  1. 21 0
      .github/workflows/format.yml
  2. 6 6
      .github/workflows/main.yml
  3. 4 4
      .vscode/JME_style.xml
  4. 2 2
      README.md
  5. 1 1
      build.gradle
  6. 25 2
      common.gradle
  7. 6 0
      config/checkstyle/checkstyle-suppressions.xml
  8. 361 0
      config/checkstyle/checkstyle.xml
  9. 1 1
      gradle/wrapper/gradle-wrapper.properties
  10. 1 1
      jme3-android-examples/build.gradle
  11. 28 4
      jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
  12. 2 2
      jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java
  13. 46 3
      jme3-core/src/main/java/com/jme3/anim/ArmatureMask.java
  14. 3 0
      jme3-core/src/main/java/com/jme3/anim/tween/AbstractTween.java
  15. 3 0
      jme3-core/src/main/java/com/jme3/anim/tween/action/BlendableAction.java
  16. 10 3
      jme3-core/src/main/java/com/jme3/app/AppTask.java
  17. 4 2
      jme3-core/src/main/java/com/jme3/app/Application.java
  18. 5 1
      jme3-core/src/main/java/com/jme3/app/BasicProfiler.java
  19. 41 42
      jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java
  20. 4 2
      jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java
  21. 7 4
      jme3-core/src/main/java/com/jme3/app/DebugKeysAppState.java
  22. 13 10
      jme3-core/src/main/java/com/jme3/app/DetailedProfiler.java
  23. 33 20
      jme3-core/src/main/java/com/jme3/app/DetailedProfilerState.java
  24. 0 841
      jme3-core/src/main/java/com/jme3/app/LegacyApplication.java
  25. 13 7
      jme3-core/src/main/java/com/jme3/app/SimpleApplication.java
  26. 6 5
      jme3-core/src/main/java/com/jme3/app/StatsView.java
  27. 3 1
      jme3-core/src/main/java/com/jme3/asset/AssetConfig.java
  28. 17 4
      jme3-core/src/main/java/com/jme3/asset/AssetManager.java
  29. 5 3
      jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java
  30. 25 0
      jme3-core/src/main/java/com/jme3/audio/Environment.java
  31. 19 4
      jme3-core/src/main/java/com/jme3/export/JmeExporter.java
  32. 4 0
      jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java
  33. 5 3
      jme3-core/src/main/java/com/jme3/input/JoystickCompatibilityMappings.java
  34. 5 0
      jme3-core/src/main/java/com/jme3/light/PointLight.java
  35. 5 3
      jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java
  36. 4 2
      jme3-core/src/main/java/com/jme3/post/HDRRenderer.java
  37. 16 0
      jme3-core/src/main/java/com/jme3/renderer/Caps.java
  38. 39 1
      jme3-core/src/main/java/com/jme3/renderer/RenderManager.java
  39. 6 0
      jme3-core/src/main/java/com/jme3/renderer/Renderer.java
  40. 7 0
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLES_30.java
  41. 1 1
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java
  42. 92 42
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  43. 14 0
      jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java
  44. 2 2
      jme3-core/src/main/java/com/jme3/shader/ShaderNodeDefinition.java
  45. 2 2
      jme3-core/src/main/java/com/jme3/system/AppSettings.java
  46. 3 2
      jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java
  47. 3 1
      jme3-core/src/main/java/com/jme3/system/JmeVersion.java
  48. 5 0
      jme3-core/src/main/java/com/jme3/system/NullRenderer.java
  49. 12 2
      jme3-core/src/main/java/com/jme3/system/Platform.java
  50. 72 0
      jme3-core/src/main/java/com/jme3/util/res/DefaultResourceLoader.java
  51. 81 0
      jme3-core/src/main/java/com/jme3/util/res/ResourceLoader.java
  52. 182 0
      jme3-core/src/main/java/com/jme3/util/res/Resources.java
  53. 7 2
      jme3-core/src/main/resources/Common/MatDefs/Blur/HGaussianBlur.j3md
  54. 4 2
      jme3-core/src/main/resources/Common/MatDefs/Blur/RadialBlur.j3md
  55. 7 2
      jme3-core/src/main/resources/Common/MatDefs/Blur/VGaussianBlur.j3md
  56. 4 15
      jme3-core/src/main/resources/Common/MatDefs/Gui/Gui.j3md
  57. 4 2
      jme3-core/src/main/resources/Common/MatDefs/Hdr/LogLum.j3md
  58. 4 2
      jme3-core/src/main/resources/Common/MatDefs/Hdr/ToneMap.j3md
  59. 4 2
      jme3-core/src/main/resources/Common/MatDefs/Light/Deferred.j3md
  60. 2 0
      jme3-core/src/main/resources/Common/MatDefs/Light/Deferred.vert
  61. 2 0
      jme3-core/src/main/resources/Common/MatDefs/Light/GBuf.vert
  62. 20 13
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md
  63. 17 11
      jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md
  64. 7 5
      jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md
  65. 2 0
      jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.vert
  66. 13 8
      jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.j3md
  67. 4 2
      jme3-core/src/main/resources/Common/MatDefs/Misc/ShowNormals.j3md
  68. 4 2
      jme3-core/src/main/resources/Common/MatDefs/Misc/Sky.j3md
  69. 4 2
      jme3-core/src/main/resources/Common/MatDefs/Misc/SkyNonCube.j3md
  70. 16 10
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md
  71. 4 2
      jme3-core/src/main/resources/Common/MatDefs/Shadow/BasicPostShadow.j3md
  72. 4 2
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md
  73. 7 4
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md
  74. 10 2
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.j3md
  75. 32 4
      jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib
  76. 1 2
      jme3-core/src/main/resources/Common/ShaderLib/Shadows.glsllib
  77. 6 14
      jme3-core/src/plugins/java/com/jme3/asset/plugins/ClasspathLocator.java
  78. 2 2
      jme3-core/src/plugins/java/com/jme3/export/binary/BinaryExporter.java
  79. 1 6
      jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java
  80. 80 0
      jme3-core/src/test/java/com/jme3/anim/tween/action/ClipActionTest.java
  81. 1 1
      jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java
  82. 1 1
      jme3-core/src/test/java/com/jme3/material/MaterialTest.java
  83. 3 1
      jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java
  84. 10 2
      jme3-core/src/test/java/com/jme3/system/TestUtil.java
  85. 3 1
      jme3-desktop/src/main/java/com/jme3/app/AppletHarness.java
  86. 3 1
      jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java
  87. 7 5
      jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java
  88. 4 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/BloomExtract.j3md
  89. 5 3
      jme3-effects/src/main/resources/Common/MatDefs/Post/BloomFinal.j3md
  90. 4 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/CartoonEdge.j3md
  91. 4 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/Compose.j3md
  92. 4 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/ContrastAdjustment.frag
  93. 10 6
      jme3-effects/src/main/resources/Common/MatDefs/Post/ContrastAdjustment.j3md
  94. 0 95
      jme3-effects/src/main/resources/Common/MatDefs/Post/ContrastAdjustment15.frag
  95. 7 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/CrossHatch.j3md
  96. 4 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/DepthOfField.j3md
  97. 7 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/FXAA.j3md
  98. 4 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/Fade.j3md
  99. 4 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/Fog.j3md
  100. 4 2
      jme3-effects/src/main/resources/Common/MatDefs/Post/LightScattering.j3md

+ 21 - 0
.github/workflows/format.yml

@@ -0,0 +1,21 @@
+name: auto-format
+on:
+  push:
+
+jobs:
+  format:
+    runs-on: ubuntu-latest
+    if: github.repository != 'jMonkeyEngine/jmonkeyengine'
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0   
+      - name: Prettify code
+        uses: creyD/[email protected]
+        with:
+          prettier_options: --tab-width 4 --print-width 110 --write **/**/*.java
+          prettier_version: "2.8.8"
+          only_changed: True
+          commit_message:  "auto-format"
+          prettier_plugins: "prettier-plugin-java"

+ 6 - 6
.github/workflows/main.yml

@@ -65,11 +65,11 @@ jobs:
 
     steps:
       - name: Clone the repo
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 1
       - name: Validate the Gradle wrapper
-        uses: gradle/wrapper-validation-action@v1.0.5
+        uses: gradle/wrapper-validation-action@v1
       - name: Build
         run: |
           ./gradlew -PuseCommitHashAsVersionName=true --no-daemon -PbuildNativeProjects=true \
@@ -108,7 +108,7 @@ jobs:
 
     steps:
       - name: Clone the repo
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 1
 
@@ -125,7 +125,7 @@ jobs:
           path: build/native
 
       - name: Validate the Gradle wrapper
-        uses: gradle/wrapper-validation-action@v1.0.5
+        uses: gradle/wrapper-validation-action@v1
       - name: Build Engine
         shell: bash
         run: |
@@ -301,7 +301,7 @@ jobs:
 
       # We need to clone everything again for uploadToMaven.sh ...
       - name: Clone the repo
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 1
 
@@ -345,7 +345,7 @@ jobs:
 
       # We need to clone everything again for uploadToMaven.sh ...
       - name: Clone the repo
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 1
 

+ 4 - 4
.vscode/JME_style.xml

@@ -43,7 +43,7 @@
 <setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
 <setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
 <setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
-<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="110"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
@@ -89,7 +89,7 @@
 <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
 <setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
-<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="180"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="110"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
@@ -220,7 +220,7 @@
 <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
 <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
 <setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
-<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="110"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
 <setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
@@ -264,7 +264,7 @@
 <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
 <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
 <setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
-<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="110"/>
 <setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="true"/>
 <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
 <setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>

+ 2 - 2
README.md

@@ -4,7 +4,7 @@ jMonkeyEngine
 [![Build Status](https://github.com/jMonkeyEngine/jmonkeyengine/workflows/Build%20jMonkeyEngine/badge.svg)](https://github.com/jMonkeyEngine/jmonkeyengine/actions)
 
 jMonkeyEngine is a 3-D game engine for adventurous Java developers. It’s open-source, cross-platform, and cutting-edge.
-v3.6.0 is the latest stable version of the engine.
+v3.6.1 is the latest stable version of the engine.
 
 The engine is used by several commercial game studios and computer-science courses. Here's a taste:
 
@@ -28,9 +28,9 @@ The engine is used by several commercial game studios and computer-science cours
  - [Chatter Games](https://chatter-games.com)
  - [Exotic Matter](https://exoticmatter.io)
  - [Demon Lord (on Google Play)](https://play.google.com/store/apps/details?id=com.dreiInitiative.demonLord&pli=1)
- - [Wild Magic](http://wildmagicgame.ru/)
  - [Marvelous Marbles (on Steam)](https://store.steampowered.com/app/2244540/Marvelous_Marbles/)
  - [Boxer (on Google Play)](https://play.google.com/store/apps/details?id=com.tharg.boxer)
+ - [Depthris (on Itch)](https://codewalker.itch.io/depthris)
 
 ## Getting started
 

+ 1 - 1
build.gradle

@@ -283,7 +283,7 @@ if (skipPrebuildLibraries != "true" && buildNativeProjects != "true") {
 //}
 
 wrapper {
-    gradleVersion = '7.6'
+    gradleVersion = '7.6.2'
 }
 
 

+ 25 - 2
common.gradle

@@ -7,6 +7,8 @@ apply plugin: 'groovy'
 apply plugin: 'maven-publish'
 apply plugin: 'signing'
 apply plugin: 'eclipse'
+apply plugin: 'checkstyle'
+
 eclipse.jdt.file.withProperties { props ->
     props.setProperty "org.eclipse.jdt.core.circularClasspath", "warning"
 }
@@ -41,7 +43,7 @@ dependencies {
     // Adding dependencies here will add the dependencies to each subproject.
     testImplementation 'junit:junit:4.13.2'
     testImplementation 'org.mockito:mockito-core:3.12.4'
-    testImplementation 'org.codehaus.groovy:groovy-test:3.0.17'
+    testImplementation 'org.codehaus.groovy:groovy-test:3.0.19'
 }
 
 // Uncomment if you want to see the status of every test that is run and
@@ -183,7 +185,7 @@ publishing {
 }
 
 publishToMavenLocal.doLast {
-    println 'published ' + project.getName() + "-${jmeFullVersion} to mavenLocal"    
+    println 'published ' + project.getName() + "-${jmeFullVersion} to mavenLocal"
 }
 task('install') {
     dependsOn 'publishToMavenLocal'
@@ -200,3 +202,24 @@ signing {
 tasks.withType(Sign) {
     onlyIf { gradle.rootProject.hasProperty('signingKey') }
 }
+
+checkstyle {
+    toolVersion '9.3'
+    configFile file("${gradle.rootProject.rootDir}/config/checkstyle/checkstyle.xml")
+}
+
+checkstyleMain {
+    source ='src/main/java'
+}
+
+checkstyleTest {
+    source ='src/test/java'
+}
+
+tasks.withType(Checkstyle) {
+    reports {
+        xml.enabled false
+        html.enabled true
+    }
+    include("**/com/jme3/renderer/**/*.java")
+}

+ 6 - 0
config/checkstyle/checkstyle-suppressions.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<!DOCTYPE suppressions PUBLIC "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN" "https://checkstyle.org/dtds/suppressions_1_2.dtd">
+
+<suppressions>
+    <!-- https://checkstyle.org/filters/suppressionfilter.html -->
+</suppressions>

+ 361 - 0
config/checkstyle/checkstyle.xml

@@ -0,0 +1,361 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "https://checkstyle.org/dtds/configuration_1_3.dtd">
+
+<!--
+    Checkstyle configuration that checks the Google coding conventions from Google Java Style
+    that can be found at https://google.github.io/styleguide/javaguide.html. 
+
+    Based on google_checks.xml found in the checkstyle repository:
+    https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml
+
+    Adapted for changes by the jMonkeyEngine contribution guidelines document.
+ -->
+
+<module name="Checker">
+    <property name="charset" value="UTF-8" />
+
+    <property name="severity" value="warning" />
+
+    <property name="fileExtensions" value="java, properties, xml" />
+    <!-- Excludes all 'module-info.java' files              -->
+    <!-- See https://checkstyle.org/filefilters/index.html -->
+    <module name="BeforeExecutionExclusionFileFilter">
+        <property name="fileNamePattern" value="module\-info\.java$" />
+    </module>
+
+    <!-- https://checkstyle.org/filters/suppressionfilter.html -->
+    <module name="SuppressionFilter">
+        <property name="file" value="config/checkstyle/checkstyle-suppressions.xml"/>
+        <property name="optional" value="true" />
+    </module>
+
+    <!-- Checks for whitespace                               -->
+    <!-- See https://checkstyle.org/checks/whitespace/index.html -->
+    <module name="FileTabCharacter">
+        <property name="eachLine" value="false" />
+    </module>
+
+    <module name="LineLength">
+        <property name="fileExtensions" value="java" />
+        <property name="max" value="110" />
+        <property name="ignorePattern"
+            value="^package.*|^import.*|a href|href|http://|https://|ftp://" />
+    </module>
+
+    <module name="TreeWalker">
+        <module name="OuterTypeFilename" />
+        <module name="IllegalTokenText">
+            <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL" />
+            <property name="format"
+                value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)" />
+            <property name="message"
+                value="Consider using special escape sequence instead of octal value or Unicode escaped value." />
+        </module>
+        <module name="AvoidEscapedUnicodeCharacters">
+            <property name="allowEscapesForControlCharacters" value="true" />
+            <property name="allowByTailComment" value="true" />
+            <property name="allowNonPrintableEscapes" value="true" />
+        </module>
+        <module name="AvoidStarImport"/>
+        <module name="OneTopLevelClass" />
+        <module name="NoLineWrap">
+            <property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT" />
+        </module>
+        <module name="EmptyBlock">
+            <property name="option" value="TEXT" />
+            <property name="tokens"
+                value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH" />
+        </module>
+        <module name="NeedBraces">
+            <property name="tokens"
+                value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE" />
+        </module>
+        <module name="LeftCurly">
+            <property name="tokens"
+                value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
+                    INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
+                    LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
+                    LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
+                    OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF" />
+        </module>
+        <module name="RightCurly">
+            <property name="id" value="RightCurlySame" />
+            <property name="tokens"
+                value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
+                    LITERAL_DO" />
+        </module>
+        <module name="RightCurly">
+            <property name="id" value="RightCurlyAlone" />
+            <property name="option" value="alone" />
+            <property name="tokens"
+                value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
+                    INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
+                    COMPACT_CTOR_DEF" />
+        </module>
+        <module name="SuppressionXpathSingleFilter">
+            <!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
+            <property name="id" value="RightCurlyAlone" />
+            <property name="query"
+                value="//RCURLY[parent::SLIST[count(./*)=1]
+                                     or preceding-sibling::*[last()][self::LCURLY]]" />
+        </module>
+        <module name="WhitespaceAfter">
+            <property name="tokens"
+                value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE,
+                    LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, DO_WHILE" />
+        </module>
+        <module name="WhitespaceAround">
+            <property name="allowEmptyConstructors" value="true" />
+            <property name="allowEmptyLambdas" value="true" />
+            <property name="allowEmptyMethods" value="true" />
+            <property name="allowEmptyTypes" value="true" />
+            <property name="allowEmptyLoops" value="true" />
+            <property name="ignoreEnhancedForColon" value="false" />
+            <property name="tokens"
+                value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
+                    BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
+                    LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
+                    LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
+                    LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
+                    NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
+                    SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND" />
+            <message key="ws.notFollowed"
+                value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)" />
+            <message key="ws.notPreceded"
+                value="WhitespaceAround: ''{0}'' is not preceded with whitespace." />
+        </module>
+        <module name="OneStatementPerLine" />
+        <module name="MultipleVariableDeclarations" />
+        <module name="ArrayTypeStyle" />
+        <module name="MissingSwitchDefault" />
+        <module name="FallThrough" />
+        <module name="UpperEll" />
+        <module name="ModifierOrder" />
+        <module name="EmptyLineSeparator">
+            <property name="tokens"
+                value="IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
+                    STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
+                    COMPACT_CTOR_DEF" />
+            <property name="allowNoEmptyLineBetweenFields" value="true" />
+        </module>
+        <module name="SeparatorWrap">
+            <property name="id" value="SeparatorWrapDot" />
+            <property name="tokens" value="DOT" />
+            <property name="option" value="nl" />
+        </module>
+        <module name="SeparatorWrap">
+            <property name="id" value="SeparatorWrapComma" />
+            <property name="tokens" value="COMMA" />
+            <property name="option" value="EOL" />
+        </module>
+        <module name="SeparatorWrap">
+            <!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 -->
+            <property name="id" value="SeparatorWrapEllipsis" />
+            <property name="tokens" value="ELLIPSIS" />
+            <property name="option" value="EOL" />
+        </module>
+        <module name="SeparatorWrap">
+            <!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 -->
+            <property name="id" value="SeparatorWrapArrayDeclarator" />
+            <property name="tokens" value="ARRAY_DECLARATOR" />
+            <property name="option" value="EOL" />
+        </module>
+        <module name="SeparatorWrap">
+            <property name="id" value="SeparatorWrapMethodRef" />
+            <property name="tokens" value="METHOD_REF" />
+            <property name="option" value="nl" />
+        </module>
+        <module name="PackageName">
+            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" />
+            <message key="name.invalidPattern"
+                value="Package name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="TypeName">
+            <property name="tokens"
+                value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
+                    ANNOTATION_DEF, RECORD_DEF" />
+            <message key="name.invalidPattern"
+                value="Type name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="MemberName">
+            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$" />
+            <message key="name.invalidPattern"
+                value="Member name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="ParameterName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$" />
+            <message key="name.invalidPattern"
+                value="Parameter name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="LambdaParameterName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$" />
+            <message key="name.invalidPattern"
+                value="Lambda parameter name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="CatchParameterName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$" />
+            <message key="name.invalidPattern"
+                value="Catch parameter name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="LocalVariableName">
+            <property name="format" value="^[a-z]([a-zA-Z0-9]*)?$" />
+            <message key="name.invalidPattern"
+                value="Local variable name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="PatternVariableName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$" />
+            <message key="name.invalidPattern"
+                value="Pattern variable name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="ClassTypeParameterName">
+            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" />
+            <message key="name.invalidPattern"
+                value="Class type name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="RecordComponentName">
+            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$" />
+            <message key="name.invalidPattern"
+                value="Record component name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="RecordTypeParameterName">
+            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" />
+            <message key="name.invalidPattern"
+                value="Record type name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="MethodTypeParameterName">
+            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" />
+            <message key="name.invalidPattern"
+                value="Method type name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="InterfaceTypeParameterName">
+            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" />
+            <message key="name.invalidPattern"
+                value="Interface type name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="NoFinalizer" />
+        <module name="GenericWhitespace">
+            <message key="ws.followed"
+                value="GenericWhitespace ''{0}'' is followed by whitespace." />
+            <message key="ws.preceded"
+                value="GenericWhitespace ''{0}'' is preceded with whitespace." />
+            <message key="ws.illegalFollow"
+                value="GenericWhitespace ''{0}'' should followed by whitespace." />
+            <message key="ws.notPreceded"
+                value="GenericWhitespace ''{0}'' is not preceded with whitespace." />
+        </module>
+        <module name="Indentation">
+            <property name="basicOffset" value="4" />
+            <property name="braceAdjustment" value="4" />
+            <property name="caseIndent" value="4" />
+            <property name="throwsIndent" value="4" />
+            <property name="lineWrappingIndentation" value="8" />
+            <property name="arrayInitIndent" value="4" />
+        </module>
+        <module name="AbbreviationAsWordInName">
+            <property name="ignoreFinal" value="false" />
+            <property name="allowedAbbreviationLength" value="1" />
+            <property name="tokens"
+                value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
+                    PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
+                    RECORD_COMPONENT_DEF" />
+        </module>
+        <module name="NoWhitespaceBeforeCaseDefaultColon" />
+        <module name="VariableDeclarationUsageDistance" />
+        <module name="CustomImportOrder">
+            <property name="sortImportsInGroupAlphabetically" value="true" />
+            <property name="separateLineBetweenGroups" value="true" />
+            <property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE" />
+            <property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF" />
+        </module>
+        <module name="MethodParamPad">
+            <property name="tokens"
+                value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
+                    SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF" />
+        </module>
+        <module name="NoWhitespaceBefore">
+            <property name="tokens"
+                value="COMMA, SEMI, POST_INC, POST_DEC, DOT,
+                    LABELED_STAT, METHOD_REF" />
+            <property name="allowLineBreaks" value="true" />
+        </module>
+        <module name="ParenPad">
+            <property name="tokens"
+                value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
+                    EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
+                    LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
+                    METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
+                    RECORD_DEF" />
+        </module>
+        <module name="OperatorWrap">
+            <property name="option" value="NL" />
+            <property name="tokens"
+                value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
+                    LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF,
+                    TYPE_EXTENSION_AND " />
+        </module>
+        <module name="AnnotationLocation">
+            <property name="id" value="AnnotationLocationMostCases" />
+            <property name="tokens"
+                value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF,
+                      RECORD_DEF, COMPACT_CTOR_DEF" />
+        </module>
+        <module name="AnnotationLocation">
+            <property name="id" value="AnnotationLocationVariables" />
+            <property name="tokens" value="VARIABLE_DEF" />
+            <property name="allowSamelineMultipleAnnotations" value="true" />
+        </module>
+        <module name="NonEmptyAtclauseDescription" />
+
+        <!--
+
+        Commented out javadoc checks, these produce a lot (4k in jme3-core) of warnings so for now, might
+        be too noisy
+
+        <module name="InvalidJavadocPosition" />
+        <module name="JavadocTagContinuationIndentation" />
+        <module name="SummaryJavadoc">
+            <property name="forbiddenSummaryFragments"
+                value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )" />
+        </module>
+        <module name="JavadocParagraph" />
+        <module name="RequireEmptyLineBeforeBlockTagGroup" />
+        <module name="AtclauseOrder">
+            <property name="tagOrder" value="@param, @return, @throws, @deprecated" />
+            <property name="target"
+                value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF" />
+        </module>
+        <module name="JavadocMethod">
+            <property name="accessModifiers" value="public" />
+            <property name="allowMissingParamTags" value="true" />
+            <property name="allowMissingReturnTag" value="true" />
+            <property name="allowedAnnotations" value="Override, Test" />
+            <property name="tokens"
+                value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF" />
+        </module>
+        <module name="MissingJavadocMethod">
+            <property name="scope" value="public" />
+            <property name="minLineCount" value="2" />
+            <property name="allowedAnnotations" value="Override, Test" />
+            <property name="tokens"
+                value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF,
+                                   COMPACT_CTOR_DEF" />
+        </module>
+        <module name="MissingJavadocType">
+            <property name="scope" value="protected" />
+            <property name="tokens"
+                value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
+                      RECORD_DEF, ANNOTATION_DEF" />
+            <property name="excludeScope" value="nothing" />
+        </module>
+        <module name="MethodName">
+            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$" />
+            <message key="name.invalidPattern"
+                value="Method name ''{0}'' must match pattern ''{1}''." />
+        </module>
+        <module name="SingleLineJavadoc" />
+    -->
+        <module name="EmptyCatchBlock">
+            <property name="exceptionVariableName" value="expected" />
+        </module>
+    </module>
+</module>

+ 1 - 1
gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-bin.zip
 networkTimeout=10000
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists

+ 1 - 1
jme3-android-examples/build.gradle

@@ -2,7 +2,7 @@ apply plugin: 'com.android.application'
 
 android {
     compileSdkVersion 28
-    buildToolsVersion "28.0.3"
+    buildToolsVersion "30.0.2"
 
     lintOptions {
         // Fix nifty gui referencing "java.awt" package.

+ 28 - 4
jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java

@@ -45,6 +45,7 @@ import java.nio.ShortBuffer;
 public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
 
     IntBuffer tmpBuff = BufferUtils.createIntBuffer(1);
+    IntBuffer tmpBuff16 = BufferUtils.createIntBuffer(16);
 
     @Override
     public void resetStats() {
@@ -694,10 +695,17 @@ public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
     // Wrapper to DrawBuffers as there's no DrawBuffer method in GLES
     @Override
     public void glDrawBuffer(int mode) {
-        tmpBuff.clear();
-        tmpBuff.put(0, mode);
-        tmpBuff.rewind();
-        glDrawBuffers(tmpBuff);
+        int nBuffers = (mode - GLFbo.GL_COLOR_ATTACHMENT0_EXT) + 1;
+        if (nBuffers <= 0 || nBuffers > 16) {
+            throw new IllegalArgumentException("Draw buffer outside range: " + Integer.toHexString(mode));
+        }
+        tmpBuff16.clear();
+        for (int i = 0; i < nBuffers - 1; i++) {
+            tmpBuff16.put(GL.GL_NONE);
+        }
+        tmpBuff16.put(mode);
+        tmpBuff16.flip();
+        glDrawBuffers(tmpBuff16);
     }
 
     @Override
@@ -729,5 +737,21 @@ public class AndroidGL implements GL, GL2, GLES_30, GLExt, GLFbo {
         GLES30.glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data);
     }
 
+    @Override
+    public void glBindVertexArray(int array) {
+        GLES30.glBindVertexArray(array);
+    }
+
+    @Override
+    public void glDeleteVertexArrays(IntBuffer arrays) {
+       GLES30.glDeleteVertexArrays(arrays.limit(),arrays);
+    }
+
+    @Override
+    public void glGenVertexArrays(IntBuffer arrays) {
+        GLES30.glGenVertexArrays(arrays.limit(),arrays);
+
+    }
+
 }
 

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

@@ -17,7 +17,7 @@ import com.jme3.audio.openal.EFX;
 import com.jme3.system.*;
 import com.jme3.system.JmeContext.Type;
 import com.jme3.util.AndroidScreenshots;
-import com.jme3.util.functional.VoidFunction;
+import com.jme3.util.res.Resources;
 
 import java.io.File;
 import java.io.IOException;
@@ -52,7 +52,7 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
     
     @Override
     public URL getPlatformAssetConfigURL() {
-        return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg");
+        return Resources.getResource("com/jme3/asset/Android.cfg");
     }
 
     @Override

+ 46 - 3
jme3-core/src/main/java/com/jme3/anim/ArmatureMask.java

@@ -1,3 +1,34 @@
+/*
+ * Copyright (c) 2009-2023 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.anim;
 
 import java.util.BitSet;
@@ -49,6 +80,9 @@ public class ArmatureMask implements AnimationMask {
      * @param armature the Armature containing the joints (not null, unaffected)
      * @param jointNames the names of the joints to be removed
      * @return this
+     *
+     * @throws IllegalArgumentException if it can not find the joint
+     *          with the specified name on the provided armature
      */
     public ArmatureMask removeJoints(Armature armature, String... jointNames) {
         for (String jointName : jointNames) {
@@ -72,6 +106,9 @@ public class ArmatureMask implements AnimationMask {
      * @param armature the Armature containing the joints (not null)
      * @param fromJoint the name of the ancestor joint
      * @return a new mask
+     *
+     * @throws IllegalArgumentException if it can not find the joint
+     *          with the specified name on the provided armature
      */
     public static ArmatureMask createMask(Armature armature, String fromJoint) {
         ArmatureMask mask = new ArmatureMask();
@@ -85,13 +122,13 @@ public class ArmatureMask implements AnimationMask {
      * @param armature the Armature containing the joints (not null)
      * @param joints the names of the joints to be included
      * @return a new mask
+     *
+     * @throws IllegalArgumentException if it can not find the joint
+     *          with the specified name on the provided armature
      */
     public static ArmatureMask createMask(Armature armature, String... joints) {
         ArmatureMask mask = new ArmatureMask();
         mask.addBones(armature, joints);
-        for (String joint : joints) {
-            mask.affectedJoints.set(armature.getJoint(joint).getId());
-        }
         return mask;
     }
 
@@ -100,6 +137,9 @@ public class ArmatureMask implements AnimationMask {
      * 
      * @param armature the Armature containing the joints
      * @param jointNames the names of the joints to be influenced
+     *
+     * @throws IllegalArgumentException if it can not find the joint
+     *          with the specified name on the provided armature
      */
     public void addBones(Armature armature, String... jointNames) {
         for (String jointName : jointNames) {
@@ -121,6 +161,9 @@ public class ArmatureMask implements AnimationMask {
      * 
      * @param armature the Armature containing the ancestor joint
      * @param jointName the names of the ancestor joint
+     *
+     * @throws IllegalArgumentException if it can not find the joint
+     *          with the specified name on the provided armature
      */
     public void addFromJoint(Armature armature, String jointName) {
         Joint joint = findJoint(armature, jointName);

+ 3 - 0
jme3-core/src/main/java/com/jme3/anim/tween/AbstractTween.java

@@ -57,6 +57,9 @@ public abstract class AbstractTween implements JmeCloneable, Tween {
     }
 
     public void setLength(double length) {
+        if (length < 0.0) {
+            throw new IllegalArgumentException("length must be greater than or equal to 0");
+        }
         this.length = length;
     }
 

+ 3 - 0
jme3-core/src/main/java/com/jme3/anim/tween/action/BlendableAction.java

@@ -105,6 +105,9 @@ public abstract class BlendableAction extends Action {
     }
 
     public void setTransitionLength(double transitionLength) {
+        if (transitionLength < 0.0) {
+            throw new IllegalArgumentException("transitionLength must be greater than or equal to 0");
+        }
         this.transitionLength = transitionLength;
         this.transition.setLength(transitionLength);
     }

+ 10 - 3
jme3-core/src/main/java/com/jme3/app/AppTask.java

@@ -31,7 +31,12 @@
  */
 package com.jme3.app;
 
-import java.util.concurrent.*;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Level;
@@ -52,7 +57,8 @@ public class AppTask<V> implements Future<V> {
 
     private V result;
     private ExecutionException exception;
-    private boolean cancelled, finished;
+    private boolean cancelled;
+    private boolean finished;
     private final ReentrantLock stateLock = new ReentrantLock();
     private final Condition finishedCondition = stateLock.newCondition();
 
@@ -100,7 +106,8 @@ public class AppTask<V> implements Future<V> {
     }
 
     @Override
-    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+    public V get(long timeout, TimeUnit unit)
+        throws InterruptedException, ExecutionException, TimeoutException {
         stateLock.lock();
         try {
             if (!isDone()) {

+ 4 - 2
jme3-core/src/main/java/com/jme3/app/Application.java

@@ -41,7 +41,9 @@ import com.jme3.renderer.Camera;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.ViewPort;
-import com.jme3.system.*;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext;
+import com.jme3.system.Timer;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
 
@@ -226,7 +228,7 @@ public interface Application {
      * After the application has stopped, it cannot be used anymore.
      * 
      @param waitFor true&rarr;wait for the context to be fully destroyed,
-     * true&rarr;don't wait
+     * false&rarr;don't wait
      */
     public void stop(boolean waitFor);
 

+ 5 - 1
jme3-core/src/main/java/com/jme3/app/BasicProfiler.java

@@ -32,7 +32,11 @@
 
 package com.jme3.app;
 
-import com.jme3.profile.*;
+
+import com.jme3.profile.AppProfiler;
+import com.jme3.profile.AppStep;
+import com.jme3.profile.SpStep;
+import com.jme3.profile.VpStep;
 import com.jme3.renderer.ViewPort;
 import com.jme3.renderer.queue.RenderQueue.Bucket;
 import com.jme3.scene.Mesh;

+ 41 - 42
jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java

@@ -122,53 +122,52 @@ public class BasicProfilerState extends BaseAppState {
         float frameTime = 1000f / 60;
         mesh.setBuffer(Type.Position, 3, new float[] {
 
-                    // first quad
-                    0, 0, 0,
-                    size, 0, 0,
-                    size, frameTime, 0,
-                    0, frameTime, 0,
-
-                    // second quad
-                    0, frameTime, 0,
-                    size, frameTime, 0,
-                    size, frameTime * 2, 0,
-                    0, frameTime * 2, 0,
-
-                    // A lower dark border just to frame the
-                    // 'update' stats against bright backgrounds
-                    0, -2, 0,
-                    size, -2, 0,
-                    size, 0, 0,
-                    0, 0, 0
-                });
+                // first quad
+                0, 0, 0,
+                size, 0, 0,
+                size, frameTime, 0,
+                0, frameTime, 0,
+                // second quad
+                0, frameTime, 0,
+                size, frameTime, 0,
+                size, frameTime * 2, 0,
+                0, frameTime * 2, 0,
+
+                // A lower dark border just to frame the
+                // 'update' stats against bright backgrounds
+                0, -2, 0,
+                size, -2, 0,
+                size, 0, 0,
+                0, 0, 0
+        });
 
         mesh.setBuffer(Type.Color, 4, new float[] {
                     // first quad, within normal frame limits
-                    0, 1, 0, 0.25f,
-                    0, 1, 0, 0.25f,
-                    0, 0.25f, 0, 0.25f,
-                    0, 0.25f, 0, 0.25f,
-
-                    // Second quad, dropped frames
-                    0.25f, 0, 0, 0.25f,
-                    0.25f, 0, 0, 0.25f,
-                    1, 0, 0, 0.25f,
-                    1, 0, 0, 0.25f,
-
-                    0, 0, 0, 0.5f,
-                    0, 0, 0, 0.5f,
-                    0, 0, 0, 0.5f,
-                    0, 0, 0, 0.5f
-                });
+                0, 1, 0, 0.25f,
+                0, 1, 0, 0.25f,
+                0, 0.25f, 0, 0.25f,
+                0, 0.25f, 0, 0.25f,
+
+                // Second quad, dropped frames
+                0.25f, 0, 0, 0.25f,
+                0.25f, 0, 0, 0.25f,
+                1, 0, 0, 0.25f,
+                1, 0, 0, 0.25f,
+
+                0, 0, 0, 0.5f,
+                0, 0, 0, 0.5f,
+                0, 0, 0, 0.5f,
+                0, 0, 0, 0.5f
+        });
 
         mesh.setBuffer(Type.Index, 3, new short[] {
-                    0, 1, 2,
-                    0, 2, 3,
-                    4, 5, 6,
-                    4, 6, 7,
-                    8, 9, 10,
-                    8, 10, 11
-                });
+                0, 1, 2,
+                0, 2, 3,
+                4, 5, 6,
+                4, 6, 7,
+                8, 9, 10,
+                8, 10, 11
+        });
     }
 
     @Override

+ 4 - 2
jme3-core/src/main/java/com/jme3/app/ChaseCameraAppState.java

@@ -82,7 +82,8 @@ public class ChaseCameraAppState extends AbstractAppState implements ActionListe
     protected Vector3f leftVector = new Vector3f();
     protected Trigger[] zoomOutTrigger = {new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)};
     protected Trigger[] zoomInTrigger = {new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false)};
-    protected Trigger[] toggleRotateTrigger = {new MouseButtonTrigger(MouseInput.BUTTON_LEFT), new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)};
+    protected Trigger[] toggleRotateTrigger = {new MouseButtonTrigger(MouseInput.BUTTON_LEFT),
+            new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)};
 
 //
 //    protected boolean rotating = false;
@@ -209,7 +210,8 @@ public class ChaseCameraAppState extends AbstractAppState implements ActionListe
     @Override
     public void update(float tpf) {
         if (spatial == null) {
-            throw new IllegalArgumentException("The spatial to follow is null, please use the setTarget method");
+            throw new IllegalArgumentException(
+                    "The spatial to follow is null, please use the setTarget method");
         }
         target.setLocalTranslation(spatial.getWorldTranslation());
         camNode.lookAt(target.getWorldTranslation(), upVector);

+ 7 - 4
jme3-core/src/main/java/com/jme3/app/DebugKeysAppState.java

@@ -55,7 +55,7 @@ public class DebugKeysAppState extends AbstractAppState {
     public static final String INPUT_MAPPING_MEMORY = "SIMPLEAPP_Memory";
     
     private Application app;
-    final private DebugKeyListener keyListener = new DebugKeyListener();
+    private final DebugKeyListener keyListener = new DebugKeyListener();
     private InputManager inputManager;
 
     public DebugKeysAppState() {
@@ -83,10 +83,12 @@ public class DebugKeysAppState extends AbstractAppState {
     public void cleanup() {
         super.cleanup();
 
-        if (inputManager.hasMapping(INPUT_MAPPING_CAMERA_POS))
+        if (inputManager.hasMapping(INPUT_MAPPING_CAMERA_POS)) {
             inputManager.deleteMapping(INPUT_MAPPING_CAMERA_POS);
-        if (inputManager.hasMapping(INPUT_MAPPING_MEMORY))
+        }
+        if (inputManager.hasMapping(INPUT_MAPPING_MEMORY)) {
             inputManager.deleteMapping(INPUT_MAPPING_MEMORY);
+        }
         
         inputManager.removeListener(keyListener);
     }
@@ -111,7 +113,8 @@ public class DebugKeysAppState extends AbstractAppState {
                     System.out.println("Camera Direction: " + cam.getDirection());
                     System.out.println("cam.setLocation(new Vector3f("
                             + loc.x + "f, " + loc.y + "f, " + loc.z + "f));");
-                    System.out.println("cam.setRotation(new Quaternion(" + rot.getX() + "f, " +rot.getY()+ "f, " + rot.getZ() + "f, " + rot.getW() + "f));");
+                    System.out.println("cam.setRotation(new Quaternion(" + rot.getX() + "f, " + rot.getY()
+                            + "f, " + rot.getZ() + "f, " + rot.getW() + "f));");
                   
                 }
             } else if (name.equals(INPUT_MAPPING_MEMORY)) {

+ 13 - 10
jme3-core/src/main/java/com/jme3/app/DetailedProfiler.java

@@ -43,7 +43,7 @@ import java.util.*;
  */
 public class DetailedProfiler implements AppProfiler {
 
-    private final static int MAX_FRAMES = 100;
+    private static final int MAX_FRAMES = 100;
     private Map<String, StatLine> data;
     private Map<String, StatLine> pool;
     private long startFrame;
@@ -59,10 +59,10 @@ public class DetailedProfiler implements AppProfiler {
     private String curSpPath = null;
     private VpStep lastVpStep = null;
 
-    final private StringBuilder path = new StringBuilder(256);
-    final private StringBuilder vpPath = new StringBuilder(256);
+    private final StringBuilder path = new StringBuilder(256);
+    private final StringBuilder vpPath = new StringBuilder(256);
 
-    final private Deque<Integer> idsPool = new ArrayDeque<>(100);
+    private final Deque<Integer> idsPool = new ArrayDeque<>(100);
 
     StatLine frameTime;
 
@@ -152,14 +152,17 @@ public class DetailedProfiler implements AppProfiler {
 
         if (data != null) {
             vpPath.setLength(0);
-            vpPath.append(vp.getName()).append("/").append((bucket == null ? step.name() : bucket.name() + " Bucket"));
+            vpPath.append(vp.getName()).append("/")
+                    .append((bucket == null ? step.name() : bucket.name() + " Bucket"));
             path.setLength(0);
             if ((lastVpStep == VpStep.PostQueue || lastVpStep == VpStep.PostFrame) && bucket != null) {
-                path.append(curAppPath).append("/").append(curVpPath).append(curSpPath).append("/").append(vpPath);
+                path.append(curAppPath).append("/").append(curVpPath).append(curSpPath).append("/")
+                    .append(vpPath);
                 curVpPath = vpPath.toString();
             } else {
                 if (bucket != null) {
-                    path.append(curAppPath).append("/").append(curVpPath).append("/").append(bucket.name() + " Bucket");
+                    path.append(curAppPath).append("/").append(curVpPath).append("/")
+                        .append(bucket.name() + " Bucket");
                 } else {
                     path.append(curAppPath).append("/").append(vpPath);
                     curVpPath = vpPath.toString();
@@ -185,7 +188,7 @@ public class DetailedProfiler implements AppProfiler {
 
     public Map<String, StatLine> getStats() {
         if (data != null) {
-            return data;//new LinkedHashMap<>(data);
+            return data; //new LinkedHashMap<>(data);
         }
         return null;
     }
@@ -256,8 +259,8 @@ public class DetailedProfiler implements AppProfiler {
     }
 
     public static class StatLine {
-        final private long[] cpuTimes = new long[MAX_FRAMES];
-        final private long[] gpuTimes = new long[MAX_FRAMES];
+        private final long[] cpuTimes = new long[MAX_FRAMES];
+        private final long[] gpuTimes = new long[MAX_FRAMES];
         private int startCursor = 0;
         private int cpuCursor = 0;
         private int gpuCursor = 0;

+ 33 - 20
jme3-core/src/main/java/com/jme3/app/DetailedProfilerState.java

@@ -60,13 +60,13 @@ public class DetailedProfilerState extends BaseAppState {
     private static final String TOGGLE_KEY = "Toggle_Detailed_Profiler";
     private static final String CLICK_KEY = "Click_Detailed_Profiler";
     private static final String INSIGNIFICANT = "Hide insignificant stat";
-    final private DetailedProfiler prof = new DetailedProfiler();
+    private final DetailedProfiler prof = new DetailedProfiler();
 
     private float time = 0;
     private BitmapFont font;
     private BitmapFont bigFont;
-    final private Node ui = new Node("Stats ui");
-    final private Map<String, StatLineView> lines = new HashMap<>();
+    private final Node ui = new Node("Stats ui");
+    private final Map<String, StatLineView> lines = new HashMap<>();
     private double totalTimeCpu;
     private double totalTimeGpu;
     private int maxLevel = 0;
@@ -83,14 +83,14 @@ public class DetailedProfilerState extends BaseAppState {
 
     private StatLineView rootLine;
     private int height = 0;
-    final private DecimalFormat df = new DecimalFormat("##0.00", new DecimalFormatSymbols(Locale.US));
+    private final DecimalFormat df = new DecimalFormat("##0.00", new DecimalFormatSymbols(Locale.US));
 
-    final private ColorRGBA dimmedWhite = ColorRGBA.White.mult(0.7f);
-    final private ColorRGBA dimmedGreen = ColorRGBA.Green.mult(0.7f);
-    final private ColorRGBA dimmedOrange = ColorRGBA.Orange.mult(0.7f);
-    final private ColorRGBA dimmedRed = ColorRGBA.Red.mult(0.7f);
+    private final ColorRGBA dimmedWhite = ColorRGBA.White.mult(0.7f);
+    private final ColorRGBA dimmedGreen = ColorRGBA.Green.mult(0.7f);
+    private final ColorRGBA dimmedOrange = ColorRGBA.Orange.mult(0.7f);
+    private final ColorRGBA dimmedRed = ColorRGBA.Red.mult(0.7f);
 
-    final private ProfilerInputListener inputListener = new ProfilerInputListener();
+    private final ProfilerInputListener inputListener = new ProfilerInputListener();
 
     public DetailedProfilerState() {
 
@@ -101,7 +101,8 @@ public class DetailedProfilerState extends BaseAppState {
         Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
         mat.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f));
         mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
-        Geometry darkenStats = new Geometry("StatsDarken", new Quad(PANEL_WIDTH, app.getCamera().getHeight()));
+        Geometry darkenStats = new Geometry("StatsDarken", new Quad(PANEL_WIDTH,
+                app.getCamera().getHeight()));
         darkenStats.setMaterial(mat);
         darkenStats.setLocalTranslation(0, -app.getCamera().getHeight(), -1);
 
@@ -116,17 +117,20 @@ public class DetailedProfilerState extends BaseAppState {
         BitmapText frameLabel = new BitmapText(bigFont);
         frameLabel.setText("Total Frame Time: ");
         ui.attachChild(frameLabel);
-        frameLabel.setLocalTranslation(new Vector3f(PANEL_WIDTH / 2 - bigFont.getLineWidth(frameLabel.getText()), -PADDING, 0));
+        frameLabel.setLocalTranslation(
+                new Vector3f(PANEL_WIDTH / 2 - bigFont.getLineWidth(frameLabel.getText()), -PADDING, 0));
 
         BitmapText cpuLabel = new BitmapText(bigFont);
         cpuLabel.setText("CPU");
         ui.attachChild(cpuLabel);
-        cpuLabel.setLocalTranslation(PANEL_WIDTH / 4 - bigFont.getLineWidth(cpuLabel.getText()) / 2, -PADDING - 30, 0);
+        cpuLabel.setLocalTranslation(PANEL_WIDTH / 4 - bigFont.getLineWidth(cpuLabel.getText()) / 2,
+                -PADDING - 30, 0);
 
         BitmapText gpuLabel = new BitmapText(bigFont);
         gpuLabel.setText("GPU");
         ui.attachChild(gpuLabel);
-        gpuLabel.setLocalTranslation(3 * PANEL_WIDTH / 4 - bigFont.getLineWidth(gpuLabel.getText()) / 2, -PADDING - 30, 0);
+        gpuLabel.setLocalTranslation(3 * PANEL_WIDTH / 4 - bigFont.getLineWidth(gpuLabel.getText()) / 2,
+                -PADDING - 30, 0);
 
         frameTimeValue = new BitmapText(bigFont);
         frameCpuTimeValue = new BitmapText(bigFont);
@@ -221,16 +225,22 @@ public class DetailedProfilerState extends BaseAppState {
         setColor(frameTimeValue, prof.getAverageFrameTime(), totalTimeCpu, false, false);
 
         frameCpuTimeValue.setText(df.format(getMsFromNs(totalTimeCpu)) + "ms");
-        frameCpuTimeValue.setLocalTranslation(new Vector3f(PANEL_WIDTH / 4 - bigFont.getLineWidth(frameCpuTimeValue.getText()) / 2, -PADDING - 50, 0));
+        frameCpuTimeValue.setLocalTranslation(
+                new Vector3f(PANEL_WIDTH / 4 - bigFont.getLineWidth(frameCpuTimeValue.getText()) / 2,
+                -PADDING - 50, 0));
         setColor(frameCpuTimeValue, totalTimeCpu, totalTimeCpu, false, false);
 
         frameGpuTimeValue.setText(df.format(getMsFromNs(totalTimeGpu)) + "ms");
-        frameGpuTimeValue.setLocalTranslation(new Vector3f(3 * PANEL_WIDTH / 4 - bigFont.getLineWidth(frameGpuTimeValue.getText()) / 2, -PADDING - 50, 0));
+        frameGpuTimeValue.setLocalTranslation(
+                new Vector3f(3 * PANEL_WIDTH / 4 - bigFont.getLineWidth(frameGpuTimeValue.getText()) / 2,
+                -PADDING - 50, 0));
         setColor(frameGpuTimeValue, totalTimeGpu, totalTimeGpu, false, false);
 
-        selectedField.setText("Selected: " + df.format(getMsFromNs(selectedValueCpu)) + "ms / " + df.format(getMsFromNs(selectedValueGpu)) + "ms");
+        selectedField.setText("Selected: " + df.format(getMsFromNs(selectedValueCpu)) + "ms / "
+                + df.format(getMsFromNs(selectedValueGpu)) + "ms");
 
-        selectedField.setLocalTranslation(3 * PANEL_WIDTH / 4 - font.getLineWidth(selectedField.getText()) / 2, -PADDING - 75, 0);
+        selectedField.setLocalTranslation(
+                3 * PANEL_WIDTH / 4 - font.getLineWidth(selectedField.getText()) / 2, -PADDING - 75, 0);
     }
 
     private StatLineView getStatLineView(String path) {
@@ -285,7 +295,8 @@ public class DetailedProfilerState extends BaseAppState {
         ui.removeFromParent();
     }
 
-    public boolean setColor(BitmapText t, double value, double totalTime, boolean isParent, boolean expended) {
+    public boolean setColor(BitmapText t, double value, double totalTime, boolean isParent,
+            boolean expended) {
 
         boolean dimmed = isParent && expended;
         boolean insignificant = false;
@@ -413,7 +424,8 @@ public class DetailedProfilerState extends BaseAppState {
             int y = -(height * LINE_HEIGHT + HEADER_HEIGHT);
 
             label.setLocalTranslation(PADDING + indent * PADDING, y, 0);
-            float gpuPos = PANEL_WIDTH - font.getLineWidth(gpuText.getText()) - PADDING * (maxLevel - indent + 1);
+            float gpuPos = PANEL_WIDTH - font.getLineWidth(gpuText.getText())
+                    - PADDING * (maxLevel - indent + 1);
             cpuText.setLocalTranslation(gpuPos - font.getLineWidth(cpuText.getText()), y, 0);
             gpuText.setLocalTranslation(gpuPos, y, 0);
 
@@ -466,7 +478,8 @@ public class DetailedProfilerState extends BaseAppState {
 
         @Override
         public String toString() {
-            return label.getText() + " - " + df.format(getMsFromNs(cpuValue)) + "ms / " + df.format(getMsFromNs(gpuValue)) + "ms";
+            return label.getText() + " - " + df.format(getMsFromNs(cpuValue)) + "ms / "
+                    + df.format(getMsFromNs(gpuValue)) + "ms";
         }
     }
 

+ 0 - 841
jme3-core/src/main/java/com/jme3/app/LegacyApplication.java

@@ -1,841 +0,0 @@
-/*
- * Copyright (c) 2009-2022 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.app;
-
-import com.jme3.app.state.AppState;
-import com.jme3.app.state.AppStateManager;
-import com.jme3.asset.AssetManager;
-import com.jme3.audio.AudioContext;
-import com.jme3.audio.AudioRenderer;
-import com.jme3.audio.Listener;
-import com.jme3.input.*;
-import com.jme3.math.Vector3f;
-import com.jme3.profile.AppProfiler;
-import com.jme3.profile.AppStep;
-import com.jme3.renderer.Camera;
-import com.jme3.renderer.RenderManager;
-import com.jme3.renderer.Renderer;
-import com.jme3.renderer.ViewPort;
-import com.jme3.system.*;
-import com.jme3.system.JmeContext.Type;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.Future;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * The <code>LegacyApplication</code> class represents an instance of a real-time 3D rendering jME
- * application.
- *
- * An <code>LegacyApplication</code> provides all the tools that are commonly used in jME3
- * applications.
- *
- * jME3 applications *SHOULD NOT EXTEND* this class but extend
- * {@link com.jme3.app.SimpleApplication} instead.
- *
- */
-public class LegacyApplication implements Application, SystemListener {
-
-  private static final Logger logger = Logger.getLogger(LegacyApplication.class.getName());
-
-  protected AssetManager assetManager;
-
-  protected AudioRenderer audioRenderer;
-  protected Renderer renderer;
-  protected RenderManager renderManager;
-  protected ViewPort viewPort;
-  protected ViewPort guiViewPort;
-
-  protected JmeContext context;
-  protected AppSettings settings;
-  protected Timer timer = new NanoTimer();
-  protected Camera cam;
-  protected Listener listener;
-
-  protected boolean inputEnabled = true;
-  protected LostFocusBehavior lostFocusBehavior = LostFocusBehavior.ThrottleOnLostFocus;
-  protected float speed = 1f;
-  protected boolean paused = false;
-  protected MouseInput mouseInput;
-  protected KeyInput keyInput;
-  protected JoyInput joyInput;
-  protected TouchInput touchInput;
-  protected InputManager inputManager;
-  protected AppStateManager stateManager;
-
-  protected AppProfiler prof;
-
-  private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<>();
-
-  /**
-   * Create a new instance of <code>LegacyApplication</code>.
-   */
-  public LegacyApplication() {
-    this((AppState[]) null);
-  }
-
-  /**
-   * Create a new instance of <code>LegacyApplication</code>, preinitialized with the specified set
-   * of app states.
-   *
-   * @param initialStates app states to pre-attach, or null for none
-   */
-  public LegacyApplication(AppState... initialStates) {
-    initStateManager();
-
-    if (initialStates != null) {
-      for (AppState a : initialStates) {
-        if (a != null) {
-          stateManager.attach(a);
-        }
-      }
-    }
-  }
-
-  /**
-   * Determine the application's behavior when unfocused.
-   *
-   * @return The lost focus behavior of the application.
-   */
-  @Override
-  public LostFocusBehavior getLostFocusBehavior() {
-    return lostFocusBehavior;
-  }
-
-  /**
-   * Changes the application's behavior when unfocused.
-   *
-   * By default, the application will {@link LostFocusBehavior#ThrottleOnLostFocus throttle the
-   * update loop} so as not to use 100% of the CPU when out of focus, e.g. alt-tabbed, minimized, or
-   * hidden by another window.
-   *
-   * @param lostFocusBehavior The new lost focus behavior to use.
-   *
-   * @see LostFocusBehavior
-   */
-  @Override
-  public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior) {
-    this.lostFocusBehavior = lostFocusBehavior;
-  }
-
-  /**
-   * Returns true if pause on lost focus is enabled, false otherwise.
-   *
-   * @return true if pause on lost focus is enabled
-   *
-   * @see #getLostFocusBehavior()
-   */
-  @Override
-  public boolean isPauseOnLostFocus() {
-    return getLostFocusBehavior() == LostFocusBehavior.PauseOnLostFocus;
-  }
-
-  /**
-   * Enable or disable pause on lost focus.
-   * <p>
-   * By default, pause on lost focus is enabled. If enabled, the application will stop updating when
-   * it loses focus or becomes inactive (e.g. alt-tab). For online or real-time applications, this
-   * might be undesirable, so this feature should be disabled. For other applications, it is best to
-   * keep it enabled so the CPU is not used unnecessarily.
-   *
-   * @param pauseOnLostFocus True to enable pause on lost focus, false otherwise.
-   *
-   * @see #setLostFocusBehavior(com.jme3.app.LostFocusBehavior)
-   */
-  @Override
-  public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
-    if (pauseOnLostFocus) {
-      setLostFocusBehavior(LostFocusBehavior.PauseOnLostFocus);
-    } else {
-      setLostFocusBehavior(LostFocusBehavior.Disabled);
-    }
-  }
-
-  @Deprecated
-  public void setAssetManager(AssetManager assetManager) {
-    if (this.assetManager != null)
-      throw new IllegalStateException("Can only set asset manager" + " before initialization.");
-
-    this.assetManager = assetManager;
-  }
-
-  private void initAssetManager() {
-    URL assetCfgUrl = null;
-
-    if (settings != null) {
-      String assetCfg = settings.getString("AssetConfigURL");
-      if (assetCfg != null) {
-        try {
-          assetCfgUrl = new URL(assetCfg);
-        } catch (MalformedURLException ex) {
-        }
-        if (assetCfgUrl == null) {
-          assetCfgUrl = LegacyApplication.class.getClassLoader().getResource(assetCfg);
-          if (assetCfgUrl == null) {
-            logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}",
-                assetCfg);
-            return;
-          }
-        }
-      }
-    }
-    if (assetCfgUrl == null) {
-      assetCfgUrl = JmeSystem.getPlatformAssetConfigURL();
-    }
-    if (assetManager == null) {
-      assetManager = JmeSystem.newAssetManager(assetCfgUrl);
-    }
-  }
-
-  /**
-   * Set the display settings to define the display created.
-   * <p>
-   * Examples of display parameters include display pixel width and height, color bit depth,
-   * z-buffer bits, anti-aliasing samples, and update frequency. If this method is called while the
-   * application is already running, then {@link #restart() } must be called to apply the settings
-   * to the display.
-   *
-   * @param settings The settings to set.
-   */
-  @Override
-  public void setSettings(AppSettings settings) {
-    this.settings = settings;
-    if (context != null && settings.useInput() != inputEnabled) {
-      // may need to create or destroy input based
-      // on settings change
-      inputEnabled = !inputEnabled;
-      if (inputEnabled) {
-        initInput();
-      } else {
-        destroyInput();
-      }
-    } else {
-      inputEnabled = settings.useInput();
-    }
-  }
-
-  /**
-   * Sets the Timer implementation that will be used for calculating frame times. By default,
-   * Application will use the Timer as returned by the current JmeContext implementation.
-   */
-  @Override
-  public void setTimer(Timer timer) {
-    this.timer = timer;
-
-    if (timer != null) {
-      timer.reset();
-    }
-
-    if (renderManager != null) {
-      renderManager.setTimer(timer);
-    }
-  }
-
-  @Override
-  public Timer getTimer() {
-    return timer;
-  }
-
-  private void initDisplay() {
-    // acquire important objects
-    // from the context
-    settings = context.getSettings();
-
-    // Only reset the timer if a user has not already provided one
-    if (timer == null) {
-      timer = context.getTimer();
-    }
-
-    renderer = context.getRenderer();
-  }
-
-  private void initAudio() {
-    if (settings.getAudioRenderer() != null && context.getType() != Type.Headless) {
-      audioRenderer = JmeSystem.newAudioRenderer(settings);
-      audioRenderer.initialize();
-      AudioContext.setAudioRenderer(audioRenderer);
-
-      listener = new Listener();
-      audioRenderer.setListener(listener);
-    }
-  }
-
-  /**
-   * Creates the camera to use for rendering. Default values are perspective projection with 45°
-   * field of view, with near and far values 1 and 1000 units respectively.
-   */
-  private void initCamera() {
-    cam = new Camera(settings.getWidth(), settings.getHeight());
-
-    cam.setFrustumPerspective(45f, (float) cam.getWidth() / cam.getHeight(), 1f, 1000f);
-    cam.setLocation(new Vector3f(0f, 0f, 10f));
-    cam.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
-
-    renderManager = new RenderManager(renderer);
-    // Remy - 09/14/2010 set the timer in the renderManager
-    renderManager.setTimer(timer);
-
-    if (prof != null) {
-      renderManager.setAppProfiler(prof);
-    }
-
-    viewPort = renderManager.createMainView("Default", cam);
-    viewPort.setClearFlags(true, true, true);
-
-    // Create a new cam for the gui
-    Camera guiCam = new Camera(settings.getWidth(), settings.getHeight());
-    guiViewPort = renderManager.createPostView("Gui Default", guiCam);
-    guiViewPort.setClearFlags(false, false, false);
-  }
-
-  /**
-   * Initializes mouse and keyboard input. Also initializes joystick input if joysticks are enabled
-   * in the AppSettings.
-   */
-  private void initInput() {
-    mouseInput = context.getMouseInput();
-    if (mouseInput != null)
-      mouseInput.initialize();
-
-    keyInput = context.getKeyInput();
-    if (keyInput != null)
-      keyInput.initialize();
-
-    touchInput = context.getTouchInput();
-    if (touchInput != null)
-      touchInput.initialize();
-
-    if (settings.useJoysticks()) {
-      joyInput = context.getJoyInput();
-      if (joyInput != null)
-        joyInput.initialize();
-    }
-
-    inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput);
-  }
-
-  private void initStateManager() {
-    stateManager = new AppStateManager(this);
-
-    // Always register a ResetStatsState to make sure
-    // that the stats are cleared every frame
-    stateManager.attach(new ResetStatsState());
-  }
-
-  /**
-   * @return The {@link AssetManager asset manager} for this application.
-   */
-  @Override
-  public AssetManager getAssetManager() {
-    return assetManager;
-  }
-
-  /**
-   * @return the {@link InputManager input manager}.
-   */
-  @Override
-  public InputManager getInputManager() {
-    return inputManager;
-  }
-
-  /**
-   * @return the {@link AppStateManager app state manager}
-   */
-  @Override
-  public AppStateManager getStateManager() {
-    return stateManager;
-  }
-
-  /**
-   * @return the {@link RenderManager render manager}
-   */
-  @Override
-  public RenderManager getRenderManager() {
-    return renderManager;
-  }
-
-  /**
-   * @return The {@link Renderer renderer} for the application
-   */
-  @Override
-  public Renderer getRenderer() {
-    return renderer;
-  }
-
-  /**
-   * @return The {@link AudioRenderer audio renderer} for the application
-   */
-  @Override
-  public AudioRenderer getAudioRenderer() {
-    return audioRenderer;
-  }
-
-  /**
-   * @return The {@link Listener listener} object for audio
-   */
-  @Override
-  public Listener getListener() {
-    return listener;
-  }
-
-  /**
-   * @return The {@link JmeContext display context} for the application
-   */
-  @Override
-  public JmeContext getContext() {
-    return context;
-  }
-
-  /**
-   * @return The {@link Camera camera} for the application
-   */
-  @Override
-  public Camera getCamera() {
-    return cam;
-  }
-
-  /**
-   * Starts the application in {@link Type#Display display} mode.
-   *
-   * @see #start(com.jme3.system.JmeContext.Type)
-   */
-  @Override
-  public void start() {
-    start(JmeContext.Type.Display, false);
-  }
-
-  /**
-   * Starts the application in {@link Type#Display display} mode.
-   *
-   * @param waitFor true&rarr;wait for the context to be initialized, false&rarr;don't wait
-   * @see #start(com.jme3.system.JmeContext.Type)
-   */
-  @Override
-  public void start(boolean waitFor) {
-    start(JmeContext.Type.Display, waitFor);
-  }
-
-  /**
-   * Starts the application. Creating a rendering context and executing the main loop in a separate
-   * thread.
-   *
-   * @param contextType the type of context to create
-   */
-  public void start(JmeContext.Type contextType) {
-    start(contextType, false);
-  }
-
-  /**
-   * Starts the application. Creating a rendering context and executing the main loop in a separate
-   * thread.
-   *
-   * @param contextType the type of context to create
-   * @param waitFor true&rarr;wait for the context to be initialized, false&rarr;don't wait
-   */
-  public void start(JmeContext.Type contextType, boolean waitFor) {
-    if (context != null && context.isCreated()) {
-      logger.warning("start() called when application already created!");
-      return;
-    }
-
-    if (settings == null) {
-      settings = new AppSettings(true);
-    }
-
-    logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
-    context = JmeSystem.newContext(settings, contextType);
-    context.setSystemListener(this);
-    context.create(waitFor);
-  }
-
-  /**
-   * Sets an AppProfiler hook that will be called back for specific steps within a single update
-   * frame. Value defaults to null.
-   *
-   * @param prof the profiler to use (alias created) or null for none
-   */
-  @Override
-  public void setAppProfiler(AppProfiler prof) {
-    this.prof = prof;
-    if (renderManager != null) {
-      renderManager.setAppProfiler(prof);
-    }
-  }
-
-  /**
-   * Returns the current AppProfiler hook, or null if none is set.
-   */
-  @Override
-  public AppProfiler getAppProfiler() {
-    return prof;
-  }
-
-  /**
-   * Initializes the application's canvas for use.
-   * <p>
-   * After calling this method, cast the {@link #getContext()} context to JmeCanvasContext, then
-   * acquire the canvas with JmeCanvasContext.getCanvas() and attach it to an AWT/Swing Frame. The
-   * rendering thread will start when the canvas becomes visible on screen, however if you wish to
-   * start the context immediately you may call {@link #startCanvas() } to force the rendering
-   * thread to start.
-   *
-   * @see Type#Canvas
-   */
-  public void createCanvas() {
-    if (context != null && context.isCreated()) {
-      logger.warning("createCanvas() called when application already created!");
-      return;
-    }
-
-    if (settings == null) {
-      settings = new AppSettings(true);
-    }
-
-    if (logger.isLoggable(Level.FINE)) {
-      logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
-    }
-    context = JmeSystem.newContext(settings, JmeContext.Type.Canvas);
-    context.setSystemListener(this);
-  }
-
-  /**
-   * Starts the rendering thread after createCanvas() has been called.
-   * <p>
-   * Same as calling startCanvas(false)
-   *
-   * @see #startCanvas(boolean)
-   */
-  public void startCanvas() {
-    startCanvas(false);
-  }
-
-  /**
-   * Starts the rendering thread after createCanvas() has been called.
-   * <p>
-   * Calling this method is optional, the canvas will start automatically when it becomes visible.
-   *
-   * @param waitFor If true, the current thread will block until the rendering thread is running
-   */
-  public void startCanvas(boolean waitFor) {
-    context.create(waitFor);
-  }
-
-  /**
-   * Internal use only.
-   */
-  @Override
-  public void reshape(int w, int h) {
-    if (renderManager != null) {
-      renderManager.notifyReshape(w, h);
-    }
-  }
-
-
-  @Override
-  public void rescale(float x, float y) {
-    if (renderManager != null) {
-      renderManager.notifyRescale(x, y);
-    }
-  }
-
-  /**
-   * Restarts the context, applying any changed settings.
-   * <p>
-   * Changes to the {@link AppSettings} of this Application are not applied immediately; calling
-   * this method forces the context to restart, applying the new settings.
-   */
-  @Override
-  public void restart() {
-    context.setSettings(settings);
-    context.restart();
-  }
-
-  /**
-   * Requests the context to close, shutting down the main loop and making necessary cleanup
-   * operations.
-   *
-   * Same as calling stop(false)
-   *
-   * @see #stop(boolean)
-   */
-  @Override
-  public void stop() {
-    stop(false);
-  }
-
-  /**
-   * Requests the context to close, shutting down the main loop and making necessary cleanup
-   * operations. After the application has stopped, it cannot be used anymore.
-   *
-   * @param waitFor true&rarr;wait for the context to be fully destroyed, true&rarr;don't wait
-   */
-  @Override
-  public void stop(boolean waitFor) {
-    logger.log(Level.FINE, "Closing application: {0}", getClass().getName());
-    context.destroy(waitFor);
-  }
-
-  /**
-   * Do not call manually. Callback from ContextListener.
-   * <p>
-   * Initializes the <code>Application</code>, by creating a display and default camera. If display
-   * settings are not specified, a default 640x480 display is created. Default values are used for
-   * the camera; perspective projection with 45° field of view, with near and far values 1 and 1000
-   * units respectively.
-   */
-  @Override
-  public void initialize() {
-    if (assetManager == null) {
-      initAssetManager();
-    }
-
-    initDisplay();
-    initCamera();
-
-    if (inputEnabled) {
-      initInput();
-    }
-    initAudio();
-
-    // update timer so that the next delta is not too large
-    // timer.update();
-    timer.reset();
-
-    // user code here
-  }
-
-  /**
-   * Internal use only.
-   */
-  @Override
-  public void handleError(String errMsg, Throwable t) {
-    // Print error to log.
-    logger.log(Level.SEVERE, errMsg, t);
-    // Display error message on screen if not in headless mode
-    if (context.getType() != JmeContext.Type.Headless) {
-      if (t != null) {
-        JmeSystem.handleErrorMessage(errMsg + "\n" + t.getClass().getSimpleName()
-            + (t.getMessage() != null ? ": " + t.getMessage() : ""));
-      } else {
-        JmeSystem.handleErrorMessage(errMsg);
-      }
-    }
-
-    stop(); // stop the application
-  }
-
-  /**
-   * Internal use only.
-   */
-  @Override
-  public void gainFocus() {
-    if (lostFocusBehavior != LostFocusBehavior.Disabled) {
-      if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
-        paused = false;
-      }
-      context.setAutoFlushFrames(true);
-      if (inputManager != null) {
-        inputManager.reset();
-      }
-    }
-  }
-
-  /**
-   * Internal use only.
-   */
-  @Override
-  public void loseFocus() {
-    if (lostFocusBehavior != LostFocusBehavior.Disabled) {
-      if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
-        paused = true;
-      }
-      context.setAutoFlushFrames(false);
-    }
-  }
-
-  /**
-   * Internal use only.
-   */
-  @Override
-  public void requestClose(boolean esc) {
-    context.destroy(false);
-  }
-
-  /**
-   * Enqueues a task/callable object to execute in the jME3 rendering thread.
-   * <p>
-   * Callables are executed right at the beginning of the main loop. They are executed even if the
-   * application is currently paused or out of focus.
-   *
-   * @param <V> type of result returned by the Callable
-   * @param callable The callable to run in the main jME3 thread
-   * @return a new instance
-   */
-  @Override
-  public <V> Future<V> enqueue(Callable<V> callable) {
-    AppTask<V> task = new AppTask<>(callable);
-    taskQueue.add(task);
-    return task;
-  }
-
-  /**
-   * Enqueues a runnable object to execute in the jME3 rendering thread.
-   * <p>
-   * Runnables are executed right at the beginning of the main loop. They are executed even if the
-   * application is currently paused or out of focus.
-   *
-   * @param runnable The runnable to run in the main jME3 thread
-   */
-  @Override
-  @SuppressWarnings("unchecked")
-  public void enqueue(Runnable runnable) {
-    enqueue(new RunnableWrapper(runnable));
-  }
-
-  /**
-   * Runs tasks enqueued via {@link #enqueue(Callable)}
-   */
-  protected void runQueuedTasks() {
-    AppTask<?> task;
-    while ((task = taskQueue.poll()) != null) {
-      if (!task.isCancelled()) {
-        task.invoke();
-      }
-    }
-  }
-
-  /**
-   * Do not call manually. Callback from ContextListener.
-   */
-  @Override
-  public void update() {
-    // Make sure the audio renderer is available to callables
-    AudioContext.setAudioRenderer(audioRenderer);
-
-    if (prof != null)
-      prof.appStep(AppStep.QueuedTasks);
-    runQueuedTasks();
-
-    if (speed == 0 || paused)
-      return;
-
-    timer.update();
-
-    if (inputEnabled) {
-      if (prof != null)
-        prof.appStep(AppStep.ProcessInput);
-      inputManager.update(timer.getTimePerFrame());
-    }
-
-    if (audioRenderer != null) {
-      if (prof != null)
-        prof.appStep(AppStep.ProcessAudio);
-      audioRenderer.update(timer.getTimePerFrame());
-    }
-
-    // user code here
-  }
-
-  protected void destroyInput() {
-    if (mouseInput != null)
-      mouseInput.destroy();
-
-    if (keyInput != null)
-      keyInput.destroy();
-
-    if (joyInput != null)
-      joyInput.destroy();
-
-    if (touchInput != null)
-      touchInput.destroy();
-
-    inputManager = null;
-  }
-
-  /**
-   * Do not call manually. Callback from ContextListener.
-   */
-  @Override
-  public void destroy() {
-    stateManager.cleanup();
-
-    destroyInput();
-    if (audioRenderer != null)
-      audioRenderer.cleanup();
-
-    timer.reset();
-  }
-
-  /**
-   * @return The GUI viewport. Which is used for the on screen statistics and FPS.
-   */
-  @Override
-  public ViewPort getGuiViewPort() {
-    return guiViewPort;
-  }
-
-  @Override
-  public ViewPort getViewPort() {
-    return viewPort;
-  }
-
-  private class RunnableWrapper implements Callable {
-    private final Runnable runnable;
-
-    public RunnableWrapper(Runnable runnable) {
-      this.runnable = runnable;
-    }
-
-    @Override
-    public Object call() {
-      runnable.run();
-      return null;
-    }
-  }
-
-  /**
-   * This call will return a list of Monitors that glfwGetMonitors() returns and information about
-   * the monitor, like width, height, and refresh rate.
-   * 
-   * @return returns a list of monitors and their information.
-   */
-  public Monitors getMonitors() {
-    return context.getMonitors();
-  }
-
-  /**
-   * Use this to get the positional number of the primary monitor from the glfwGetMonitors()
-   * function call.
-   * 
-   * @return the position of the value in the arraylist of the primary monitor.
-   */
-  public int getPrimaryMonitor() {
-    return context.getPrimaryMonitor();
-  }
-}

+ 13 - 7
jme3-core/src/main/java/com/jme3/app/SimpleApplication.java

@@ -76,7 +76,7 @@ public abstract class SimpleApplication extends LegacyApplication {
     protected BitmapFont guiFont;
     protected FlyByCamera flyCam;
     protected boolean showSettings = true;
-    final private AppActionListener actionListener = new AppActionListener();
+    private final AppActionListener actionListener = new AppActionListener();
 
     private class AppActionListener implements ActionListener {
 
@@ -242,8 +242,9 @@ public abstract class SimpleApplication extends LegacyApplication {
 
     @Override
     public void update() {
-        if (prof != null)
+        if (prof != null) {
             prof.appStep(AppStep.BeginFrame);
+        }
 
         super.update(); // makes sure to execute AppTasks
         if (speed == 0 || paused) {
@@ -253,15 +254,17 @@ public abstract class SimpleApplication extends LegacyApplication {
         float tpf = timer.getTimePerFrame() * speed;
 
         // update states
-        if (prof != null)
+        if (prof != null) {
             prof.appStep(AppStep.StateManagerUpdate);
+        }
         stateManager.update(tpf);
 
         // simple update and root node
         simpleUpdate(tpf);
 
-        if (prof != null)
+        if (prof != null) {
             prof.appStep(AppStep.SpatialUpdate);
+        }
         rootNode.updateLogicalState(tpf);
         guiNode.updateLogicalState(tpf);
 
@@ -269,18 +272,21 @@ public abstract class SimpleApplication extends LegacyApplication {
         guiNode.updateGeometricState();
 
         // render states
-        if (prof != null)
+        if (prof != null) {
             prof.appStep(AppStep.StateManagerRender);
+        }
         stateManager.render(renderManager);
 
-        if (prof != null)
+        if (prof != null) {
             prof.appStep(AppStep.RenderFrame);
+        }
         renderManager.render(tpf, context.isRenderable());
         simpleRender(renderManager);
         stateManager.postRender();
 
-        if (prof != null)
+        if (prof != null) {
             prof.appStep(AppStep.EndFrame);
+        }
     }
 
     public void setDisplayFps(boolean show) {

+ 6 - 5
jme3-core/src/main/java/com/jme3/app/StatsView.java

@@ -60,11 +60,11 @@ import com.jme3.util.clone.JmeCloneable;
  * </pre>
  */
 public class StatsView extends Node implements Control, JmeCloneable {
-    final private BitmapText statText;
-    final private Statistics statistics;
+    private final BitmapText statText;
+    private final Statistics statistics;
 
-    final private String[] statLabels;
-    final private int[] statData;
+    private final String[] statLabels;
+    private final int[] statData;
 
     private boolean enabled = true;
 
@@ -96,8 +96,9 @@ public class StatsView extends Node implements Control, JmeCloneable {
 
     @Override
     public void update(float tpf) {
-        if (!isEnabled())
+        if (!isEnabled()) {
             return;
+        }
 
         statistics.getData(statData);
         stringBuilder.setLength(0);

+ 3 - 1
jme3-core/src/main/java/com/jme3/asset/AssetConfig.java

@@ -39,6 +39,8 @@ import java.util.Scanner;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.jme3.util.res.Resources;
+
 /**
  * <code>AssetConfig</code> loads a config file to configure the asset manager.
  *
@@ -101,7 +103,7 @@ public final class AssetConfig {
                     }
                 } else if (cmd.equals("INCLUDE")) {
                     String includedCfg = scan.nextLine().trim();
-                    URL includedCfgUrl = Thread.currentThread().getContextClassLoader().getResource(includedCfg);
+                    URL includedCfgUrl = Resources.getResource(includedCfg);
                     if (includedCfgUrl != null) {
                         loadText(assetManager, includedCfgUrl);
                     } else {

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

@@ -46,6 +46,7 @@ import com.jme3.texture.Texture;
 import com.jme3.texture.plugins.TGALoader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
 
@@ -86,7 +87,7 @@ import java.util.List;
  * so that modifications to one instance do not leak onto others.
  */
 public interface AssetManager {
-
+    
     /**
      * Adds a {@link ClassLoader} that is used to load {@link Class classes}
      * that are needed for finding and loading Assets. 
@@ -94,23 +95,35 @@ public interface AssetManager {
      * use registerLocator for that.
      * 
      * @param loader A ClassLoader that Classes in asset files can be loaded from.
+     * @deprecated use {@link com.jme3.util.res.Resources}
      */
-    public void addClassLoader(ClassLoader loader);
+    @Deprecated
+    public default void addClassLoader(ClassLoader loader) {
+        
+    }
 
     /**
      * Remove a {@link ClassLoader} from the list of registered ClassLoaders
      * 
      * @param loader the ClassLoader to be removed
+     * @deprecated use {@link com.jme3.util.res.Resources}
      */
-    public void removeClassLoader(ClassLoader loader);
+    @Deprecated
+    public default void removeClassLoader(ClassLoader loader) {
+        
+    }
 
     /**
      * Retrieve the list of registered ClassLoaders that are used for loading 
      * {@link Class classes} from asset files.
      * 
      * @return an unmodifiable list
+    * @deprecated use {@link com.jme3.util.res.Resources}
      */
-    public List<ClassLoader> getClassLoaders();
+    @Deprecated
+    public default List<ClassLoader> getClassLoaders() {
+        return new ArrayList<>();
+    }
     
     /**
      * Register an {@link AssetLoader} by using a class object.

+ 5 - 3
jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java

@@ -73,8 +73,8 @@ public class DesktopAssetManager implements AssetManager {
     final private CopyOnWriteArrayList<AssetEventListener> eventListeners =
             new CopyOnWriteArrayList<>();
 
-    final private List<ClassLoader> classLoaders =
-            Collections.synchronizedList(new ArrayList<>());
+    @Deprecated
+    final private List<ClassLoader> classLoaders = Collections.synchronizedList(new ArrayList<>());
 
     public DesktopAssetManager() {
         this(null);
@@ -99,21 +99,23 @@ public class DesktopAssetManager implements AssetManager {
         }
     }
 
+    @Deprecated
     @Override
     public void addClassLoader(ClassLoader loader) {
         classLoaders.add(loader);
     }
 
+    @Deprecated
     @Override
     public void removeClassLoader(ClassLoader loader) {
         classLoaders.remove(loader);
     }
 
+    @Deprecated
     @Override
     public List<ClassLoader> getClassLoaders() {
         return Collections.unmodifiableList(classLoaders);
     }
-
     @Override
     public void addAssetEventListener(AssetEventListener listener) {
         eventListeners.add(listener);

+ 25 - 0
jme3-core/src/main/java/com/jme3/audio/Environment.java

@@ -252,4 +252,29 @@ public class Environment {
     public void setRoomRolloffFactor(float roomRolloffFactor) {
         this.roomRolloffFactor = roomRolloffFactor;
     }
+
+    @Override
+    public boolean equals(Object env2) {
+        if (env2 == null)
+            return false;
+        if (env2 == this)
+            return true;
+        if (!(env2 instanceof Environment))
+            return false;
+
+        Environment e2 = (Environment) env2;
+        return (e2.airAbsorbGainHf == airAbsorbGainHf
+                && e2.decayHFRatio == decayHFRatio
+                && e2.decayHfLimit == decayHfLimit
+                && e2.decayTime == decayTime
+                && e2.density == density
+                && e2.diffusion == diffusion
+                && e2.gain == gain
+                && e2.gainHf == gainHf
+                && e2.lateReverbDelay == lateReverbDelay
+                && e2.lateReverbGain == lateReverbGain
+                && e2.reflectDelay == reflectDelay
+                && e2.reflectGain == reflectGain
+                && e2.roomRolloffFactor == roomRolloffFactor);
+    } 
 }

+ 19 - 4
jme3-core/src/main/java/com/jme3/export/JmeExporter.java

@@ -51,14 +51,29 @@ public interface JmeExporter {
     public void save(Savable object, OutputStream f) throws IOException;
     
     /**
-     * Export the {@link Savable} to a file.
-     * 
+     * Export the {@link Savable} to a file. If the path to the file doesn't exist, the directories are
+     * made.
+     *
      * @param object The savable to export
      * @param f The file to export to
      * @throws IOException If an io exception occurs during export
      */
-    public void save(Savable object, File f) throws IOException;
-    
+    default void save(Savable object, File f) throws IOException {
+        save(object, f, true);
+    }
+
+    /**
+     * Export the {@link Savable} to a file. If the path to the file doesn't exist, the parent
+     * directories can be created if the <code>createDirectories</code> flag is true. If the path does
+     * not exist and <code>createDirectories</code> is false, then an exception is thrown.
+     *
+     * @param object The savable to export
+     * @param f The file to export to
+     * @param createDirectories flag to indicate if the directories should be created
+     * @throws IOException If an io exception occurs during export
+     */
+    public void save(Savable object, File f, boolean createDirectories) throws IOException;
+
     /**
      * Returns the {@link OutputCapsule} for the given savable object.
      * 

+ 4 - 0
jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java

@@ -201,6 +201,10 @@ public class SavableClassUtil {
         }
     }
 
+    /**
+     * @deprecated use {@link #fromName(java.lang.String)} instead 
+     */
+    @Deprecated
     public static Savable fromName(String className, List<ClassLoader> loaders) throws InstantiationException,
             InvocationTargetException, NoSuchMethodException,
             IllegalAccessException, ClassNotFoundException, IOException {

+ 5 - 3
jme3-core/src/main/java/com/jme3/input/JoystickCompatibilityMappings.java

@@ -44,6 +44,8 @@ import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import com.jme3.util.res.Resources;
+
 
 /**
  * Provides compatibility mapping to different joysticks
@@ -554,9 +556,9 @@ public class JoystickCompatibilityMappings {
         }
     }
 
-    protected static void loadMappings(ClassLoader cl, String path) throws IOException {
+    protected static void loadMappings(String path) throws IOException {
         logger.log(Level.FINE, "Searching for mappings for path:{0}", path);
-        for (Enumeration<URL> en = cl.getResources(path); en.hasMoreElements(); ) {
+        for (Enumeration<URL> en = Resources.getResources(path); en.hasMoreElements(); ) {
             URL u = en.nextElement();
             try {
                 loadMappingProperties(u);
@@ -574,7 +576,7 @@ public class JoystickCompatibilityMappings {
     protected static void loadDefaultMappings() {
         for (String s : searchPaths) {
             try {
-                loadMappings(JoystickCompatibilityMappings.class.getClassLoader(), s);
+                loadMappings(s);
             } catch (IOException e) {
                 logger.log(Level.SEVERE, "Error searching resource path:{0}", s);
             }

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

@@ -165,6 +165,11 @@ public class PointLight extends Light {
         if (radius < 0) {
             throw new IllegalArgumentException("Light radius cannot be negative");
         }
+        
+        if (radius == Float.POSITIVE_INFINITY) {
+            radius = Float.MAX_VALUE;
+        }
+        
         this.radius = radius;
         if (radius != 0f) {
             this.invRadius = 1f / radius;

+ 5 - 3
jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java

@@ -149,10 +149,12 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
         fsQuad.setHeight(1);
 
         if (!renderer.getCaps().contains(Caps.PackedFloatTexture)) {
-            if (!renderer.getCaps().contains(Caps.FloatTexture)) {
-                fbFormat = Format.RGB8;
-            } else {
+            if(renderer.getCaps().contains(Caps.FloatColorBufferRGB)){
                 fbFormat = Format.RGB16F;
+            } else if(renderer.getCaps().contains(Caps.FloatColorBufferRGBA)){
+                fbFormat = Format.RGBA16F;
+            } else {
+                fbFormat = Format.RGB8;
             }
         }
 

+ 4 - 2
jme3-core/src/main/java/com/jme3/post/HDRRenderer.java

@@ -91,7 +91,7 @@ public class HDRRenderer implements SceneProcessor {
     private float whiteLevel = 100f;
     private float throttle = -1;
     private int maxIterations = -1;
-    private Image.Format bufFormat = Format.RGB16F;
+    private Image.Format bufFormat = Format.RGB8;
 
     private MinFilter fbMinFilter = MinFilter.BilinearNoMipMaps;
     private MagFilter fbMagFilter = MagFilter.Bilinear;
@@ -106,8 +106,10 @@ public class HDRRenderer implements SceneProcessor {
         Collection<Caps> caps = renderer.getCaps();
         if (caps.contains(Caps.PackedFloatColorBuffer))
             bufFormat = Format.RGB111110F;
-        else if (caps.contains(Caps.FloatColorBuffer))
+        else if (caps.contains(Caps.FloatColorBufferRGB))
             bufFormat = Format.RGB16F;
+        else if (caps.contains(Caps.FloatColorBufferRGBA))
+            bufFormat = Format.RGBA16F;
         else {
             enabled = false;
             return;

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

@@ -227,6 +227,16 @@ public enum Caps {
      */
     FloatTexture,
 
+    /**
+     * Supports rendering on RGB floating point textures
+     */
+    FloatColorBufferRGB,
+
+    /**
+     * Supports rendering on RGBA floating point textures
+     */
+    FloatColorBufferRGBA,
+
     /**
      * Supports integer textures.
      */
@@ -236,6 +246,7 @@ public enum Caps {
      * Supports floating point FBO color buffers (Format.RGB16F).
      */
     FloatColorBuffer,
+    
 
     /**
      * Supports floating point depth buffer.
@@ -341,6 +352,11 @@ public enum Caps {
      */
     OpenGLES20,
 
+    /**
+     * Supports WebGL
+     */
+    WebGL,
+
     /**
      * Supports RGB8 / RGBA8 textures.
      */

+ 39 - 1
jme3-core/src/main/java/com/jme3/renderer/RenderManager.java

@@ -58,8 +58,10 @@ import com.jme3.scene.VertexBuffer;
 import com.jme3.shader.Shader;
 import com.jme3.shader.UniformBinding;
 import com.jme3.shader.UniformBindingManager;
+import com.jme3.shader.VarType;
 import com.jme3.system.NullRenderer;
 import com.jme3.system.Timer;
+import com.jme3.texture.FrameBuffer;
 import com.jme3.util.SafeArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -101,6 +103,7 @@ public class RenderManager {
     private LightFilter lightFilter = new DefaultLightFilter();
     private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass;
     private int singlePassLightBatchSize = 1;
+    private MatParamOverride boundDrawBufferId=new MatParamOverride(VarType.Int,"BoundDrawBuffer",0);
 
 
     /**
@@ -111,6 +114,7 @@ public class RenderManager {
      */
     public RenderManager(Renderer renderer) {
         this.renderer = renderer;
+        this.forcedOverrides.add(boundDrawBufferId);
     }
 
     /**
@@ -629,6 +633,12 @@ public class RenderManager {
             setWorldMatrix(geom.getWorldMatrix());
         }
 
+        // Use material override to pass the current target index (used in api such as GL ES that do not support glDrawBuffer)
+        FrameBuffer currentFb = this.renderer.getCurrentFrameBuffer();
+        if (currentFb != null && !currentFb.isMultiTarget()) {
+            this.boundDrawBufferId.setValue(currentFb.getTargetIndex());
+        }
+
         // Perform light filtering if we have a light filter.
         LightList lightList = geom.getWorldLightList();
 
@@ -639,7 +649,7 @@ public class RenderManager {
         }
 
         Material material = geom.getMaterial();
-
+        
         // If forcedTechnique exists, we try to force it for the render.
         // If it does not exist in the mat def, we check for forcedMaterial and render the geom if not null.
         // Otherwise, the geometry is not rendered.
@@ -1298,4 +1308,32 @@ public class RenderManager {
             }
         }
     }
+
+
+    /**
+     * Returns true if the draw buffer target id is passed to the shader.
+     * 
+     * @return True if the draw buffer target id is passed to the shaders.
+     */
+    public boolean getPassDrawBufferTargetIdToShaders() {
+        return this.forcedOverrides.contains(boundDrawBufferId);
+    }
+
+    /**
+     * Enable or disable passing the draw buffer target id to the shaders. This
+     * is needed to handle FrameBuffer.setTargetIndex correctly in some
+     * backends.
+     * 
+     * @param v
+     *            True to enable, false to disable (default is true)
+     */
+    public void setPassDrawBufferTargetIdToShaders(boolean v) {
+        if (v) {
+            if (!this.forcedOverrides.contains(boundDrawBufferId)) {
+                this.forcedOverrides.add(boundDrawBufferId);
+            }
+        } else {
+            this.forcedOverrides.remove(boundDrawBufferId);
+        }
+    }
 }

+ 6 - 0
jme3-core/src/main/java/com/jme3/renderer/Renderer.java

@@ -527,4 +527,10 @@ public interface Renderer {
  
     }
     
+    /**
+     * Returns the current FrameBuffer that is being rendered to.
+     * @return the FrameBuffer or null if rendering to the screen.
+     */
+    public FrameBuffer getCurrentFrameBuffer();
+
 }

+ 7 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GLES_30.java

@@ -31,6 +31,8 @@
  */
 package com.jme3.renderer.opengl;
 
+import java.nio.IntBuffer;
+
 /**
  * GL functions and constants only available on vanilla OpenGL ES 3.0.
  *
@@ -40,5 +42,10 @@ public interface GLES_30 extends GL {
 
     public static final int GL_RGB10_A2 = 0x8059;
     public static final int GL_UNSIGNED_INT_2_10_10_10_REV = 0x8368;
+ 
+    public void glBindVertexArray(int array);
 
+    public void glDeleteVertexArrays(IntBuffer arrays);
+   
+    public void glGenVertexArrays(IntBuffer arrays);
 }

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

@@ -121,7 +121,7 @@ public final class GLImageFormats {
             formatSrgbSwiz(formatToGL, Format.Luminance8Alpha8,     GLExt.GL_SRGB8_ALPHA8_EXT, GL3.GL_RG,       GL.GL_UNSIGNED_BYTE);
         }
         
-        if (caps.contains(Caps.OpenGL20)) {
+        if (caps.contains(Caps.OpenGL20)||caps.contains(Caps.OpenGLES30)) {
             if (!caps.contains(Caps.CoreProfile)) {
                 format(formatToGL, Format.Alpha8,           GL2.GL_ALPHA8,            GL.GL_ALPHA,           GL.GL_UNSIGNED_BYTE);
                 format(formatToGL, Format.Luminance8,       GL2.GL_LUMINANCE8,        GL.GL_LUMINANCE,       GL.GL_UNSIGNED_BYTE);

+ 92 - 42
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -187,7 +187,12 @@ public final class GLRenderer implements Renderer {
         return extensionSet;
     }
 
+    public static boolean isWebGL(String version) {
+        return version.contains("WebGL");
+    }
+
     public static int extractVersion(String version) {
+        if (version.startsWith("WebGL 2.0")) return 300;
         Matcher m = GLVERSION_PATTERN.matcher(version);
         if (m.matches()) {
             int major = Integer.parseInt(m.group(1));
@@ -208,7 +213,11 @@ public final class GLRenderer implements Renderer {
     }
 
     private void loadCapabilitiesES() {
-        int oglVer = extractVersion(gl.glGetString(GL.GL_VERSION));
+        String version = gl.glGetString(GL.GL_VERSION);
+        int oglVer = extractVersion(version);
+        if (isWebGL(version)) {
+            caps.add(Caps.WebGL);       
+        }
         caps.add(Caps.GLSL100);
         caps.add(Caps.OpenGLES20);
 
@@ -378,7 +387,7 @@ public final class GLRenderer implements Renderer {
                     hasExtension("GL_ARB_half_float_pixel");
 
             if (!hasFloatTexture) {
-                hasFloatTexture = caps.contains(Caps.OpenGL30);
+                hasFloatTexture = caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30);
             }
         }
 
@@ -409,9 +418,14 @@ public final class GLRenderer implements Renderer {
         }
 
         if (hasExtension("GL_ARB_color_buffer_float") &&
-                hasExtension("GL_ARB_half_float_pixel")) {
+                hasExtension("GL_ARB_half_float_pixel")
+                ||caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30)) {
             // XXX: Require both 16- and 32-bit float support for FloatColorBuffer.
             caps.add(Caps.FloatColorBuffer);
+            caps.add(Caps.FloatColorBufferRGBA);
+            if (!caps.contains(Caps.OpenGLES30)) {
+                caps.add(Caps.FloatColorBufferRGB);
+            }
         }
 
         if (caps.contains(Caps.OpenGLES30) || hasExtension("GL_ARB_depth_buffer_float")) {
@@ -450,13 +464,13 @@ public final class GLRenderer implements Renderer {
 
         // == end texture format extensions ==
 
-        if (hasExtension("GL_ARB_vertex_array_object") || caps.contains(Caps.OpenGL30)) {
+        if (hasExtension("GL_ARB_vertex_array_object") || caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30) ) {
             caps.add(Caps.VertexBufferArray);
         }
 
         if (hasExtension("GL_ARB_texture_non_power_of_two") ||
                 hasExtension("GL_OES_texture_npot") ||
-                caps.contains(Caps.OpenGL30)) {
+                caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30)) {
             caps.add(Caps.NonPowerOfTwoTextures);
         } else {
             logger.log(Level.WARNING, "Your graphics card does not "
@@ -530,7 +544,7 @@ public final class GLRenderer implements Renderer {
 
         // Supports sRGB pipeline.
         if ( (hasExtension("GL_ARB_framebuffer_sRGB") && hasExtension("GL_EXT_texture_sRGB"))
-                || caps.contains(Caps.OpenGL30)) {
+                || caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30)) {
             caps.add(Caps.Srgb);
         }
 
@@ -539,8 +553,10 @@ public final class GLRenderer implements Renderer {
             caps.add(Caps.SeamlessCubemap);
         }
 
-        if (caps.contains(Caps.OpenGL32) && !hasExtension("GL_ARB_compatibility")) {
-            caps.add(Caps.CoreProfile);
+        if ((caps.contains(Caps.OpenGLES30) || caps.contains(Caps.OpenGL32)) && !hasExtension("GL_ARB_compatibility")) {
+            if (JmeSystem.getPlatform().getOs() != Platform.Os.iOS) { // some features are not supported on iOS
+                caps.add(Caps.CoreProfile);
+            }
         }
 
         if (hasExtension("GL_ARB_get_program_binary")) {
@@ -679,9 +695,17 @@ public final class GLRenderer implements Renderer {
 
         if (caps.contains(Caps.CoreProfile)) {
             // Core Profile requires VAO to be bound.
-            gl3.glGenVertexArrays(intBuf16);
-            int vaoId = intBuf16.get(0);
-            gl3.glBindVertexArray(vaoId);
+            if(gl3!=null){
+                gl3.glGenVertexArrays(intBuf16);
+                int vaoId = intBuf16.get(0);
+                gl3.glBindVertexArray(vaoId);
+            }else if(gl instanceof GLES_30){
+                ((GLES_30)gl).glGenVertexArrays(intBuf16);
+                int vaoId = intBuf16.get(0);
+                ((GLES_30)gl).glBindVertexArray(vaoId);                
+            }   else{
+                throw new UnsupportedOperationException("Core profile not supported");
+            }
         }
         if (gl2 != null && !(gl instanceof GLES_30)) {
             gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
@@ -1247,6 +1271,37 @@ public final class GLRenderer implements Renderer {
         }
     }
 
+    private boolean isValidNumber(float v) {
+        return !Float.isNaN(v);
+    }
+
+    private boolean isValidNumber(FloatBuffer fb) {
+        for(int i = 0; i < fb.limit(); i++) {
+            if (!isValidNumber(fb.get(i))) return false;
+        }
+        return true;
+    }
+    
+    private boolean isValidNumber(Vector2f v) {
+        return isValidNumber(v.x) && isValidNumber(v.y);
+    }
+
+    private boolean isValidNumber(Vector3f v) {
+        return isValidNumber(v.x) && isValidNumber(v.y) && isValidNumber(v.z);
+    }
+
+    private boolean isValidNumber(Quaternion q) {
+        return isValidNumber(q.getX()) && isValidNumber(q.getY()) && isValidNumber(q.getZ()) && isValidNumber(q.getW());
+    }
+
+    private boolean isValidNumber(ColorRGBA c) {
+        return isValidNumber(c.r) && isValidNumber(c.g) && isValidNumber(c.b) && isValidNumber(c.a);
+    }
+
+    private boolean isValidNumber(Vector4f c) {
+        return isValidNumber(c.x) && isValidNumber(c.y) && isValidNumber(c.z) && isValidNumber(c.w);
+    }
+
     protected void updateUniform(Shader shader, Uniform uniform) {
         int shaderId = shader.getId();
 
@@ -1282,26 +1337,32 @@ public final class GLRenderer implements Renderer {
         switch (uniform.getVarType()) {
             case Float:
                 Float f = (Float) uniform.getValue();
+                assert isValidNumber(f) : "Invalid float value " + f;
                 gl.glUniform1f(loc, f.floatValue());
                 break;
             case Vector2:
                 Vector2f v2 = (Vector2f) uniform.getValue();
+                assert isValidNumber(v2) : "Invalid Vector2f value " + v2;
                 gl.glUniform2f(loc, v2.getX(), v2.getY());
                 break;
             case Vector3:
                 Vector3f v3 = (Vector3f) uniform.getValue();
+                assert isValidNumber(v3) : "Invalid Vector3f value " + v3;
                 gl.glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ());
                 break;
             case Vector4:
                 Object val = uniform.getValue();
                 if (val instanceof ColorRGBA) {
                     ColorRGBA c = (ColorRGBA) val;
+                    assert isValidNumber(c) : "Invalid ColorRGBA value " + c;
                     gl.glUniform4f(loc, c.r, c.g, c.b, c.a);
                 } else if (val instanceof Vector4f) {
                     Vector4f c = (Vector4f) val;
+                    assert isValidNumber(c) : "Invalid Vector4f value " + c;
                     gl.glUniform4f(loc, c.x, c.y, c.z, c.w);
                 } else {
                     Quaternion c = (Quaternion) uniform.getValue();
+                    assert isValidNumber(c) : "Invalid Quaternion value " + c;
                     gl.glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW());
                 }
                 break;
@@ -1311,11 +1372,13 @@ public final class GLRenderer implements Renderer {
                 break;
             case Matrix3:
                 fb = uniform.getMultiData();
+                assert isValidNumber(fb) : "Invalid Matrix3f value " + uniform.getValue();
                 assert fb.remaining() == 9;
                 gl.glUniformMatrix3(loc, false, fb);
                 break;
             case Matrix4:
                 fb = uniform.getMultiData();
+                assert isValidNumber(fb) : "Invalid Matrix4f value " + uniform.getValue();
                 assert fb.remaining() == 16;
                 gl.glUniformMatrix4(loc, false, fb);
                 break;
@@ -1325,22 +1388,27 @@ public final class GLRenderer implements Renderer {
                 break;
             case FloatArray:
                 fb = uniform.getMultiData();
+                assert isValidNumber(fb) : "Invalid float array value " + Arrays.asList((float[]) uniform.getValue());
                 gl.glUniform1(loc, fb);
                 break;
             case Vector2Array:
                 fb = uniform.getMultiData();
+                assert isValidNumber(fb) : "Invalid Vector2f array value " + Arrays.deepToString((Vector2f[]) uniform.getValue());
                 gl.glUniform2(loc, fb);
                 break;
             case Vector3Array:
                 fb = uniform.getMultiData();
+                assert isValidNumber(fb) : "Invalid Vector3f array value " + Arrays.deepToString((Vector3f[]) uniform.getValue());
                 gl.glUniform3(loc, fb);
                 break;
             case Vector4Array:
                 fb = uniform.getMultiData();
+                assert isValidNumber(fb) : "Invalid Vector4f array value " + Arrays.deepToString((Vector4f[]) uniform.getValue());
                 gl.glUniform4(loc, fb);
                 break;
             case Matrix4Array:
                 fb = uniform.getMultiData();
+                assert isValidNumber(fb) : "Invalid Matrix4f array value " + Arrays.deepToString((Matrix4f[]) uniform.getValue());
                 gl.glUniformMatrix4(loc, false, fb);
                 break;
             case Int:
@@ -1477,7 +1545,6 @@ public final class GLRenderer implements Renderer {
                     + "Only GLSL 1.00 shaders are supported.");
         }
 
-        boolean insertPrecision = false;
         // Upload shader source.
         // Merge the defines and source code.
         stringBuf.setLength(0);
@@ -1508,16 +1575,8 @@ public final class GLRenderer implements Renderer {
                 }
             }
 
-            if (gles2 || gles3) {
-                //Inserting precision only to fragment shaders creates some link failures because of different precision between shaders
-                //But adding the precision to all shaders generates rendering glitches in some devices if not set to highp
-                if (source.getType() == ShaderType.Fragment) {
-                    // GLES requires precision qualifier.
-                    insertPrecision = true;
-                }
-            }
         }
-
+        
         if (linearizeSrgbImages) {
             stringBuf.append("#define SRGB 1\n");
         }
@@ -1526,24 +1585,6 @@ public final class GLRenderer implements Renderer {
         stringBuf.append(source.getDefines());
         stringBuf.append(source.getSource());
 
-        if(insertPrecision){
-            // default precision could be defined in GLSLCompat.glsllib so final users can use custom defined precision instead
-            // precision token is not a preprocessor directive therefore it must be placed after #extension tokens to avoid
-            // Error P0001: Extension directive must occur before any non-preprocessor tokens
-            int idx = stringBuf.lastIndexOf("#extension");
-            idx = stringBuf.indexOf("\n", idx);
-
-            if(version>=310) {
-                stringBuf.insert(idx + 1, "precision highp sampler2DMS;\n");
-            }
-            if(version>=300) {
-                stringBuf.insert(idx + 1, "precision highp sampler2DArray;\n");
-                stringBuf.insert(idx + 1, "precision highp sampler2DShadow;\n");
-                stringBuf.insert(idx + 1, "precision highp sampler3D;\n");
-                stringBuf.insert(idx + 1, "precision highp sampler2D;\n");
-            }
-            stringBuf.insert(idx + 1, "precision highp float;\n");
-        }
 
         intBuf1.clear();
         intBuf1.put(0, stringBuf.length());
@@ -1668,7 +1709,7 @@ public final class GLRenderer implements Renderer {
     public void setShader(Shader shader) {
         if (shader == null) {
             throw new IllegalArgumentException("Shader cannot be null");
-        } else {
+        } else {            
             if (shader.isUpdateNeeded()) {
                 updateShaderData(shader);
             }
@@ -2029,8 +2070,17 @@ public final class GLRenderer implements Renderer {
         mainFbOverride = fb;
     }
 
+
+    @Override
+    public FrameBuffer getCurrentFrameBuffer() {
+        if(mainFbOverride!=null){
+            return mainFbOverride;
+        }
+        return context.boundFB;
+    }
+
     public void setReadDrawBuffers(FrameBuffer fb) {
-        if (gl2 == null || gl instanceof GLES_30) {
+        if (gl2 == null) {
             return;
         }
 
@@ -2635,7 +2685,7 @@ public final class GLRenderer implements Renderer {
         if (unit < 0 || unit >= RenderContext.maxTextureUnits) {
             throw new TextureUnitException();
         }
-
+        
         Image image = tex.getImage();
         if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) {
             // Check NPOT requirements

+ 14 - 0
jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java

@@ -75,6 +75,7 @@ public class InstancedGeometry extends Geometry {
 
     private int firstUnusedIndex = 0;
     private int numVisibleInstances = 0;
+    private Camera cam;
 
     public InstancedGeometry() {
         super();
@@ -275,6 +276,13 @@ public class InstancedGeometry extends Geometry {
         }
     }
 
+    /**
+     * @Deprecated use {@link #updateInstances(com.jme3.renderer.Camera)
+     */
+    public void updateInstances() {
+        updateInstances(cam);
+    }
+
     public void updateInstances(Camera cam) {
         FloatBuffer fb = (FloatBuffer) transformInstanceData.getData();
         fb.limit(fb.capacity());
@@ -415,6 +423,12 @@ public class InstancedGeometry extends Geometry {
         allInstanceData = allData.toArray(new VertexBuffer[allData.size()]);
     }
 
+    @Override
+    public boolean checkCulling(Camera cam) {
+        this.cam = cam;
+        return super.checkCulling(cam);
+    }
+
     @Override
     public int collideWith(Collidable other, CollisionResults results) {
         return 0; // Ignore collision

+ 2 - 2
jme3-core/src/main/java/com/jme3/shader/ShaderNodeDefinition.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2021 jMonkeyEngine
+ * Copyright (c) 2009-2023 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -201,7 +201,7 @@ public class ShaderNodeDefinition implements Savable {
         oc.write(shadersPath.toArray(str), "shadersPath", null);
         oc.write(type, "type", null);
         oc.writeSavableArrayList((ArrayList) inputs, "inputs", new ArrayList<ShaderNodeVariable>());
-        oc.writeSavableArrayList((ArrayList) outputs, "inputs", new ArrayList<ShaderNodeVariable>());
+        oc.writeSavableArrayList((ArrayList) outputs, "outputs", new ArrayList<ShaderNodeVariable>());
     }
 
     public List<String> getShadersLanguage() {

+ 2 - 2
jme3-core/src/main/java/com/jme3/system/AppSettings.java

@@ -876,12 +876,12 @@ public final class AppSettings extends HashMap<String, Object> {
     }
 
     /**
-     * Set the number of samples per pixel. A value of 1 indicates
+     * Set the number of samples per pixel. A value of 0 or 1 indicates
      * each pixel should be single-sampled, higher values indicate
      * a pixel should be multi-sampled.
      *
      * @param value The number of samples
-     * (Default: 1)
+     * (Default: 0)
      */
     public void setSamples(int value) {
         putInteger("Samples", value);

+ 3 - 2
jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java

@@ -35,6 +35,7 @@ import com.jme3.asset.AssetManager;
 import com.jme3.asset.DesktopAssetManager;
 import com.jme3.audio.AudioRenderer;
 import com.jme3.input.SoftTextDialogInput;
+import com.jme3.util.res.Resources;
 
 import java.io.File;
 import java.io.IOException;
@@ -126,11 +127,11 @@ public abstract class JmeSystemDelegate {
     }
 
     public InputStream getResourceAsStream(String name) {
-        return this.getClass().getResourceAsStream(name);
+        return Resources.getResourceAsStream(name,this.getClass());
     }
 
     public URL getResource(String name) {
-        return this.getClass().getResource(name);
+        return Resources.getResource(name,this.getClass());
     }
 
     public boolean trackDirectMemory() {

+ 3 - 1
jme3-core/src/main/java/com/jme3/system/JmeVersion.java

@@ -36,6 +36,8 @@ import java.util.Properties;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.jme3.util.res.Resources;
+
 /**
  * Pulls in version info from the version.properties file.
  * 
@@ -48,7 +50,7 @@ public class JmeVersion {
     
     static {
         try {
-            props.load(JmeVersion.class.getResourceAsStream("version.properties"));
+            props.load(Resources.getResourceAsStream("version.properties",JmeVersion.class));
         } catch (IOException | NullPointerException ex) {
             logger.log(Level.WARNING, "Unable to read version info!", ex);
         }

+ 5 - 0
jme3-core/src/main/java/com/jme3/system/NullRenderer.java

@@ -293,4 +293,9 @@ public class NullRenderer implements Renderer {
     public boolean isMainFrameBufferSrgb() {
         return false;
     }
+
+    @Override
+    public FrameBuffer getCurrentFrameBuffer() {
+        return null;
+    }
 }

+ 12 - 2
jme3-core/src/main/java/com/jme3/system/Platform.java

@@ -139,7 +139,13 @@ public enum Platform {
     /**
      * Android running on unknown platform (could be x86 or mips for example).
      */
-    Android_Other(Os.Android);
+    Android_Other(Os.Android),
+    
+    /**
+    * Generic web platform on unknown architecture
+    */
+    Web(Os.Web, true) // assume always 64-bit, it shouldn't matter for web
+    ;
 
     
     /**
@@ -165,7 +171,11 @@ public enum Platform {
         /**
          * Android operating systems
          */
-        Android
+        Android,
+        /**
+         * Generic web platform
+         */
+        Web
     }
 
     private final boolean is64bit;

+ 72 - 0
jme3-core/src/main/java/com/jme3/util/res/DefaultResourceLoader.java

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2009-2023 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.util.res;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+/**
+ * Default implementation of {@link ResourceLoader}. 
+ * Loads from classpath.
+ */
+class DefaultResourceLoader implements ResourceLoader {
+    
+    DefaultResourceLoader() {
+        
+    }
+
+    @Override
+    public InputStream getResourceAsStream(String path, Class<?> parent) {
+        if (parent == null) {
+            return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
+        } else {
+            return parent.getResourceAsStream(path);
+        }
+    }
+
+    @Override
+    public URL getResource(String path, Class<?> parent) {
+        if (parent == null) {
+            return Thread.currentThread().getContextClassLoader().getResource(path);
+        } else {
+            return parent.getResource(path);
+        }
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String path) throws IOException {
+        return Thread.currentThread().getContextClassLoader().getResources(path);        
+    }
+
+}

+ 81 - 0
jme3-core/src/main/java/com/jme3/util/res/ResourceLoader.java

@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2023 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.util.res;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+public interface ResourceLoader {
+    /**
+     * Finds the resource with the given name relative to the given parent class
+     * or to the root if the parent is null.
+     * 
+     * @param path
+     *            The resource name
+     * @param parent
+     *            Optional parent class
+     * @return The resource URL or null if not found
+     */
+    public URL getResource(String path, Class<?> parent);
+
+    /**
+     * Finds the resource with the given name relative to the given parent class
+     * or to the root if the parent is null.
+     * 
+     * @param path
+     *            The resource name
+     * @param parent
+     *            Optional parent class
+     * @return An input stream to the resource or null if not found
+     */
+    public InputStream getResourceAsStream(String path, Class<?> parent);
+
+
+    /**
+     * Finds all resources with the given name.
+     * 
+     * 
+     * @param path
+     *            The resource name
+     * @return An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
+     *         the resource. If no resources could be found, the enumeration
+     *         will be empty.
+     *
+     * @throws IOException
+     *             If I/O errors occur
+     *
+     * @throws IOException
+     */
+    public Enumeration<URL> getResources(String path) throws IOException;
+}

+ 182 - 0
jme3-core/src/main/java/com/jme3/util/res/Resources.java

@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2009-2023 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.util.res;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class is used to load resources from the default location usually the
+ * classpath.
+ */
+public class Resources {
+    /**
+     * The property name to set the ResourceLoader to use. Should be set automatically
+     * by the JmeSystemDelegate. 
+     * Note: changing this property after the first use of the ResourceLoader will have no effect.
+     */
+    public static final String PROPERTY_RESOURCE_LOADER_IMPLEMENTATION = "com.jme3.ResourceLoaderImplementation";
+
+    private static final String DEFAULT_IMPL = "com.jme3.util.res.DefaultResourceLoader";
+    private static final Logger LOGGER = Logger.getLogger(Resources.class.getName());
+    private static ResourceLoader impl = null;
+
+    @SuppressWarnings("unchecked")
+    private static Class<? extends ResourceLoader> findResourceLoaderClass(String className) {
+        Class<?> clazz = null;
+
+        try {
+            clazz = Class.forName(className);
+        } catch (final Throwable e) {
+            LOGGER.log(Level.WARNING, "Unable to access {0}", className);
+        }
+
+        if (clazz != null && !ResourceLoader.class.isAssignableFrom(clazz)) {
+            LOGGER.log(Level.WARNING, "{0} does not implement {1}", new Object[] { className, ResourceLoader.class.getName() });
+            clazz = null;
+        }
+
+        return (Class<? extends ResourceLoader>) clazz;
+    }
+
+    private static ResourceLoader getResourceLoader() {
+        if (impl != null) return impl;
+        Class<? extends ResourceLoader> clazz = null;
+        String userDefinedImpl = System.getProperty(PROPERTY_RESOURCE_LOADER_IMPLEMENTATION, null);
+        
+        if (userDefinedImpl != null) {
+            LOGGER.log(Level.FINE, "Loading user defined ResourceLoader implementation {0}", userDefinedImpl);
+            clazz = findResourceLoaderClass(userDefinedImpl);
+        }
+
+        if (clazz == null) {
+            LOGGER.log(Level.FINE, "No usable user defined ResourceLoader implementation found, using default implementation {0}", DEFAULT_IMPL);
+            clazz = findResourceLoaderClass(DEFAULT_IMPL);
+        }
+
+        if (clazz == null) {
+            throw new RuntimeException("No ResourceLoader implementation found");
+        }
+
+        try {
+            impl = (ResourceLoader) clazz.getDeclaredConstructor().newInstance();
+        } catch (final Throwable e) {
+            throw new RuntimeException("Could not instantiate ResourceLoader class " + clazz.getName(), e);
+        }
+
+        return impl;
+    }
+
+    /**
+     * Sets the resource loader implementation to use.
+     * @param impl The resource loader implementation
+     */
+    public static void setResourceLoader(ResourceLoader impl) {
+        Resources.impl = impl;
+    }
+
+    /**
+     * Finds the resource with the given name.
+     * 
+     * @param path
+     *            The resource name
+     * @return The resource URL or null if not found
+     */
+    public static URL getResource(String path) {
+        return getResourceLoader().getResource(path, null);
+    }
+
+    /**
+     * Finds the resource with the given name relative to the given parent class
+     * or to the root if the parent is null.
+     * 
+     * @param path
+     *            The resource name
+     * @param parent
+     *            Optional parent class
+     * @return The resource URL or null if not found
+     */
+    public static URL getResource(String path, Class<?> parent) {
+        return getResourceLoader().getResource(path, parent);
+    }
+
+    /**
+     * Finds the resource with the given name.
+     * 
+     * @param path
+     *            The resource name
+     * @return An input stream to the resource or null if not found
+     */
+    public static InputStream getResourceAsStream(String path) {
+        return getResourceLoader().getResourceAsStream(path, null);
+    }
+
+    /**
+     * Finds the resource with the given name relative to the given parent class
+     * or to the root if the parent is null.
+     * 
+     * @param path
+     *            The resource name
+     * @param parent
+     *            Optional parent class
+     * @return An input stream to the resource or null if not found
+     */
+    public static InputStream getResourceAsStream(String path, Class<?> parent) {
+        return getResourceLoader().getResourceAsStream(path, parent);
+    }
+
+    /**
+     * Finds all resources with the given name.
+     * 
+     * 
+     * @param path
+     *            The resource name
+     *
+     * 
+     * @return An enumeration of {@link java.net.URL <tt>URL</tt>} objects for
+     *         the resource. If no resources could be found, the enumeration
+     *         will be empty.
+     *
+     * @throws IOException
+     *             If I/O errors occur
+     *
+     * @throws IOException
+     */
+    public static Enumeration<URL> getResources(String path) throws IOException {
+        return getResourceLoader().getResources(path);
+    }
+
+}

+ 7 - 2
jme3-core/src/main/resources/Common/MatDefs/Blur/HGaussianBlur.j3md

@@ -1,6 +1,7 @@
 MaterialDef HGaussianBlur {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Texture2D Texture
         Float Size
@@ -8,10 +9,14 @@ MaterialDef HGaussianBlur {
     }
 
     Technique {
-        VertexShader GLSL100 GLSL150:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL100 GLSL150: Common/MatDefs/Blur/HGaussianBlur.frag
+        VertexShader GLSL300 GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Blur/HGaussianBlur.frag
 
         WorldParameters {
         }
+        
+        Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
+        }
     }
 }

+ 4 - 2
jme3-core/src/main/resources/Common/MatDefs/Blur/RadialBlur.j3md

@@ -1,6 +1,7 @@
 MaterialDef Radial Blur {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Texture2D Texture
         Color Color
@@ -10,13 +11,14 @@ MaterialDef Radial Blur {
     }
 
     Technique {
-        VertexShader GLSL300 GLSL120 GLSL150:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL300 GLSL120 GLSL150: Common/MatDefs/Blur/RadialBlur.frag
+        VertexShader GLSL300 GLSL150 GLSL120  :   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL120  : Common/MatDefs/Blur/RadialBlur.frag
 
         WorldParameters {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             RESOLVE_MS : NumSamples
         }
     }

+ 7 - 2
jme3-core/src/main/resources/Common/MatDefs/Blur/VGaussianBlur.j3md

@@ -1,6 +1,7 @@
 MaterialDef VGaussianBlur {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Texture2D Texture
         Float Size
@@ -8,10 +9,14 @@ MaterialDef VGaussianBlur {
     }
 
     Technique {
-        VertexShader GLSL100 GLSL150:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL100 GLSL150: Common/MatDefs/Blur/VGaussianBlur.frag
+        VertexShader GLSL300 GLSL150 GLSL100  :   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100  : Common/MatDefs/Blur/VGaussianBlur.frag
 
         WorldParameters {
         }
+        
+        Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
+        }
     }
 }

+ 4 - 15
jme3-core/src/main/resources/Common/MatDefs/Gui/Gui.j3md

@@ -1,37 +1,26 @@
 MaterialDef Default GUI {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Texture2D Texture
         Color Color (Color)
         Boolean VertexColor (UseVertexColor)
     }
 
     Technique {
-        VertexShader GLSL150:   Common/MatDefs/Gui/Gui.vert
-        FragmentShader GLSL150: Common/MatDefs/Gui/Gui.frag
+        VertexShader GLSL300 GLSL150 GLSL100:   Common/MatDefs/Gui/Gui.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Gui/Gui.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             TEXTURE : Texture
             VERTEX_COLOR : VertexColor
         }
     }
 
-    Technique {
-        VertexShader GLSL100:   Common/MatDefs/Gui/Gui.vert
-        FragmentShader GLSL100: Common/MatDefs/Gui/Gui.frag
-
-        WorldParameters {
-            WorldViewProjectionMatrix
-        }
-
-        Defines {
-            TEXTURE : Texture
-            VERTEX_COLOR : VertexColor
-        }
-    }
 
 }

+ 4 - 2
jme3-core/src/main/resources/Common/MatDefs/Hdr/LogLum.j3md

@@ -1,6 +1,7 @@
 MaterialDef Log Lum 2D {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Texture2D Texture
         Vector2 BlockSize
         Vector2 PixelSize
@@ -12,14 +13,15 @@ MaterialDef Log Lum 2D {
     }
 
     Technique {
-        VertexShader GLSL100:   Common/MatDefs/Gui/Gui.vert
-        FragmentShader GLSL100: Common/MatDefs/Hdr/LogLum.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Gui/Gui.vert
+        FragmentShader GLSL300 GLSL150 GLSL100:   Common/MatDefs/Hdr/LogLum.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             TEXTURE
             ENCODE_LUM : EncodeLum
             DECODE_LUM : DecodeLum

+ 4 - 2
jme3-core/src/main/resources/Common/MatDefs/Hdr/ToneMap.j3md

@@ -1,5 +1,6 @@
 MaterialDef Tone Mapper {
     MaterialParameters {
+        Int BoundDrawBuffer
         Texture2D Texture
         Texture2D Lum
         Texture2D Lum2
@@ -9,14 +10,15 @@ MaterialDef Tone Mapper {
         Float Gamma
     }
     Technique {
-        VertexShader GLSL100:   Common/MatDefs/Gui/Gui.vert
-        FragmentShader GLSL100: Common/MatDefs/Hdr/ToneMap.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Gui/Gui.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Hdr/ToneMap.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             TEXTURE
         }
     }

+ 4 - 2
jme3-core/src/main/resources/Common/MatDefs/Light/Deferred.j3md

@@ -2,6 +2,7 @@
 MaterialDef Phong Lighting Deferred {
 
     MaterialParameters {
+        Int BoundDrawBuffer
 
         // Use more efficient algorithms to improve performance
         Boolean LowQuality
@@ -35,8 +36,8 @@ MaterialDef Phong Lighting Deferred {
     Technique {
         LightMode MultiPass
 
-        VertexShader GLSL100:   Common/MatDefs/Light/Deferred.vert
-        FragmentShader GLSL100: Common/MatDefs/Light/Deferred.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Light/Deferred.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Light/Deferred.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -46,6 +47,7 @@ MaterialDef Phong Lighting Deferred {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             ATTENUATION : Attenuation
             V_TANGENT : VTangent
             MINNAERT  : Minnaert

+ 2 - 0
jme3-core/src/main/resources/Common/MatDefs/Light/Deferred.vert

@@ -1,3 +1,5 @@
+#import "Common/ShaderLib/GLSLCompat.glsllib"
+
 varying vec2 texCoord;
 
 attribute vec3 inPosition;

+ 2 - 0
jme3-core/src/main/resources/Common/MatDefs/Light/GBuf.vert

@@ -1,3 +1,5 @@
+#import "Common/ShaderLib/GLSLCompat.glsllib"
+
 uniform mat4 g_WorldViewProjectionMatrix;
 uniform mat4 g_WorldMatrix;
 

+ 20 - 13
jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md

@@ -1,6 +1,7 @@
 MaterialDef Phong Lighting {
 
     MaterialParameters {
+        Int BoundDrawBuffer
 
         // Compute vertex lighting in the shader
         // For better performance
@@ -133,8 +134,8 @@ MaterialDef Phong Lighting {
     Technique {
         LightMode SinglePass
 
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150:   Common/MatDefs/Light/SPLighting.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/SPLighting.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Light/SPLighting.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100: Common/MatDefs/Light/SPLighting.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -146,7 +147,8 @@ MaterialDef Phong Lighting {
             ViewProjectionMatrix            
         }
 
-        Defines {           
+        Defines {  
+            BOUND_DRAW_BUFFER: BoundDrawBuffer         
             VERTEX_COLOR : UseVertexColor
             VERTEX_LIGHTING : VertexLighting           
             MATERIAL_COLORS : UseMaterialColors         
@@ -180,8 +182,8 @@ MaterialDef Phong Lighting {
 
         LightMode MultiPass
 
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150:   Common/MatDefs/Light/Lighting.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/Lighting.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Light/Lighting.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100: Common/MatDefs/Light/Lighting.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -194,6 +196,7 @@ MaterialDef Phong Lighting {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             VERTEX_COLOR : UseVertexColor
             VERTEX_LIGHTING : VertexLighting            
             MATERIAL_COLORS : UseMaterialColors
@@ -225,8 +228,8 @@ MaterialDef Phong Lighting {
 
     Technique PreShadow {
 
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150 :   Common/MatDefs/Shadow/PreShadow.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PreShadow.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100: Common/MatDefs/Shadow/PreShadow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -236,6 +239,7 @@ MaterialDef Phong Lighting {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             DISCARD_ALPHA : AlphaDiscardThreshold
             NUM_BONES : NumberOfBones
             INSTANCING : UseInstancing
@@ -255,8 +259,8 @@ MaterialDef Phong Lighting {
 
 
     Technique PostShadow {
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150:   Common/MatDefs/Shadow/PostShadow.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PostShadow.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100: Common/MatDefs/Shadow/PostShadow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -267,6 +271,7 @@ MaterialDef Phong Lighting {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             HARDWARE_SHADOWS : HardwareShadows
             FILTER_MODE : FilterMode
             PCFEDGE : PCFEdge
@@ -292,8 +297,8 @@ MaterialDef Phong Lighting {
 
   Technique PreNormalPass {
 
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150 :   Common/MatDefs/SSAO/normal.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/SSAO/normal.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100: Common/MatDefs/SSAO/normal.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -304,6 +309,7 @@ MaterialDef Phong Lighting {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             DIFFUSEMAP_ALPHA : DiffuseMap
             NUM_BONES : NumberOfBones
             INSTANCING : UseInstancing
@@ -315,8 +321,8 @@ MaterialDef Phong Lighting {
 
     Technique Glow {
 
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150:   Common/MatDefs/Misc/Unshaded.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/Glow.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Misc/Unshaded.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100: Common/MatDefs/Light/Glow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -325,6 +331,7 @@ MaterialDef Phong Lighting {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             NEED_TEXCOORD1
             HAS_GLOWMAP : GlowMap
             HAS_GLOWCOLOR : GlowColor

+ 17 - 11
jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md

@@ -1,6 +1,7 @@
 MaterialDef PBR Lighting {
 
     MaterialParameters {
+        Int BoundDrawBuffer
 
         // Alpha threshold for fragment discarding
         Float AlphaDiscardThreshold (AlphaTestFallOff)
@@ -129,8 +130,8 @@ MaterialDef PBR Lighting {
     Technique {
         LightMode SinglePassAndImageBased
         
-        VertexShader GLSL300 GLSL110 GLSL150:   Common/MatDefs/Light/PBRLighting.vert
-        FragmentShader GLSL300 GLSL110 GLSL150: Common/MatDefs/Light/PBRLighting.frag
+        VertexShader GLSL300 GLSL150 GLSL110:   Common/MatDefs/Light/PBRLighting.vert
+        FragmentShader GLSL300 GLSL150 GLSL110: Common/MatDefs/Light/PBRLighting.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -141,7 +142,8 @@ MaterialDef PBR Lighting {
             ViewMatrix
         }
 
-        Defines {         
+        Defines {  
+            BOUND_DRAW_BUFFER: BoundDrawBuffer       
             BASECOLORMAP : BaseColorMap            
             NORMALMAP : NormalMap
             NORMALSCALE : NormalScale
@@ -176,8 +178,8 @@ MaterialDef PBR Lighting {
 
     Technique PreShadow {
 
-        VertexShader GLSL300 GLSL100 GLSL150 :   Common/MatDefs/Shadow/PreShadow.vert
-        FragmentShader GLSL300 GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadowPBR.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PreShadow.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Shadow/PreShadowPBR.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -187,6 +189,7 @@ MaterialDef PBR Lighting {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             DISCARD_ALPHA : AlphaDiscardThreshold
             NUM_BONES : NumberOfBones
             INSTANCING : UseInstancing
@@ -206,8 +209,8 @@ MaterialDef PBR Lighting {
 
 
     Technique PostShadow {
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150:   Common/MatDefs/Shadow/PostShadow.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadowPBR.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PostShadow.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100: Common/MatDefs/Shadow/PostShadowPBR.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -217,6 +220,7 @@ MaterialDef PBR Lighting {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             HARDWARE_SHADOWS : HardwareShadows
             FILTER_MODE : FilterMode
             PCFEDGE : PCFEdge
@@ -241,8 +245,8 @@ MaterialDef PBR Lighting {
 
     Technique PreNormalPass {
 
-        VertexShader GLSL100 :   Common/MatDefs/SSAO/normal.vert
-        FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
+        VertexShader   GLSL300 GLSL150 GLSL100 :   Common/MatDefs/SSAO/normal.vert
+        FragmentShader GLSL300 GLSL150 GLSL100 : Common/MatDefs/SSAO/normal.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -253,6 +257,7 @@ MaterialDef PBR Lighting {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             BASECOLORMAP_ALPHA : BaseColorMap            
             NUM_BONES : NumberOfBones
             INSTANCING : UseInstancing
@@ -264,8 +269,8 @@ MaterialDef PBR Lighting {
 
     Technique Glow {
 
-        VertexShader GLSL100 GLSL150:   Common/MatDefs/Misc/Unshaded.vert
-        FragmentShader GLSL100 GLSL150: Common/MatDefs/Light/Glow.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Misc/Unshaded.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Light/Glow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -274,6 +279,7 @@ MaterialDef PBR Lighting {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             NEED_TEXCOORD1
             NUM_BONES : NumberOfBones
             INSTANCING : UseInstancing

+ 7 - 5
jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md

@@ -2,20 +2,22 @@ Exception This material definition is deprecated. Please use Unshaded.j3md inste
 MaterialDef Colored Textured {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Texture2D ColorMap
         Color Color (Color)
     }
 
     Technique {
-        VertexShader GLSL100:   Common/MatDefs/Misc/ColoredTextured.vert
-        FragmentShader GLSL100: Common/MatDefs/Misc/ColoredTextured.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Misc/ColoredTextured.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Misc/ColoredTextured.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
         }
+        Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
+        }
     }
 
-    Technique {
-    }
-
+ 
 }

+ 2 - 0
jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.vert

@@ -1,3 +1,5 @@
+#import "Common/ShaderLib/GLSLCompat.glsllib"
+
 uniform mat4 g_WorldViewProjectionMatrix;
 
 attribute vec3 inPosition;

+ 13 - 8
jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.j3md

@@ -1,6 +1,7 @@
 MaterialDef Point Sprite {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Texture2D Texture
         Float Quadratic
         Boolean PointSprite
@@ -24,8 +25,8 @@ MaterialDef Point Sprite {
         // Point sprite should be used if running on ES2, but crash
         // if on desktop (because its not supported by HW)
 
-        VertexShader   GLSL100 GLSL100 GLSL150 : Common/MatDefs/Misc/Particle.vert
-        FragmentShader GLSL100 GLSL120 GLSL150 : Common/MatDefs/Misc/Particle.frag
+        VertexShader   GLSL300 GLSL150 GLSL120 GLSL100: Common/MatDefs/Misc/Particle.vert
+        FragmentShader GLSL300 GLSL150 GLSL120 GLSL100: Common/MatDefs/Misc/Particle.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -41,6 +42,7 @@ MaterialDef Point Sprite {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             USE_TEXTURE : Texture
             POINT_SPRITE : PointSprite
         }
@@ -48,8 +50,8 @@ MaterialDef Point Sprite {
 
     Technique PreShadow {
 
-        VertexShader GLSL100 GLSL150 :   Common/MatDefs/Shadow/PreShadow.vert
-        FragmentShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PreShadow.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Shadow/PreShadow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -59,6 +61,7 @@ MaterialDef Point Sprite {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             COLOR_MAP : Texture
         }
 
@@ -74,8 +77,8 @@ MaterialDef Point Sprite {
 
     Technique SoftParticles{
 
-        VertexShader  GLSL100 GLSL150 : Common/MatDefs/Misc/SoftParticle.vert
-        FragmentShader GLSL120 GLSL150 : Common/MatDefs/Misc/SoftParticle.frag
+        VertexShader   GLSL300 GLSL150 GLSL100: Common/MatDefs/Misc/SoftParticle.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Misc/SoftParticle.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -91,6 +94,7 @@ MaterialDef Point Sprite {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             USE_TEXTURE : Texture
             POINT_SPRITE : PointSprite
             RESOLVE_DEPTH_MS : NumSamplesDepth
@@ -99,14 +103,15 @@ MaterialDef Point Sprite {
 
    Technique Glow {
 
-        VertexShader GLSL100 GLSL150:   Common/MatDefs/Misc/Unshaded.vert
-        FragmentShader GLSL100 GLSL150: Common/MatDefs/Light/Glow.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Misc/Unshaded.vert
+        FragmentShader GLSL300 GLSL150 GLSL100:   Common/MatDefs/Light/Glow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             NEED_TEXCOORD1
             HAS_GLOWMAP : GlowMap
             HAS_GLOWCOLOR : GlowColor

+ 4 - 2
jme3-core/src/main/resources/Common/MatDefs/Misc/ShowNormals.j3md

@@ -1,12 +1,13 @@
 MaterialDef Debug Normals {
     MaterialParameters {
+        Int BoundDrawBuffer
         // For instancing
         Boolean UseInstancing
     }
 
     Technique {
-        VertexShader GLSL100 GLSL150:   Common/MatDefs/Misc/ShowNormals.vert
-        FragmentShader GLSL100 GLSL150: Common/MatDefs/Misc/ShowNormals.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Misc/ShowNormals.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Misc/ShowNormals.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -16,6 +17,7 @@ MaterialDef Debug Normals {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             INSTANCING : UseInstancing
         }
     }

+ 4 - 2
jme3-core/src/main/resources/Common/MatDefs/Misc/Sky.j3md

@@ -1,13 +1,14 @@
 MaterialDef Sky Plane {
     MaterialParameters {
+        Int BoundDrawBuffer
         TextureCubeMap Texture
         Boolean SphereMap
         Boolean EquirectMap
         Vector3 NormalScale
     }
     Technique {
-        VertexShader GLSL100 GLSL150:   Common/MatDefs/Misc/Sky.vert
-        FragmentShader GLSL100 GLSL150: Common/MatDefs/Misc/Sky.frag
+        VertexShader    GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Misc/Sky.vert
+        FragmentShader  GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Misc/Sky.frag
 
         WorldParameters {
             ViewMatrix
@@ -16,6 +17,7 @@ MaterialDef Sky Plane {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             SPHERE_MAP : SphereMap
             EQUIRECT_MAP : EquirectMap
         }

+ 4 - 2
jme3-core/src/main/resources/Common/MatDefs/Misc/SkyNonCube.j3md

@@ -1,13 +1,14 @@
 MaterialDef Sky Plane {
     MaterialParameters {
+        Int BoundDrawBuffer
         Texture2D Texture
         Boolean SphereMap
         Boolean EquirectMap
         Vector3 NormalScale
     }
     Technique {
-        VertexShader GLSL100 GLSL150:   Common/MatDefs/Misc/Sky.vert
-        FragmentShader GLSL100 GLSL150: Common/MatDefs/Misc/Sky.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Misc/Sky.vert
+        FragmentShader GLSL300 GLSL150 GLSL100:   Common/MatDefs/Misc/Sky.frag
 
         WorldParameters {
             ViewMatrix
@@ -16,6 +17,7 @@ MaterialDef Sky Plane {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             SPHERE_MAP : SphereMap
             EQUIRECT_MAP : EquirectMap
         }

+ 16 - 10
jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md

@@ -1,6 +1,7 @@
 MaterialDef Unshaded {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Texture2D ColorMap
         Texture2D LightMap
         Color Color (Color)
@@ -65,8 +66,8 @@ MaterialDef Unshaded {
     }
 
     Technique {
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150:   Common/MatDefs/Misc/Unshaded.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Misc/Unshaded.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Misc/Unshaded.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100 : Common/MatDefs/Misc/Unshaded.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -75,6 +76,7 @@ MaterialDef Unshaded {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             INSTANCING : UseInstancing
             SEPARATE_TEXCOORD : SeparateTexCoord
             HAS_COLORMAP : ColorMap
@@ -92,8 +94,8 @@ MaterialDef Unshaded {
 
     Technique PreNormalPass {
 
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150 :   Common/MatDefs/SSAO/normal.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/SSAO/normal.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/SSAO/normal.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/SSAO/normal.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -104,6 +106,7 @@ MaterialDef Unshaded {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             COLORMAP_ALPHA : ColorMap
             NUM_BONES : NumberOfBones
             INSTANCING : UseInstancing
@@ -114,8 +117,8 @@ MaterialDef Unshaded {
 
     Technique PreShadow {
 
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150 :   Common/MatDefs/Shadow/PreShadow.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PreShadow.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PreShadow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -125,6 +128,7 @@ MaterialDef Unshaded {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             COLOR_MAP : ColorMap
             DISCARD_ALPHA : AlphaDiscardThreshold
             NUM_BONES : NumberOfBones
@@ -145,8 +149,8 @@ MaterialDef Unshaded {
 
 
     Technique PostShadow {
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150:   Common/MatDefs/Shadow/PostShadow.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.frag
+        VertexShader    GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PostShadow.vert
+        FragmentShader  GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PostShadow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -156,6 +160,7 @@ MaterialDef Unshaded {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             HARDWARE_SHADOWS : HardwareShadows
             FILTER_MODE : FilterMode
             PCFEDGE : PCFEdge
@@ -181,8 +186,8 @@ MaterialDef Unshaded {
 
     Technique Glow {
 
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150:   Common/MatDefs/Misc/Unshaded.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Light/Glow.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Misc/Unshaded.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Light/Glow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -191,6 +196,7 @@ MaterialDef Unshaded {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             NEED_TEXCOORD1
             HAS_GLOWMAP : GlowMap
             HAS_GLOWCOLOR : GlowColor

+ 4 - 2
jme3-core/src/main/resources/Common/MatDefs/Shadow/BasicPostShadow.j3md

@@ -1,13 +1,14 @@
 MaterialDef Basic Post Shadow {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Texture2D ShadowMap
         Matrix4 LightViewProjectionMatrix
     }
 
     Technique {
-        VertexShader GLSL100 GLSL150:   Common/MatDefs/Shadow/BasicPostShadow.vert
-        FragmentShader GLSL100 GLSL150: Common/MatDefs/Shadow/BasicPostShadow.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/BasicPostShadow.vert
+        FragmentShader GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/BasicPostShadow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -15,6 +16,7 @@ MaterialDef Basic Post Shadow {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             NO_SHADOW2DPROJ
         }
 

+ 4 - 2
jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadow.j3md

@@ -1,6 +1,7 @@
 MaterialDef Post Shadow {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int FilterMode
         Boolean HardwareShadows
 
@@ -35,8 +36,8 @@ MaterialDef Post Shadow {
     }
 
     Technique {
-        VertexShader GLSL310 GLSL300 GLSL100 GLSL150:   Common/MatDefs/Shadow/PostShadow.vert
-        FragmentShader GLSL310 GLSL300 GLSL100 GLSL150: Common/MatDefs/Shadow/PostShadow.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PostShadow.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 GLSL100: Common/MatDefs/Shadow/PostShadow.frag
 
         WorldParameters {
             WorldViewProjectionMatrix
@@ -44,6 +45,7 @@ MaterialDef Post Shadow {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             HARDWARE_SHADOWS : HardwareShadows
             FILTER_MODE : FilterMode
             PCFEDGE : PCFEdge

+ 7 - 4
jme3-core/src/main/resources/Common/MatDefs/Shadow/PostShadowFilter.j3md

@@ -1,6 +1,7 @@
 MaterialDef Post Shadow {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int FilterMode
         Boolean HardwareShadows
 
@@ -42,14 +43,15 @@ MaterialDef Post Shadow {
     }
 
     Technique {
-        VertexShader GLSL310 GLSL150:   Common/MatDefs/Shadow/PostShadowFilter.vert
-        FragmentShader GLSL310 GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag
+        VertexShader   GLSL310 GLSL300 GLSL150 :   Common/MatDefs/Shadow/PostShadowFilter.vert
+        FragmentShader GLSL310 GLSL300 GLSL150 :   Common/MatDefs/Shadow/PostShadowFilter15.frag
 
         WorldParameters {
             ResolutionInverse
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             RESOLVE_MS : NumSamples
             RESOLVE_DEPTH_MS : NumSamplesDepth
             HARDWARE_SHADOWS : HardwareShadows
@@ -65,14 +67,15 @@ MaterialDef Post Shadow {
     }
 
     Technique {
-        VertexShader GLSL100:   Common/MatDefs/Shadow/PostShadowFilter.vert
-        FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowFilter.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PostShadowFilter.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Shadow/PostShadowFilter.frag
 
         WorldParameters {
             ResolutionInverse
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             HARDWARE_SHADOWS : HardwareShadows
             FILTER_MODE : FilterMode
             PCFEDGE : PCFEdge

+ 10 - 2
jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.j3md

@@ -1,7 +1,15 @@
 MaterialDef Pre Shadow {
+    MaterialParameters {
+        Int BoundDrawBuffer
+    }
+
     Technique {
-        VertexShader GLSL100 GLSL150 :   Common/MatDefs/Shadow/PreShadow.vert
-        FragmentShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PreShadow.vert
+        FragmentShader GLSL300 GLSL150 GLSL100:   Common/MatDefs/Shadow/PreShadow.frag
+
+        Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
+        }
 
         WorldParameters {
             WorldViewProjectionMatrix

+ 32 - 4
jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib

@@ -1,3 +1,17 @@
+#ifdef FRAGMENT_SHADER
+      precision highp float;
+      precision highp int;
+      precision highp sampler2DArray;
+      precision highp sampler2DShadow;
+      precision highp samplerCube;
+      precision highp sampler3D;
+      precision highp sampler2D;
+      #if __VERSION__ >= 310
+        precision highp sampler2DMS;
+      #endif
+
+#endif
+
 #if defined GL_ES
 #  define hfloat highp float
 #  define hvec2  highp vec2
@@ -19,11 +33,25 @@
 #endif
 
 #if __VERSION__ >= 130
-#  ifdef GL_ES
-out highp vec4 outFragColor;
-#  else
-out vec4 outFragColor;
+
+#ifdef FRAGMENT_SHADER
+  #ifdef GL_ES
+    #ifdef BOUND_DRAW_BUFFER
+      #for i=0..15 ( #if $i<=BOUND_DRAW_BUFFER  $0 #endif ) 
+        #if BOUND_DRAW_BUFFER == $i
+          layout( location = $i ) out highp vec4 outFragColor;
+        #else
+          layout( location = $i ) out highp vec4 outNOP$i;
+        #endif
+      #endfor
+    #else
+      out highp vec4 outFragColor;
+    #endif
+  #else
+    out vec4 outFragColor;
+  #endif
 #endif
+
 #  define texture1D texture
 #  define texture2D texture
 #  define texture3D texture

+ 1 - 2
jme3-core/src/main/resources/Common/ShaderLib/Shadows.glsllib

@@ -177,8 +177,7 @@ float Shadow_DoPCF(in SHADOWMAP tex,in vec4 projCoord){
     if (border > 0.0)
         return 1.0;
 
-    float bound = KERNEL * 0.5 - 0.5;
-    bound *= PCFEDGE;
+    const float bound = (KERNEL * 0.5 - 0.5 ) * PCFEDGE;
     for (float y = -bound; y <= bound; y += PCFEDGE){
         for (float x = -bound; x <= bound; x += PCFEDGE){
             #if __VERSION__ < 130

+ 6 - 14
jme3-core/src/plugins/java/com/jme3/asset/plugins/ClasspathLocator.java

@@ -33,6 +33,8 @@ package com.jme3.asset.plugins;
 
 import com.jme3.asset.*;
 import com.jme3.system.JmeSystem;
+import com.jme3.util.res.Resources;
+
 import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
@@ -87,20 +89,10 @@ public class ClasspathLocator implements AssetLocator {
 //            name = root + name;
 //        }
 
-        if (JmeSystem.isLowPermissions()){
-            url = ClasspathLocator.class.getResource("/" + name);
-        }else{
-            url = Thread.currentThread().getContextClassLoader().getResource(name);
-        }
-
-        if (url == null) {
-            final List<ClassLoader> classLoaders = manager.getClassLoaders();
-            for (final ClassLoader classLoader : classLoaders) {
-                url = classLoader.getResource(name);
-                if(url != null) {
-                    break;
-                }
-            }
+        if (JmeSystem.isLowPermissions()) {
+            url = Resources.getResource("/" + name, ClasspathLocator.class);    
+        } else {
+            url = Resources.getResource(name);
         }
 
         if (url == null)

+ 2 - 2
jme3-core/src/plugins/java/com/jme3/export/binary/BinaryExporter.java

@@ -328,9 +328,9 @@ public class BinaryExporter implements JmeExporter {
     }
 
     @Override
-    public void save(Savable object, File f) throws IOException {
+    public void save(Savable object, File f, boolean createDirectories) throws IOException {
         File parentDirectory = f.getParentFile();
-        if (parentDirectory != null && !parentDirectory.exists()) {
+        if (parentDirectory != null && !parentDirectory.exists() && createDirectories) {
             parentDirectory.mkdirs();
         }
 

+ 1 - 6
jme3-core/src/plugins/java/com/jme3/export/binary/BinaryImporter.java

@@ -329,12 +329,7 @@ public final class BinaryImporter implements JmeImporter {
             int dataLength = ByteUtils.convertIntFromBytes(dataArray, loc);
             loc+=4;
 
-            Savable out = null;
-            if (assetManager != null) {
-                out = SavableClassUtil.fromName(bco.className, assetManager.getClassLoaders());
-            } else {
-                out = SavableClassUtil.fromName(bco.className);
-            }
+            Savable  out = SavableClassUtil.fromName(bco.className);
 
             BinaryInputCapsule cap = new BinaryInputCapsule(this, out, bco);
             cap.setContent(dataArray, loc, loc+dataLength);

+ 80 - 0
jme3-core/src/test/java/com/jme3/anim/tween/action/ClipActionTest.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2023 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.anim.tween.action;
+
+import com.jme3.anim.AnimClip;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+/**
+ * Test for ClipAction.
+ *
+ * @author Saichand Chowdary
+ */
+public class ClipActionTest {
+
+    /**
+     * Test to verify setTransitionLength on BlendableAction does not accept negative values.
+     */
+    @Test
+    public void testSetTransitionLength_negativeInput_exceptionThrown() {
+        AnimClip animClip = new AnimClip("clip");
+        ClipAction clipAction = new ClipAction(animClip);
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+                () -> clipAction.setTransitionLength(-1));
+        assertEquals("transitionLength must be greater than or equal to 0", thrown.getMessage());
+    }
+
+    /**
+     * Test to verify setTransitionLength on BlendableAction accepts zero.
+     */
+    @Test
+    public void testSetTransitionLength_zeroInput_noExceptionThrown() {
+        AnimClip animClip = new AnimClip("clip");
+        ClipAction clipAction = new ClipAction(animClip);
+        clipAction.setTransitionLength(0);
+        assertEquals(0, clipAction.getTransitionLength(), 0);
+    }
+
+    /**
+     * Test to verify setTransitionLength on BlendableAction accepts positive values.
+     */
+    @Test
+    public void testSetTransitionLength_positiveNumberInput_noExceptionThrown() {
+        AnimClip animClip = new AnimClip("clip");
+        ClipAction clipAction = new ClipAction(animClip);
+        clipAction.setTransitionLength(1.23d);
+        assertEquals(1.23d, clipAction.getTransitionLength(), 0);
+    }
+
+}

+ 1 - 1
jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java

@@ -449,7 +449,7 @@ public class MaterialMatParamTest {
             MaterialMatParamTest.this.usedTextures[unit] = texture;
         }
     };
-    private final RenderManager renderManager = new RenderManager(renderer);
+    private final RenderManager renderManager = TestUtil.createRenderManager(renderer);
 
     private boolean evaluated = false;
     private Shader usedShader = null;

+ 1 - 1
jme3-core/src/test/java/com/jme3/material/MaterialTest.java

@@ -104,7 +104,7 @@ public class MaterialTest {
 
         material.selectTechnique("Default", renderManager);
 
-        checkRequiredCaps(Caps.GLSL100, Caps.GLSL120);
+        checkRequiredCaps(Caps.GLSL120);
     }
 
     @Test

+ 3 - 1
jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java

@@ -32,6 +32,8 @@
 package com.jme3.system;
 
 import com.jme3.audio.AudioRenderer;
+import com.jme3.util.res.Resources;
+
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.URL;
@@ -45,7 +47,7 @@ public class MockJmeSystemDelegate extends JmeSystemDelegate {
 
     @Override
     public URL getPlatformAssetConfigURL() {
-        return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/General.cfg");
+        return Resources.getResource("com/jme3/asset/General.cfg");
     }
 
     @Override

+ 10 - 2
jme3-core/src/test/java/com/jme3/system/TestUtil.java

@@ -35,6 +35,8 @@ import com.jme3.asset.AssetConfig;
 import com.jme3.asset.AssetManager;
 import com.jme3.asset.DesktopAssetManager;
 import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -55,7 +57,13 @@ public class TestUtil {
         return new DesktopAssetManager(true);
     }
     
-    public static RenderManager createRenderManager() {
-        return new RenderManager(new NullRenderer());
+    public static RenderManager createRenderManager() {        
+        return createRenderManager(new NullRenderer());
+    }
+
+    public static RenderManager createRenderManager(Renderer renderer) {
+        RenderManager rm = new RenderManager(renderer);
+        rm.setPassDrawBufferTargetIdToShaders(false);
+        return rm;
     }
 }

+ 3 - 1
jme3-desktop/src/main/java/com/jme3/app/AppletHarness.java

@@ -34,6 +34,8 @@ package com.jme3.app;
 import com.jme3.system.AppSettings;
 import com.jme3.system.JmeCanvasContext;
 import com.jme3.system.JmeSystem;
+import com.jme3.util.res.Resources;
+
 import java.applet.Applet;
 import java.awt.Canvas;
 import java.awt.Graphics;
@@ -150,7 +152,7 @@ public class AppletHarness extends Applet {
             assetCfg = new URL(getParameter("AssetConfigURL"));
         } catch (MalformedURLException ex){
             System.out.println(ex.getMessage());
-            assetCfg = getClass().getResource("/com/jme3/asset/Desktop.cfg");
+            assetCfg = Resources.getResource("/com/jme3/asset/Desktop.cfg",this.getClass());
         }
 
         createCanvas();

+ 3 - 1
jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java

@@ -39,6 +39,8 @@ import com.jme3.audio.openal.EFX;
 import com.jme3.system.JmeContext.Type;
 import com.jme3.texture.Image;
 import com.jme3.texture.image.ColorSpace;
+import com.jme3.util.res.Resources;
+
 import jme3tools.converters.ImageToAwt;
 
 import javax.imageio.IIOImage;
@@ -70,7 +72,7 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
 
     @Override
     public URL getPlatformAssetConfigURL() {
-        return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Desktop.cfg");
+        return Resources.getResource("com/jme3/asset/Desktop.cfg");
     }
     
     private static BufferedImage verticalFlip(BufferedImage original) {

+ 7 - 5
jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java

@@ -44,6 +44,8 @@ import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.jme3.util.res.Resources;
+
 /**
  * Utility class to register, extract, and load native libraries.
  * <br>
@@ -274,7 +276,7 @@ public final class NativeLibraryLoader {
     private static int computeNativesHash() {
         URLConnection conn = null;
         String classpath = System.getProperty("java.class.path");
-        URL url = Thread.currentThread().getContextClassLoader().getResource("com/jme3/system/NativeLibraryLoader.class");
+        URL url = Resources.getResource("com/jme3/system/NativeLibraryLoader.class");
 
         try {
             StringBuilder sb = new StringBuilder(url.toString());
@@ -371,9 +373,9 @@ public final class NativeLibraryLoader {
             fileNameInJar = pathInJar;
         }
         
-        URL url = Thread.currentThread().getContextClassLoader().getResource(pathInJar);
+        URL url = Resources.getResource(pathInJar);
         if (url == null) {
-            url = Thread.currentThread().getContextClassLoader().getResource(fileNameInJar);
+            url = Resources.getResource(fileNameInJar);
         }
         
         if (url == null) {
@@ -401,7 +403,7 @@ public final class NativeLibraryLoader {
             return;
         }
         
-        URL url = Thread.currentThread().getContextClassLoader().getResource(pathInJar);
+        URL url = Resources.getResource(pathInJar);
         if (url == null) {
             return;
         }
@@ -462,7 +464,7 @@ public final class NativeLibraryLoader {
             return;
         }
 
-        URL url = Thread.currentThread().getContextClassLoader().getResource(pathInJar);
+        URL url = Resources.getResource(pathInJar);
 
         if (url == null) {
             if (isRequired) {

+ 4 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/BloomExtract.j3md

@@ -1,6 +1,7 @@
 MaterialDef Bloom {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Texture2D Texture
         Float ExposurePow
@@ -10,13 +11,14 @@ MaterialDef Bloom {
     }
 
     Technique {
-        VertexShader GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL150 GLSL100: Common/MatDefs/Post/bloomExtract.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Post/bloomExtract.frag
 
         WorldParameters {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             HAS_GLOWMAP : GlowMap
             DO_EXTRACT : Extract
             RESOLVE_MS : NumSamples

+ 5 - 3
jme3-effects/src/main/resources/Common/MatDefs/Post/BloomFinal.j3md

@@ -1,20 +1,22 @@
 MaterialDef Bloom Final {
 
     MaterialParameters {
-          Int NumSamples
+        Int BoundDrawBuffer
+        Int NumSamples
         Texture2D Texture
         Texture2D BloomTex
         Float BloomIntensity
     }
 
     Technique {
-        VertexShader GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL150 GLSL100: Common/MatDefs/Post/bloomFinal.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Post/bloomFinal.frag
 
         WorldParameters {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             RESOLVE_MS : NumSamples
         }
     }

+ 4 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/CartoonEdge.j3md

@@ -1,6 +1,7 @@
 MaterialDef Cartoon Edge {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Int NumSamplesDepth
         Texture2D Texture
@@ -16,8 +17,8 @@ MaterialDef Cartoon Edge {
     }
 
      Technique {
-        VertexShader GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL150 GLSL100: Common/MatDefs/Post/CartoonEdge.frag
+        VertexShader   GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Post/CartoonEdge.frag
 
         WorldParameters {
             WorldViewMatrix
@@ -25,6 +26,7 @@ MaterialDef Cartoon Edge {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             RESOLVE_MS : NumSamples
             RESOLVE_DEPTH_MS : NumSamplesDepth
         }

+ 4 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/Compose.j3md

@@ -1,6 +1,7 @@
 MaterialDef Default GUI {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Int NumSamplesDepth
         Texture2D Texture
@@ -8,13 +9,14 @@ MaterialDef Default GUI {
     }
 
     Technique {
-        VertexShader GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL150 GLSL100: Common/MatDefs/Post/Compose.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Post/Compose.frag
 
         WorldParameters {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             RESOLVE_MS : NumSamples
         }
 

+ 4 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/ContrastAdjustment.frag

@@ -37,9 +37,11 @@
  * Then a power law is applied, using the exponent for each channel.
  * Finally, the output value is scaled linearly, using the scaling factor for each channel.
  *
- * Supports GLSL100 GLSL110 GLSL120 GLSL130.
  */
 
+#import "Common/ShaderLib/GLSLCompat.glsllib"
+#import "Common/ShaderLib/MultiSample.glsllib"
+
 //constant inputs from java source
 uniform float m_redChannelExponent;
 uniform float m_greenChannelExponent;
@@ -55,7 +57,7 @@ uniform float m_lowerLimit;
 uniform float m_upperLimit;
 
 //container for the input from post.vert
-uniform sampler2D m_Texture;
+uniform COLORTEXTURE m_Texture;
 
 //varying input from post.vert vertex shader
 varying vec2 texCoord;

+ 10 - 6
jme3-effects/src/main/resources/Common/MatDefs/Post/ContrastAdjustment.j3md

@@ -4,6 +4,7 @@
 MaterialDef ColorAdjustmentFilter {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Texture2D Texture
         Float redChannelExponent
@@ -15,14 +16,17 @@ MaterialDef ColorAdjustmentFilter {
         Float greenChannelScale
         Float blueChannelScale
     }
+ 
 
     Technique {
-        VertexShader GLSL150 GLSL300 GLSL310 GLSL320:   Common/MatDefs/Post/Post15.vert
-        FragmentShader GLSL150 GLSL300 GLSL310 GLSL320: Common/MatDefs/Post/ContrastAdjustment15.frag
-    }
+        VertexShader    GLSL300 GLSL150 GLSL100:    Common/MatDefs/Post/Post.vert
+        FragmentShader  GLSL300 GLSL150 GLSL100:  Common/MatDefs/Post/ContrastAdjustment.frag
+        WorldParameters {
+        }
 
-    Technique {
-        VertexShader  GLSL100 GLSL110 GLSL120 GLSL130:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL100 GLSL110 GLSL120 GLSL130:  Common/MatDefs/Post/ContrastAdjustment.frag
+        Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
+            NUM_SAMPLES : NumSamples
+        }
     }
 }

+ 0 - 95
jme3-effects/src/main/resources/Common/MatDefs/Post/ContrastAdjustment15.frag

@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2009-2021 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.
- */
-
-/*
- * Used by ContrastAdjustment.j3md to adjust the color channels.
- *
- * First, the input range is normalized to upper and lower limits.
- * Then a power law is applied, using the exponent for each channel.
- * Finally, the output value is scaled linearly, using the scaling factor for each channel.
- *
- * Supports glsl150 and glsl150+ including android GLES glsl300, glsl310 and glsl320.
- */
-
-#import "Common/ShaderLib/GLSLCompat.glsllib"
-#import "Common/ShaderLib/MultiSample.glsllib"
-
-//constant inputs from java source
-uniform float m_redChannelExponent;
-uniform float m_greenChannelExponent;
-uniform float m_blueChannelExponent;
-
-//final scale values
-uniform float m_redChannelScale;
-uniform float m_greenChannelScale;
-uniform float m_blueChannelScale;
-
-//input range
-uniform float m_lowerLimit;
-uniform float m_upperLimit;
-
-//container for the input from post15.vert
-uniform COLORTEXTURE m_Texture;
-
-//varying input from post15.vert vertex shader
-in vec2 texCoord;
-
-//the output color
-out vec4 fragColor;
-
-void main() {
-    //get the color from a 2d sampler.
-    vec4 color = texture2D(m_Texture, texCoord);
-
-    //apply the color transfer function.
-
-    //1) adjust the channels input range
-    color.rgb = (color.rgb - vec3(m_lowerLimit)) / (vec3(m_upperLimit) - vec3(m_lowerLimit));
-
-    // avoid negative levels
-    color.r = max(color.r, 0.0);
-    color.g = max(color.g, 0.0);
-    color.b = max(color.b, 0.0);
-
-    //2) apply transfer functions on different channels.
-    color.r = pow(color.r, m_redChannelExponent);
-    color.g = pow(color.g, m_greenChannelExponent);
-    color.b = pow(color.b, m_blueChannelExponent);
-
-    //3) scale the output levels
-    color.r = color.r * m_redChannelScale;
-    color.b = color.b * m_blueChannelScale;
-    color.g = color.g * m_greenChannelScale;
-
-    //4) process the textures colors.
-    fragColor = color;
-}

+ 7 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/CrossHatch.j3md

@@ -1,6 +1,7 @@
 MaterialDef CrossHatch {
  
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Texture2D Texture;
         Vector4 LineColor;
@@ -18,11 +19,15 @@ MaterialDef CrossHatch {
     }
  
     Technique {
-        VertexShader GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL150 GLSL100: Common/MatDefs/Post/CrossHatch.frag
+        VertexShader   GLSL300 GLSL150 GLSL100 : Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100 : Common/MatDefs/Post/CrossHatch.frag
  
         WorldParameters {
         }
+
+        Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
+        }
     }
 
 }

+ 4 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/DepthOfField.j3md

@@ -1,6 +1,7 @@
 MaterialDef Depth Of Field {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Int NumSamplesDepth
         Texture2D Texture
@@ -14,14 +15,15 @@ MaterialDef Depth Of Field {
     }
 
     Technique {
-        VertexShader GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL150 GLSL100: Common/MatDefs/Post/DepthOfField.frag
+        VertexShader   GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100 : Common/MatDefs/Post/DepthOfField.frag
 
         WorldParameters {
             FrustumNearFar
         }
         
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             RESOLVE_MS : NumSamples
             RESOLVE_DEPTH_MS : NumSamplesDepth
             BLUR_THRESHOLD : BlurThreshold

+ 7 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/FXAA.j3md

@@ -1,5 +1,6 @@
 MaterialDef FXAA {
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Texture2D Texture
         Float SubPixelShift
@@ -8,10 +9,14 @@ MaterialDef FXAA {
         Float ReduceMul
     }
     Technique {
-        VertexShader GLSL300 GLSL100 GLSL150:   Common/MatDefs/Post/FXAA.vert
-        FragmentShader GLSL300 GLSL100 GLSL150: Common/MatDefs/Post/FXAA.frag
+        VertexShader   GLSL300 GLSL150 GLSL100:   Common/MatDefs/Post/FXAA.vert
+        FragmentShader GLSL300 GLSL150 GLSL100:   Common/MatDefs/Post/FXAA.frag
         WorldParameters {
             ResolutionInverse
         }
+
+        Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
+        }
     }
 }

+ 4 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/Fade.j3md

@@ -1,19 +1,21 @@
 MaterialDef Fade {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Texture2D Texture
         Float Value
     }
 
     Technique {
-        VertexShader GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL150 GLSL100: Common/MatDefs/Post/Fade.frag
+        VertexShader   GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Post/Fade.frag
 
         WorldParameters {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             RESOLVE_MS : NumSamples
         }
     }

+ 4 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/Fog.j3md

@@ -1,6 +1,7 @@
 MaterialDef Fade {
 
     MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Int NumSamplesDepth
         Texture2D Texture
@@ -11,13 +12,14 @@ MaterialDef Fade {
     }
 
     Technique {
-        VertexShader GLSL150 GLSL100:   Common/MatDefs/Post/Post.vert
-        FragmentShader GLSL150 GLSL100: Common/MatDefs/Post/Fog.frag
+        VertexShader   GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL300 GLSL150 GLSL100 :   Common/MatDefs/Post/Fog.frag
 
         WorldParameters {
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             RESOLVE_MS : NumSamples
             RESOLVE_DEPTH_MS : NumSamplesDepth
         }

+ 4 - 2
jme3-effects/src/main/resources/Common/MatDefs/Post/LightScattering.j3md

@@ -1,5 +1,6 @@
 MaterialDef Light Scattering {
- MaterialParameters {
+    MaterialParameters {
+        Int BoundDrawBuffer
         Int NumSamples
         Int NumSamplesDepth
         Texture2D Texture
@@ -14,13 +15,14 @@ MaterialDef Light Scattering {
     }
 
     Technique {
-        VertexShader GLSL300 GLSL150 GLSL120:   Common/MatDefs/Post/Post.vert
+        VertexShader   GLSL300 GLSL150 GLSL120:   Common/MatDefs/Post/Post.vert
         FragmentShader GLSL300 GLSL150 GLSL120: Common/MatDefs/Post/LightScattering.frag
 
         WorldParameters {          
         }
 
         Defines {
+            BOUND_DRAW_BUFFER: BoundDrawBuffer
             RESOLVE_MS : NumSamples
             RESOLVE_DEPTH_MS : NumSamplesDepth
             DISPLAY: Display

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