Browse Source

Merge pull request #306 from peedeeboy/feature/improve_gradle_project_wizard

Extend/improve Gradle project wizard
Toni Helenius 2 years ago
parent
commit
17a5b22994
29 changed files with 3440 additions and 4 deletions
  1. BIN
      jme3-templates/src/com/jme3/gde/templates/GradleDesktopGameProject.zip
  2. 91 0
      jme3-templates/src/com/jme3/gde/templates/files/freemarker/build.gradle.ftl
  3. 3 0
      jme3-templates/src/com/jme3/gde/templates/files/freemarker/settings.gradle.ftl
  4. 3 0
      jme3-templates/src/com/jme3/gde/templates/files/patchnotes/330-stable.html
  5. 20 0
      jme3-templates/src/com/jme3/gde/templates/files/patchnotes/332-stable.html
  6. 97 0
      jme3-templates/src/com/jme3/gde/templates/files/patchnotes/340-stable.html
  7. 21 0
      jme3-templates/src/com/jme3/gde/templates/files/patchnotes/341-stable.html
  8. 564 0
      jme3-templates/src/com/jme3/gde/templates/files/patchnotes/350-stable.html
  9. 19 0
      jme3-templates/src/com/jme3/gde/templates/files/patchnotes/351-stable.html
  10. 7 0
      jme3-templates/src/com/jme3/gde/templates/files/patchnotes/352-stable.html
  11. 12 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/Bundle.properties
  12. 112 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameAdditionalLibrariesPanel.java
  13. 85 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameAdditionalLibrariesPanelVisual.form
  14. 227 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameAdditionalLibrariesPanelVisual.java
  15. 111 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameGuiPanel.java
  16. 213 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameGuiPanelVisual.form
  17. 257 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameGuiPanelVisual.java
  18. 106 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameJMEVersionPanel.java
  19. 175 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameJMEVersionPanelVisual.form
  20. 266 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameJMEVersionPanelVisual.java
  21. 65 3
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameWizardIterator.java
  22. 1 1
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameWizardPanel.java
  23. 194 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/AdditionalLibrary.java
  24. 37 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/Bundle.properties
  25. 164 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/GUILibrary.java
  26. 126 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/JMEVersion.java
  27. 135 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/LWJGLVersion.java
  28. 166 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/NetworkingLibrary.java
  29. 163 0
      jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/PhysicsLibrary.java

BIN
jme3-templates/src/com/jme3/gde/templates/GradleDesktopGameProject.zip


+ 91 - 0
jme3-templates/src/com/jme3/gde/templates/files/freemarker/build.gradle.ftl

@@ -0,0 +1,91 @@
+<#-- FreeMarker template (see http://freemarker.org/) -->
+plugins {
+    id 'java'
+    id 'application'
+}
+
+group 'com.mygame'
+version '1.0'
+
+mainClassName = "com.mygame.Main"
+
+repositories {
+    mavenCentral()
+    jcenter()
+    maven { url 'https://jitpack.io' }
+}
+
+project.ext {
+  jmeVer = '${jmeVersion}'
+}
+
+project(":assets") {
+    apply plugin: "java"
+
+    buildDir = rootProject.file("build/assets")
+
+    sourceSets {
+        main {
+            resources {
+                srcDir '.'
+            }
+        }
+    }
+}
+
+dependencies {
+
+  // Core JME
+  implementation "org.jmonkeyengine:jme3-core:$jmeVer"
+  implementation "org.jmonkeyengine:jme3-desktop:$jmeVer"
+  implementation "${lwjglArtifact}:$jmeVer"
+
+  // Suppress errors / warnings building in SDK
+  implementation "org.jmonkeyengine:jme3-jogg:$jmeVer"
+  implementation "org.jmonkeyengine:jme3-plugins:$jmeVer"
+  <#if guiLibrary.label != "">
+  
+  // GUI Library
+  <#if guiLibrary.isCoreJmeLibrary == true>
+  implementation "${guiLibrary.artifact}:$jmeVer"
+  <#else>
+  implementation "${guiLibrary.artifact}"
+  </#if>
+  </#if>
+  <#if physicsLibrary.label != "">
+  
+  // Physics Library
+  <#if physicsLibrary.isCoreJmeLibrary == true>
+  implementation "${physicsLibrary.artifact}:$jmeVer"
+  <#else>
+  implementation "${physicsLibrary.artifact}"
+  </#if>
+  </#if>
+  <#if networkingLibrary.label != "">
+  
+  // Networking Library
+  <#if networkingLibrary.isCoreJmeLibrary == true>
+  implementation "${networkingLibrary.artifact}:$jmeVer"
+  <#else>
+  implementation "${networkingLibrary.artifact}"
+  </#if>
+  </#if>
+
+  // Additional Libraries
+  <#list additionalLibraries as additionalLibrary>
+  <#if additionalLibrary.isCoreJmeLibrary == true>
+  implementation "${additionalLibrary.artifact}:$jmeVer"
+  <#else>
+  implementation "${additionalLibrary.artifact}"
+  </#if>
+  </#list>
+
+  // Assets sub-project
+  runtimeOnly project(':assets')
+}
+
+jar {
+    manifest {
+        attributes 'Main-Class': "$mainClassName"
+    }
+}

+ 3 - 0
jme3-templates/src/com/jme3/gde/templates/files/freemarker/settings.gradle.ftl

@@ -0,0 +1,3 @@
+<#-- FreeMarker template (see http://freemarker.org/) -->
+rootProject.name = '${name}'
+include 'assets'

+ 3 - 0
jme3-templates/src/com/jme3/gde/templates/files/patchnotes/330-stable.html

@@ -0,0 +1,3 @@
+<html>
+    <p>This is the stable release of v3.3.0</p>
+</html>

+ 20 - 0
jme3-templates/src/com/jme3/gde/templates/files/patchnotes/332-stable.html

@@ -0,0 +1,20 @@
+<html>
+    <p>A production-quality JMonkeyEngine 3.3 patch release with the following changes relative to 3.3.0-stable:</p>
+    <ul>
+        <li>low-risk bugfixes in the libraries
+            <ul>
+                <li><a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1304" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1304/hovercard">Issue 1304</a>, <a href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1321" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1321/hovercard">PR 1321</a>, <a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1340" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1340/hovercard">Issue 1340</a>:  PBR shader fails to compile</li>
+                <li><a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1347" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1347/hovercard">Issue 1347</a>: <code>ClassCastException</code> in <code>TerrainPath.reIndexGeometry()</code></li>
+                <li><a href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1338" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1338/hovercard">PR 1338</a>: <code>AndroidNativeImageLoader</code> attempts to open stream 2x</li>
+                <li><a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1341" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1341/hovercard">Issue 1341</a>: indirect audio buffer causes OpenAL to crash</li>
+                <li><a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/995" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/995/hovercard">Issue 995</a>: unclosed query in <code>DetailedProfiler</code></li>
+                <li><a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1272" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1272/hovercard">Issue 1272</a>: renderer mistakenly believes it is on mobile</li>
+                <li><a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1319" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1319/hovercard">Issue 1319</a>: deadlock if context initialization fails</li>
+                <li><a href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1322" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1322/hovercard">PR 1322</a>: <code>GL_INVALID_ENUM</code> in debug mode</li>
+                <li><a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1360" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1360/hovercard">Issue 1360</a>: <code>ArmatureDebugAppState</code> causes <code>OpenGLException</code></li>
+            </ul>
+        </li>
+        <li>improvements to tests and examples</li>
+        <li>improvements to javadoc and code formatting</li>
+    </ul>
+</html>

File diff suppressed because it is too large
+ 97 - 0
jme3-templates/src/com/jme3/gde/templates/files/patchnotes/340-stable.html


+ 21 - 0
jme3-templates/src/com/jme3/gde/templates/files/patchnotes/341-stable.html

@@ -0,0 +1,21 @@
+<html>
+    <p>A production-quality JMonkeyEngine 3.4 patch release with the following changes relative to 3.4.0-stable:</p>
+    <ul>
+        <li>
+            <p>Bugs and defects addressed:</p>
+            <ul>
+                <li>crashes while deleting native objects (issue <a  data-error-text="Failed to load title" data-id="1014494021" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1614" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1614/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1614">#1614</a>, PR <a  data-error-text="Failed to load title" data-id="1020231972" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1617" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1617/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1617">#1617</a>)</li>
+                <li>can't recover from a disconnected audio device (PR <a  data-error-text="Failed to load title" data-id="957261545" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1593" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1593/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1593">#1593</a>)</li>
+                <li>Android devices can't load assets whose names start with "/" (issue <a  data-error-text="Failed to load title" data-id="108134879" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/352" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/352/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/352">#352</a>, PR <a  data-error-text="Failed to load title" data-id="926525496" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1577" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1577/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1577">#1577</a>)</li>
+                <li>assertion failure while loading a valid glTF model (issue <a  data-error-text="Failed to load title" data-id="443113067" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1089" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1089/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1089">#1089</a>)</li>
+            </ul>
+        </li>
+        <li>
+            <p>General improvements:</p>
+            <ul>
+                <li>faster asset loading of AWT-based texture assets (PR <a  data-error-text="Failed to load title" data-id="1013568571" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1613" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1613/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1613">#1613</a>)</li>
+                <li>improved performance in Node.java (issue <a  data-error-text="Failed to load title" data-id="1061458869" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1672" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1672/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1672">#1672</a>, PR <a  data-error-text="Failed to load title" data-id="1061608071" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1673" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1673/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1673">#1673</a>)</li>
+            </ul>
+        </li>
+    </ul>
+</html>

+ 564 - 0
jme3-templates/src/com/jme3/gde/templates/files/patchnotes/350-stable.html

@@ -0,0 +1,564 @@
+<html>
+    <p>A production-quality release of JMonkeyEngine with the following noteworthy changes relative to v3.4.1-stable:</p>
+    <ul>
+        <li>
+            <p>Upgrade considerations (potential breaking changes):</p>
+            <ul>
+                <li>explicitly requires Java v8 or higher (issue <a 
+                        data-error-text="Failed to load title" data-id="1057648305" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1661" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1661/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1661">#1661</a>)</li>
+                <li>cannot create a GL window on armhf (issue <a 
+                        data-error-text="Failed to load title" data-id="1083920706" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1710" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1710/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1710">#1710</a>)</li>
+                <li>to use the "jme3-lwjgl3" library in a Gradle build, you'll need Gradle v6.3 or later</li>
+                <li>more precise return types for 13 <code>clone()</code> methods (issue <a 
+                        data-error-text="Failed to load title" data-id="1061740053" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1676" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1676/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1676">#1676</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1063940115"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1683"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1683/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1683">#1683</a>)</li>
+                <li>changed the semantics of <code>FastMath.sphericalToCartesianZ()</code> (issue <a
+                        data-error-text="Failed to load title" data-id="602199862"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1349" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1349/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1349">#1349</a>, PR <a
+                        data-error-text="Failed to load title" data-id="917149209"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1573"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1573/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1573">#1573</a>)</li>
+                <li>deleted the <code>Weights</code> class, which was deprecated in v3.4 (issue <a
+                        data-error-text="Failed to load title" data-id="1027670362"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1620"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1620/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1620">#1620</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1034739447"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1632"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1632/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1632">#1632</a>)</li>
+                <li>protected the no-arg constructor of <code>TransformTrack</code> (PR <a 
+                        data-error-text="Failed to load title" data-id="867296253" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1531"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1531/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1531">#1531</a>)</li>
+            </ul>
+        </li>
+        <li>
+            <p>New features:</p>
+            <ul>
+                <li>enhancements to <code>SurfaceView</code> for Android (PR <a 
+                        data-error-text="Failed to load title" data-id="1052174015" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1650"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1650/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1650">#1650</a>), including:
+                    <ul>
+                        <li>optional binding to activity lifecycle</li>
+                        <li>added listeners for "layout drawn" and "renderer started"</li>
+                        <li>new <code>DestructionPolicy</code> applied when exiting an activity</li>
+                        <li>add full stack trace and a "copy log" button to the error dialog</li>
+                        <li>added settings for "show error dialog", "exit on Esc pressed", and "show Esc exit prompt"</li>
+                        <li>added the <code>getGlEsVersion()</code> method</li>
+                    </ul>
+                </li>
+                <li>determine the maximum supported width for lines (issue <a 
+                        data-error-text="Failed to load title" data-id="891292089" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1555" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1555/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1555">#1555</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1046458584"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1643"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1643/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1643">#1643</a>)</li>
+                <li>support for additional compressed texture formats (issue <a 
+                        data-error-text="Failed to load title" data-id="1025045511" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1619" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1619/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1619">#1619</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1028124715"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1621"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1621/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1621">#1621</a>)</li>
+                <li>texture flipping for certain RGTC formats (PR <a 
+                        data-error-text="Failed to load title" data-id="1034361791" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1629"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1629/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1629">#1629</a>)</li>
+                <li>app settings to position the initial window in LWJGL v3 (issue <a 
+                        data-error-text="Failed to load title" data-id="1059679493" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1668"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1668/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1668">#1668</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1059722428"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1670"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1670/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1670">#1670</a>)</li>
+                <li>a color-adjustment filter (issue <a 
+                        data-error-text="Failed to load title" data-id="278709787" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/781" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/781/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/781">#781</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1058624724"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1665"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1665/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1665">#1665</a>, <a 
+                        data-hovercard-type="commit"
+                        data-hovercard-url="https://github.com/jMonkeyEngine/jmonkeyengine/commit/a9e88b0457aefbe79d13e2d4ce145683335e1ca1/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/commit/a9e88b0457aefbe79d13e2d4ce145683335e1ca1"><tt>a9e88b0</tt></a>)
+                </li>
+                <li><code>RectangleMesh</code> class for custom quads (PR <a 
+                        data-error-text="Failed to load title" data-id="1073875428" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1701"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1701/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1701">#1701</a>)</li>
+                <li><code>calculateD()</code> and <code>calculateNormal()</code> methods in <code>Rectangle</code> class (PR
+                    <a  data-error-text="Failed to load title" data-id="1073875428"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1701"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1701/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1701">#1701</a>)</li>
+                <li><code>CenterQuad</code> mesh in jme3-core (PR <a 
+                        data-error-text="Failed to load title" data-id="1077427678" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1704"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1704/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1704">#1704</a>)</li>
+                <li>added <code>getNbMorphTargets()</code>, <code>setKeyframesWeight()</code>, and
+                    <code>setNbMorphTargets()</code> to the <code>MorphTrack</code> class (PR <a
+                        data-error-text="Failed to load title" data-id="1006902354"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1608"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1608/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1608">#1608</a>)</li>
+                <li>made <code>getOffsetTransform()</code> and <code>getAttachmentsNode()</code> accessible to subclasses of
+                    <code>Joint</code> (PR <a  data-error-text="Failed to load title"
+                                               data-id="871287256" data-permission-text="Title is private"
+                                               data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1536"
+                                               data-hovercard-type="pull_request"
+                                               data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1536/hovercard"
+                                               href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1536">#1536</a>)</li>
+                <li>distinguish ARM macOS/Windows from other platforms (issue <a 
+                        data-error-text="Failed to load title" data-id="864564825" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1528" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1528/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1528">#1528</a>, PR <a
+                        data-error-text="Failed to load title" data-id="865710101"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1530"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1530/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1530">#1530</a>)</li>
+                <li><code>Platform.getOs()</code> to determine the generic OS name (<a 
+                        data-hovercard-type="commit"
+                        data-hovercard-url="https://github.com/jMonkeyEngine/jmonkeyengine/commit/cc75352428dc43edbe44c44deeb698840cf1e863/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/commit/cc75352428dc43edbe44c44deeb698840cf1e863"><tt>cc75352</tt></a>)
+                </li>
+                <li>convert inner class Layer to a top-level class (issue <a 
+                        data-error-text="Failed to load title" data-id="901002309" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1566" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1566/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1566">#1566</a>, PRs <a
+                        data-error-text="Failed to load title" data-id="1054228467"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1656"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1656/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1656">#1656</a> and <a
+                        data-error-text="Failed to load title" data-id="1094825638"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1730"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1730/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1730">#1730</a>)</li>
+            </ul>
+        </li>
+        <li>
+            <p>Bugs and defects addressed:</p>
+            <ul>
+                <li>LWJGL v3 hangs while creating a window (issues <a 
+                        data-error-text="Failed to load title" data-id="496487822" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1193" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1193/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1193">#1193</a>, <a
+                        data-error-text="Failed to load title" data-id="851655366"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1514" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1514/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1514">#1514</a>, and <a
+                        data-error-text="Failed to load title" data-id="892362014"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1558" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1558/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1558">#1558</a>; PRs <a
+                        data-error-text="Failed to load title" data-id="1067978375"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1690"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1690/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1690">#1690</a> and <a
+                        data-error-text="Failed to load title" data-id="1089004840"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1722"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1722/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1722">#1722</a>)</li>
+                <li>incorrect cursor position on macOS with Retina (issue <a 
+                        data-error-text="Failed to load title" data-id="358296166" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/893" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/893/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/893">#893</a>, PRs <a
+                        data-error-text="Failed to load title" data-id="997059118"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1607"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1607/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1607">#1607</a>, <a
+                        data-error-text="Failed to load title" data-id="1105175486"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1746"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1746/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1746">#1746</a>, and <a
+                        data-error-text="Failed to load title" data-id="1106311573"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1753"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1753/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1753">#1753</a>; <a 
+                        data-hovercard-type="commit"
+                        data-hovercard-url="https://github.com/jMonkeyEngine/jmonkeyengine/commit/c44d74b4f89561093f08e5e8b1e3cd14e0f9411a/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/commit/c44d74b4f89561093f08e5e8b1e3cd14e0f9411a"><tt>c44d74b</tt></a>)
+                </li>
+                <li>Right-to-Left and cursive fonts displayed incorrectly (issue <a 
+                        data-error-text="Failed to load title" data-id="479299479" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1158" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1158/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1158">#1158</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1065158338"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1685"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1685/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1685">#1685</a>)</li>
+                <li>incorrect <code>VirtualIndexBuffer</code> for a <code>LineLoop</code> mesh (issue <a
+                        data-error-text="Failed to load title" data-id="988355372"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1603" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1603/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1603">#1603</a>, PR <a
+                        data-error-text="Failed to load title" data-id="993936335"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1605"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1605/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1605">#1605</a>)</li>
+                <li><code>ChaseCamera</code> won't rotate after being re-enabled (PR <a 
+                        data-error-text="Failed to load title" data-id="1070191068" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1692"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1692/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1692">#1692</a>)</li>
+                <li>ineffective audio filters (issue <a 
+                        data-error-text="Failed to load title" data-id="1071581812" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1699" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1699/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1699">#1699</a>, PRs <a
+                        data-error-text="Failed to load title" data-id="1079269586"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1707"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1707/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1707">#1707</a> and <a
+                        data-error-text="Failed to load title" data-id="1092889410"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1728"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1728/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1728">#1728</a>)</li>
+                <li><code>NullPointerException</code> in <code>reloadJoysticks()</code> when joystick lacks buttons (issue
+                    <a  data-error-text="Failed to load title" data-id="1099432283"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1739" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1739/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1739">#1739</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1099505321"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1740"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1740/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1740">#1740</a>)</li>
+                <li><code>LwjglCanvas</code> doesn't initialize OpenCL for LWJGL v2 (PR <a 
+                        data-error-text="Failed to load title" data-id="1088509985" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1720"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1720/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1720">#1720</a>)</li>
+                <li><code>sphericalToCartesianZ()</code> doesn't reverse <code>cartesianZToSpherical()</code> (issue <a
+                        data-error-text="Failed to load title" data-id="602199862"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1349" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1349/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1349">#1349</a>, PR <a
+                        data-error-text="Failed to load title" data-id="917149209"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1573"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1573/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1573">#1573</a>)</li>
+                <li>null-pointer exceptions while loading erroneous glTF models:
+                    <ul>
+                        <li>in <code>GltfLoader.findChildren()</code> (PR <a 
+                                data-error-text="Failed to load title" data-id="813234842"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1471"
+                                data-hovercard-type="pull_request"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1471/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1471">#1471</a>)</li>
+                        <li>in <code>SkinningControl.getAttachmentsNode()</code> (issue <a 
+                                data-error-text="Failed to load title" data-id="1011968207"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1612"
+                                data-hovercard-type="issue"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1612/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1612">#1612</a>, PR <a
+                                data-error-text="Failed to load title" data-id="1046180255"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1639"
+                                data-hovercard-type="pull_request"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1639/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1639">#1639</a>)</li>
+                    </ul>
+                </li>
+                <li>cryptic crash when more than 16 textures are used (issue <a 
+                        data-error-text="Failed to load title" data-id="30067689" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/37" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/37/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/37">#37</a>, PR <a
+                        data-error-text="Failed to load title" data-id="918243637"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1574"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1574/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1574">#1574</a>)</li>
+                <li>error 1280 in macOS querying <code>GL_FRAMEBUFFER_SRGB_CAPABLE_EXT</code> (PR <a
+                        data-error-text="Failed to load title" data-id="1105685727"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1749"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1749/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1749">#1749</a>)</li>
+                <li><code>fromJmeKeyCode()</code> returns garbage for unknown keys (issue <a
+                        data-error-text="Failed to load title" data-id="975993582"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1596" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1596/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1596">#1596</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1045487902"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1638"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1638/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1638">#1638</a>)</li>
+                <li>serialization bugs:
+                    <ul>
+                        <li><code>CartoonEdgeFilter</code> class (issue <a 
+                                data-error-text="Failed to load title" data-id="1046197347"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1640"
+                                data-hovercard-type="issue"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1640/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1640">#1640</a>, PR <a
+                                data-error-text="Failed to load title" data-id="1048938903"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1647"
+                                data-hovercard-type="pull_request"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1647/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1647">#1647</a>)</li>
+                        <li><code>RenderState</code> class (issues <a 
+                                data-error-text="Failed to load title" data-id="1088441789"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1718"
+                                data-hovercard-type="issue"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1718/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1718">#1718</a> and <a
+                                data-error-text="Failed to load title" data-id="1089050335"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1723"
+                                data-hovercard-type="issue"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1723/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1723">#1723</a>; PRs <a
+                                data-error-text="Failed to load title" data-id="1088457150"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1719"
+                                data-hovercard-type="pull_request"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1719/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1719">#1719</a> and <a
+                                data-error-text="Failed to load title" data-id="1089485500"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1726"
+                                data-hovercard-type="pull_request"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1726/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1726">#1726</a>)</li>
+                        <li><code>ShaderNodeVariable</code> class (PR <a 
+                                data-error-text="Failed to load title" data-id="938321778"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1585"
+                                data-hovercard-type="pull_request"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1585/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1585">#1585</a>)</li>
+                        <li><code>Torus</code> class (PR <a 
+                                data-error-text="Failed to load title" data-id="938324019"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1586"
+                                data-hovercard-type="pull_request"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1586/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1586">#1586</a>)</li>
+                        <li><code>SkeletonInterBoneWire</code>, <code>SkeletonPoints</code>, <code>SkeletonWire</code>,
+                            <code>ArmatureInterJointsWire</code>, <code>Surface</code>, and <code>CenterQuad</code> (issue
+                            <a  data-error-text="Failed to load title" data-id="1077600885"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1705"
+                                data-hovercard-type="issue"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1705/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1705">#1705</a>, PR <a
+                                data-error-text="Failed to load title" data-id="1106636726"
+                                data-permission-text="Title is private"
+                                data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1754"
+                                data-hovercard-type="pull_request"
+                                data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1754/hovercard"
+                                href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1754">#1754</a>)</li>
+                    </ul>
+                </li>
+                <li>typo in the value returned by <code>LodGenerator.toString()</code> (<a 
+                        data-hovercard-type="commit"
+                        data-hovercard-url="https://github.com/jMonkeyEngine/jmonkeyengine/commit/cfa497db246e9f74a2939ea87af159611127d232/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/commit/cfa497db246e9f74a2939ea87af159611127d232"><tt>cfa497d</tt></a>)
+                </li>
+                <li>typos in logger/exception messages (PRs <a 
+                        data-error-text="Failed to load title" data-id="938317301" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1584"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1584/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1584">#1584</a> and <a
+                        data-error-text="Failed to load title" data-id="1098251510"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1737"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1737/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1737">#1737</a>, <a 
+                        data-hovercard-type="commit"
+                        data-hovercard-url="https://github.com/jMonkeyEngine/jmonkeyengine/commit/3beca2b4ee6ac089c65791f22dcfcc4c516501be/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/commit/3beca2b4ee6ac089c65791f22dcfcc4c516501be"><tt>3beca2b</tt></a>)
+                </li>
+            </ul>
+        </li>
+        <li>
+            <p>General improvements:</p>
+            <ul>
+                <li>upgraded LWJGL to v3.3.0 (PR <a  data-error-text="Failed to load title"
+                                                     data-id="1055309183" data-permission-text="Title is private"
+                                                     data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1659"
+                                                     data-hovercard-type="pull_request"
+                                                     data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1659/hovercard"
+                                                     href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1659">#1659</a>)</li>
+                <li>faster asset loading on Android devices (issue <a 
+                        data-error-text="Failed to load title" data-id="1028244436" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1622" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1622/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1622">#1622</a>, PR <a
+                        data-error-text="Failed to load title" data-id="1034097959"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1627"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1627/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1627">#1627</a>)</li>
+                <li>faster writing of MJPEG files (PR <a 
+                        data-error-text="Failed to load title" data-id="956895351" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1592"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1592/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1592">#1592</a>)</li>
+                <li>more efficient logging (issues <a 
+                        data-error-text="Failed to load title" data-id="1061458869" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1672" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1672/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1672">#1672</a> and <a
+                        data-error-text="Failed to load title" data-id="1061879063"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1680" data-hovercard-type="issue"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1680/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1680">#1680</a>, PRs <a
+                        data-error-text="Failed to load title" data-id="1061608071"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1673"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1673/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1673">#1673</a> and <a
+                        data-error-text="Failed to load title" data-id="1063651872"
+                        data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1682"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1682/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1682">#1682</a>)</li>
+                <li>un-deprecate <code>AndroidBufferImageLoader</code> (PR <a 
+                        data-error-text="Failed to load title" data-id="1056479795" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1660"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1660/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1660">#1660</a>)</li>
+                <li>numerous javadoc improvements</li>
+                <li>improvements to examples and tests</li>
+            </ul>
+        </li>
+        <li>
+            <p>New deprecations:</p>
+            <ul>
+                <li><code>Quaternion.negate()</code> (PR <a 
+                        data-error-text="Failed to load title" data-id="1077203051" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1702"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1702/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1702">#1702</a>)</li>
+                <li><code>BitmapText(BitmapFont font, boolean rightToLeft)</code> (PR <a 
+                        data-error-text="Failed to load title" data-id="1065158338" data-permission-text="Title is private"
+                        data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1685"
+                        data-hovercard-type="pull_request"
+                        data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1685/hovercard"
+                        href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1685">#1685</a>)</li>
+                <li><code>CenterQuad</code> mesh in jme3-vr (<a  data-hovercard-type="commit"
+                                                                 data-hovercard-url="https://github.com/jMonkeyEngine/jmonkeyengine/commit/3097f05ef1ab7d74b2c4085a211a66eff30628f9/hovercard"
+                                                                 href="https://github.com/jMonkeyEngine/jmonkeyengine/commit/3097f05ef1ab7d74b2c4085a211a66eff30628f9"><tt>3097f05</tt></a>)
+                </li>
+            </ul>
+        </li>
+    </ul>
+</html>

+ 19 - 0
jme3-templates/src/com/jme3/gde/templates/files/patchnotes/351-stable.html

@@ -0,0 +1,19 @@
+<html>
+    <p>A production-quality JMonkeyEngine 3.5 patch release with the following changes relative to 3.5.0-stable:</p>
+    <ul>
+        <li>
+            <p>Bugs and defects addressed:</p>
+            <ul>
+                <li>various built-in shaders don't support core profile (issue <a data-error-text="Failed to load title" data-id="1162267916" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1780" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1780/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1780">#1780</a>, PR <a data-error-text="Failed to load title" data-id="1163375710" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1781" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1781/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1781">#1781</a>)</li>
+                <li><code>AssertionError</code> in <code>ALAudioRenderer</code> (issue <a data-error-text="Failed to load title" data-id="1111893425" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1761" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1761/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1761">#1761</a>, PR <a data-error-text="Failed to load title" data-id="1114142297" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1762" data-hovercard-type="pull_request" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/pull/1762/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1762">#1762</a>)</li>
+            </ul>
+        </li>
+        <li>
+            <p>General improvements:</p>
+            <ul>
+                <li>enabled native physics for "Apple Silicon" Macs (issue <a data-error-text="Failed to load title" data-id="864564825" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1528" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1528/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1528">#1528</a>, <a data-hovercard-type="commit" data-hovercard-url="https://github.com/jMonkeyEngine/jmonkeyengine/commit/b40a91d2d26bffd7de7842e2eb7dbdcd45314d98/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/commit/b40a91d2d26bffd7de7842e2eb7dbdcd45314d98"><tt>b40a91d</tt></a>)</li>
+                <li>added LWJGL v3 natives for "Apple Silicon" Macs (issue <a data-error-text="Failed to load title" data-id="864564825" data-permission-text="Title is private" data-url="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1528" data-hovercard-type="issue" data-hovercard-url="/jMonkeyEngine/jmonkeyengine/issues/1528/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1528">#1528</a>, <a data-hovercard-type="commit" data-hovercard-url="https://github.com/jMonkeyEngine/jmonkeyengine/commit/de2e2d38c8622dac588d414ab6fb89efee7bfb6a/hovercard" href="https://github.com/jMonkeyEngine/jmonkeyengine/commit/de2e2d38c8622dac588d414ab6fb89efee7bfb6a"><tt>de2e2d3</tt></a>)</li>
+            </ul>
+        </li>
+    </ul>
+</html>

+ 7 - 0
jme3-templates/src/com/jme3/gde/templates/files/patchnotes/352-stable.html

@@ -0,0 +1,7 @@
+<html>
+    <p>A production-quality JMonkeyEngine 3.5 patch release with the following changes relative to 3.5.1-stable:</p>
+    <ul>
+        <li>solved <a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1710">issue 1710 - can't create windows on armhf platforms</a> by upgrading LWJGL to v3.3.1</li>
+        <li>solved <a href="https://github.com/jMonkeyEngine/jmonkeyengine/issues/1788">issue 1788 - mipmaps generated for wrong framebuffer</a> by backporting <a href="https://github.com/jMonkeyEngine/jmonkeyengine/pull/1791">PR 1791</a></li>
+    </ul>
+</html>

+ 12 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/Bundle.properties

@@ -4,3 +4,15 @@ GradleDesktopGamePanelVisual.createdFolderLabel.text=Project &Folder:
 GradleDesktopGamePanelVisual.projectNameLabel.text=Project &Name:
 GradleDesktopGamePanelVisual.projectLocationLabel.text=Project &Location:
 GradleDesktopGamePanelVisual.browseButton.actionCommand=BROWSE
+
+LBL_ChooseEngineStep=Engine
+GradleDesktopGameJMEVersionPanelVisual.lwjglVersionLabel.text=&LWJGL Version:
+GradleDesktopGameJMEVersionPanelVisual.jmeVersionDescriptionTextPane.text=
+GradleDesktopGameJMEVersionPanelVisual.jmeVersionLabel.text=jMonkeyEngine &Version:
+
+LBL_ChooseGuiStep=GUI / Physics / Networking
+GradleDesktopGameGuiPanelVisual.guiLabel.text=&Graphical User Interface:
+GradleDesktopGameGuiPanelVisual.physicsEngineLabel.text=&Physics Engine:
+GradleDesktopGameGuiPanelVisual.networkingLabel.text=&Networking:
+
+LBL_ChooseAdditionalLibrariesStep=Additional Libraries

+ 112 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameAdditionalLibrariesPanel.java

@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop;
+
+import java.awt.Component;
+import javax.swing.event.ChangeListener;
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.HelpCtx;
+import org.openide.util.NbBundle;
+
+/**
+ * New Gradle Game Wizard Panel for selecting additional jMonkeyEngine
+ * and 3rd party recommended libraries.
+ *
+ * @author peedeeboy
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class GradleDesktopGameAdditionalLibrariesPanel implements 
+        WizardDescriptor.Panel, WizardDescriptor.ValidatingPanel,
+        WizardDescriptor.FinishablePanel {
+
+    /**
+     * JPanel containing the Additional Libraries UI
+     */
+    private GradleDesktopGameAdditionalLibrariesPanelVisual component;
+
+    public GradleDesktopGameAdditionalLibrariesPanel() {
+    }
+
+    @Override
+    public Component getComponent() {
+        if (component == null) {
+            component = new GradleDesktopGameAdditionalLibrariesPanelVisual(this);
+            component.setName(NbBundle.getMessage(
+                    GradleDesktopGameAdditionalLibrariesPanel.class, 
+                    "LBL_ChooseAdditionalLibrariesStep"));
+        }
+        return component;
+    }
+
+    @Override
+    public HelpCtx getHelp() {
+        return new HelpCtx("sdk.project_creation");
+    }
+
+    @Override
+    public void readSettings(Object settings) {
+    }
+
+    @Override
+    public void storeSettings(Object settings) {
+        WizardDescriptor d = (WizardDescriptor) settings;
+        component.store(d);
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    @Override
+    public void addChangeListener(ChangeListener listener) {
+        // Not required - no validation on this panel
+    }
+
+    @Override
+    public void removeChangeListener(ChangeListener listener) {
+        // Not required - no validation on this panel
+    }
+
+    @Override
+    public void validate() throws WizardValidationException {
+        // Not required - no validation on this panel
+    }
+
+    @Override
+    public boolean isFinishPanel() {
+        return true;
+    }
+
+}

+ 85 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameAdditionalLibrariesPanelVisual.form

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="additionalLibraryScrollPane" pref="0" max="32767" attributes="0"/>
+                  <Component id="jSeparator1" max="32767" attributes="0"/>
+                  <Component id="libraryDescriptionScrollPane" alignment="0" pref="388" max="32767" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="additionalLibraryScrollPane" pref="142" max="32767" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="jSeparator1" min="-2" pref="10" max="-2" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="libraryDescriptionScrollPane" pref="75" max="32767" attributes="0"/>
+              <EmptySpace min="-2" pref="55" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Container class="javax.swing.JScrollPane" name="additionalLibraryScrollPane">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTable" name="additionalLibraryTable">
+          <Properties>
+            <Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
+              <Table columnCount="0" rowCount="0"/>
+            </Property>
+            <Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor">
+              <JTableSelectionModel selectionMode="0"/>
+            </Property>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JSeparator" name="jSeparator1">
+    </Component>
+    <Container class="javax.swing.JScrollPane" name="libraryDescriptionScrollPane">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextArea" name="libraryDescriptionTextArea">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+            <Property name="columns" type="int" value="20"/>
+            <Property name="lineWrap" type="boolean" value="true"/>
+            <Property name="rows" type="int" value="3"/>
+            <Property name="wrapStyleWord" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

+ 227 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameAdditionalLibrariesPanelVisual.java

@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop;
+
+import com.jme3.gde.templates.gradledesktop.options.AdditionalLibrary;
+import java.awt.Dimension;
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.GroupLayout;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.LayoutStyle;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.DefaultTableModel;
+import org.openide.WizardDescriptor;
+
+/**
+ * UI Component for the New Gradle Game additional libraries panel.
+ *
+ * @author peedeeboy
+ */
+public class GradleDesktopGameAdditionalLibrariesPanelVisual extends JPanel
+        implements ListSelectionListener {
+
+    private final int ROW_PADDING = 4;
+    
+    /**
+     * Creates new form GradleDesktopGameAdditionalLibrariesPanelVisual
+     */
+    public GradleDesktopGameAdditionalLibrariesPanelVisual(
+            GradleDesktopGameAdditionalLibrariesPanel panel) {
+        initComponents();
+
+        populateLibraryTable();
+        additionalLibraryTable.getSelectionModel()
+                .addListSelectionListener(this);
+        additionalLibraryTable.setRowHeight(
+                additionalLibraryTable.getRowHeight() + ROW_PADDING);
+        additionalLibraryTable.setIntercellSpacing(new Dimension(0, 0));
+        additionalLibraryTable.getParent().setBackground(
+                additionalLibraryTable.getBackground());
+        additionalLibraryTable.getColumnModel().getColumn(0).setMaxWidth(30);
+        additionalLibraryTable.getTableHeader().setUI(null);
+        additionalLibraryTable.repaint();
+    }
+
+    private void populateLibraryTable() {
+        int noRows = AdditionalLibrary.values().length;
+        Object[][] tableData = new Object[noRows][2];
+
+        int row = 0;
+        for (AdditionalLibrary library : AdditionalLibrary.values()) {
+            tableData[row][0] = Boolean.FALSE;
+            tableData[row][1] = library;
+            row++;
+        }
+
+        String[] cols = {"", ""};
+        AdditionalLibraryTableModel model = new AdditionalLibraryTableModel(tableData, cols);
+        additionalLibraryTable.setModel(model);
+        additionalLibraryTable.repaint();
+    }
+
+    @Override
+    public void valueChanged(ListSelectionEvent event) {
+        updateLibraryDescription();
+    }
+
+    private void updateLibraryDescription() {
+        int selectedRow = additionalLibraryTable.getSelectedRow();
+
+        if (selectedRow == -1) {
+            libraryDescriptionTextArea.setText("");
+        } else {
+            AdditionalLibrary selectedLibrary = (AdditionalLibrary)
+                    additionalLibraryTable.getValueAt(selectedRow, 1);
+            libraryDescriptionTextArea.setText(selectedLibrary
+                    .getDescription());
+        }
+    }
+
+    protected void store(WizardDescriptor d) {
+        AdditionalLibraryTableModel model = (AdditionalLibraryTableModel)
+                additionalLibraryTable.getModel();
+        List<AdditionalLibrary> selectedLibraries =
+                model.getSelectedLibraries();
+
+        d.putProperty("additionalLibraries", selectedLibraries);
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form EditorjmeVersion.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        additionalLibraryScrollPane = new JScrollPane();
+        additionalLibraryTable = new JTable();
+        jSeparator1 = new JSeparator();
+        libraryDescriptionScrollPane = new JScrollPane();
+        libraryDescriptionTextArea = new JTextArea();
+
+        additionalLibraryTable.setModel(new DefaultTableModel(
+            new Object [][] {
+
+            },
+            new String [] {
+
+            }
+        ));
+        additionalLibraryTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        additionalLibraryScrollPane.setViewportView(additionalLibraryTable);
+
+        libraryDescriptionTextArea.setEditable(false);
+        libraryDescriptionTextArea.setColumns(20);
+        libraryDescriptionTextArea.setLineWrap(true);
+        libraryDescriptionTextArea.setRows(3);
+        libraryDescriptionTextArea.setWrapStyleWord(true);
+        libraryDescriptionScrollPane.setViewportView(libraryDescriptionTextArea);
+
+        GroupLayout layout = new GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                    .addComponent(additionalLibraryScrollPane, GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
+                    .addComponent(jSeparator1)
+                    .addComponent(libraryDescriptionScrollPane, GroupLayout.DEFAULT_SIZE, 388, Short.MAX_VALUE))
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(additionalLibraryScrollPane, GroupLayout.DEFAULT_SIZE, 142, Short.MAX_VALUE)
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jSeparator1, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(libraryDescriptionScrollPane, GroupLayout.DEFAULT_SIZE, 75, Short.MAX_VALUE)
+                .addGap(55, 55, 55))
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private JScrollPane additionalLibraryScrollPane;
+    private JTable additionalLibraryTable;
+    private JSeparator jSeparator1;
+    private JScrollPane libraryDescriptionScrollPane;
+    private JTextArea libraryDescriptionTextArea;
+    // End of variables declaration//GEN-END:variables
+
+    private static final class AdditionalLibraryTableModel
+            extends DefaultTableModel {
+
+        public AdditionalLibraryTableModel(Object[][] data,
+                Object[] columnNames) {
+            super(data, columnNames);
+        }
+
+        @Override
+        public Class<?> getColumnClass(int columnIndex) {
+            switch (columnIndex) {
+                case 0:
+                    return Boolean.class;
+                case 1:
+                    return String.class;
+                default:
+                    return super.getColumnClass(columnIndex);
+            }
+        }
+
+        @Override
+        public boolean isCellEditable(int row, int column) {
+            return column == 0;
+        }
+
+        public List<AdditionalLibrary> getSelectedLibraries() {
+            List<AdditionalLibrary> selectedLibraries = new ArrayList<>();
+            for (int i = 0; i < getRowCount(); i++) {
+                if ((Boolean) getValueAt(i, 0)) {
+                    selectedLibraries.add((AdditionalLibrary) getValueAt(i, 1));
+                }
+            }
+
+            return selectedLibraries;
+        }
+    }
+}

+ 111 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameGuiPanel.java

@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop;
+
+import java.awt.Component;
+import javax.swing.event.ChangeListener;
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.HelpCtx;
+import org.openide.util.NbBundle;
+
+/**
+ * New Gradle Game Wizard Panel for selecting GUI / Physics / Networking
+ * libraries.
+ *
+ * @author peedeeboy
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class GradleDesktopGameGuiPanel implements WizardDescriptor.Panel,
+        WizardDescriptor.ValidatingPanel, WizardDescriptor.FinishablePanel {
+
+    /**
+     * JPanel containing the GUI / Physics / Networking options UI
+     */
+    private GradleDesktopGameGuiPanelVisual component;
+
+    public GradleDesktopGameGuiPanel() {
+    }
+
+    @Override
+    public Component getComponent() {
+        if (component == null) {
+            component = new GradleDesktopGameGuiPanelVisual(this);
+            component.setName(NbBundle.getMessage(
+                    GradleDesktopGameGuiPanelVisual.class,
+                    "LBL_ChooseGuiStep"));
+        }
+        return component;
+    }
+
+    @Override
+    public HelpCtx getHelp() {
+        return new HelpCtx("sdk.project_creation");
+    }
+
+    @Override
+    public void readSettings(Object settings) {
+    }
+
+    @Override
+    public void storeSettings(Object settings) {
+        WizardDescriptor d = (WizardDescriptor) settings;
+        component.store(d);
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    @Override
+    public void addChangeListener(ChangeListener listener) {
+        // Not required - no validation on this panel
+    }
+
+    @Override
+    public void removeChangeListener(ChangeListener listener) {
+        // Not required - no validation on this panel
+    }
+
+    @Override
+    public void validate() throws WizardValidationException {
+        // Not required - no validation on this panel
+    }
+
+    @Override
+    public boolean isFinishPanel() {
+        return false;
+    }
+
+}

+ 213 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameGuiPanelVisual.form

@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="jSeparator1" alignment="0" max="32767" attributes="0"/>
+                  <Group type="102" attributes="0">
+                      <Group type="103" groupAlignment="0" attributes="0">
+                          <Component id="guiDescriptionScrollPane" max="32767" attributes="0"/>
+                          <Component id="networkingDescriptionScrollPane" max="32767" attributes="0"/>
+                          <Group type="102" alignment="0" attributes="0">
+                              <Component id="guiLabel" min="-2" max="-2" attributes="0"/>
+                              <EmptySpace max="32767" attributes="0"/>
+                              <Component id="guiComboBox" min="-2" pref="120" max="-2" attributes="0"/>
+                          </Group>
+                          <Component id="jSeparator2" max="32767" attributes="0"/>
+                          <Component id="physicsEngineDescriptionScrollPane" alignment="0" max="32767" attributes="0"/>
+                          <Group type="102" alignment="1" attributes="0">
+                              <Component id="networkingLabel" min="-2" max="-2" attributes="0"/>
+                              <EmptySpace max="32767" attributes="0"/>
+                              <Component id="networkingComboBox" min="-2" pref="120" max="-2" attributes="0"/>
+                          </Group>
+                          <Group type="102" alignment="1" attributes="0">
+                              <Component id="physicsEngineLabel" min="-2" max="-2" attributes="0"/>
+                              <EmptySpace max="32767" attributes="0"/>
+                              <Component id="physicsEngineComboBox" min="-2" pref="120" max="-2" attributes="0"/>
+                          </Group>
+                      </Group>
+                      <EmptySpace max="-2" attributes="0"/>
+                  </Group>
+              </Group>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="guiLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="guiComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="guiDescriptionScrollPane" pref="75" max="32767" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="jSeparator1" min="-2" pref="10" max="-2" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="physicsEngineComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="physicsEngineLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="physicsEngineDescriptionScrollPane" pref="75" max="32767" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="jSeparator2" min="-2" pref="10" max="-2" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="networkingComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="networkingLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="networkingDescriptionScrollPane" pref="75" max="32767" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JLabel" name="guiLabel">
+      <Properties>
+        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+          <ComponentRef name="guiComboBox"/>
+        </Property>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/templates/gradledesktop/Bundle.properties" key="GradleDesktopGameGuiPanelVisual.guiLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="guiComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+          <Connection code="new DefaultComboBoxModel(GUILibrary.values())" type="code"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="guiComboBoxActionPerformed"/>
+      </Events>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+    <Container class="javax.swing.JScrollPane" name="guiDescriptionScrollPane">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextArea" name="guiDescriptionTextArea">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+            <Property name="columns" type="int" value="20"/>
+            <Property name="lineWrap" type="boolean" value="true"/>
+            <Property name="rows" type="int" value="3"/>
+            <Property name="wrapStyleWord" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JSeparator" name="jSeparator1">
+    </Component>
+    <Component class="javax.swing.JLabel" name="physicsEngineLabel">
+      <Properties>
+        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+          <ComponentRef name="physicsEngineComboBox"/>
+        </Property>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/templates/gradledesktop/Bundle.properties" key="GradleDesktopGameGuiPanelVisual.physicsEngineLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="physicsEngineComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+          <Connection code="new DefaultComboBoxModel(PhysicsLibrary.values())" type="code"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="physicsEngineComboBoxActionPerformed"/>
+      </Events>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+    <Container class="javax.swing.JScrollPane" name="physicsEngineDescriptionScrollPane">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextArea" name="physicsEngineDescriptionTextArea">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+            <Property name="columns" type="int" value="20"/>
+            <Property name="lineWrap" type="boolean" value="true"/>
+            <Property name="rows" type="int" value="3"/>
+            <Property name="wrapStyleWord" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JSeparator" name="jSeparator2">
+    </Component>
+    <Component class="javax.swing.JLabel" name="networkingLabel">
+      <Properties>
+        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+          <ComponentRef name="networkingComboBox"/>
+        </Property>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/templates/gradledesktop/Bundle.properties" key="GradleDesktopGameGuiPanelVisual.networkingLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="networkingComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+          <Connection code="new DefaultComboBoxModel(NetworkingLibrary.values())" type="code"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="networkingComboBoxActionPerformed"/>
+      </Events>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+    <Container class="javax.swing.JScrollPane" name="networkingDescriptionScrollPane">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextArea" name="networkingDescriptionTextArea">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+            <Property name="columns" type="int" value="20"/>
+            <Property name="lineWrap" type="boolean" value="true"/>
+            <Property name="rows" type="int" value="3"/>
+            <Property name="wrapStyleWord" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

+ 257 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameGuiPanelVisual.java

@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop;
+
+import com.jme3.gde.templates.gradledesktop.options.GUILibrary;
+import com.jme3.gde.templates.gradledesktop.options.NetworkingLibrary;
+import com.jme3.gde.templates.gradledesktop.options.PhysicsLibrary;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.GroupLayout;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JTextArea;
+import javax.swing.LayoutStyle;
+import org.openide.WizardDescriptor;
+import org.openide.awt.Mnemonics;
+import org.openide.util.NbBundle;
+
+/**
+ * UI Compoment for the New Gradle Game Wizard GUI panel.
+ *
+ * @author peedeeboy
+ */
+public class GradleDesktopGameGuiPanelVisual extends JPanel {
+
+    private final GradleDesktopGameGuiPanel panel;
+
+    /**
+     * Creates new form GradleDesktopGameGuiPanelVisual
+     */
+    public GradleDesktopGameGuiPanelVisual(GradleDesktopGameGuiPanel panel) {
+        initComponents();
+        updateGuiLibraryDescription();
+        updatePhysicsLibraryDescription();
+        updateNetworkingLibraryDescription();
+
+        this.panel = panel;
+    }
+
+    private void updateGuiLibraryDescription() {
+        GUILibrary selectedGuiLibrary = (GUILibrary) guiComboBox.getSelectedItem();
+        guiDescriptionTextArea.setText(selectedGuiLibrary.getDescription());
+    }
+
+    private void updatePhysicsLibraryDescription() {
+        PhysicsLibrary selectedPhysicsLibrary = (PhysicsLibrary) physicsEngineComboBox.getSelectedItem();
+        physicsEngineDescriptionTextArea.setText(selectedPhysicsLibrary.getDescription());
+    }
+
+    private void updateNetworkingLibraryDescription() {
+        NetworkingLibrary selectedNetworkingLibrary = (NetworkingLibrary) networkingComboBox.getSelectedItem();
+        networkingDescriptionTextArea.setText(selectedNetworkingLibrary.getDescription());
+    }
+
+    protected void store(WizardDescriptor d) {
+        GUILibrary selectedGuiLibrary = (GUILibrary) guiComboBox.getSelectedItem();
+        PhysicsLibrary selectedPhysicsLibrary = (PhysicsLibrary) physicsEngineComboBox.getSelectedItem();
+        NetworkingLibrary selectedNetworkingLibrary = (NetworkingLibrary) networkingComboBox.getSelectedItem();
+
+        d.putProperty("guiLibrary", selectedGuiLibrary);
+        d.putProperty("physicsLibrary", selectedPhysicsLibrary);
+        d.putProperty("networkingLibrary", selectedNetworkingLibrary);
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        guiLabel = new JLabel();
+        guiComboBox = new JComboBox<>();
+        guiDescriptionScrollPane = new JScrollPane();
+        guiDescriptionTextArea = new JTextArea();
+        jSeparator1 = new JSeparator();
+        physicsEngineLabel = new JLabel();
+        physicsEngineComboBox = new JComboBox<>();
+        physicsEngineDescriptionScrollPane = new JScrollPane();
+        physicsEngineDescriptionTextArea = new JTextArea();
+        jSeparator2 = new JSeparator();
+        networkingLabel = new JLabel();
+        networkingComboBox = new JComboBox<>();
+        networkingDescriptionScrollPane = new JScrollPane();
+        networkingDescriptionTextArea = new JTextArea();
+
+        guiLabel.setLabelFor(guiComboBox);
+        Mnemonics.setLocalizedText(guiLabel, NbBundle.getMessage(GradleDesktopGameGuiPanelVisual.class, "GradleDesktopGameGuiPanelVisual.guiLabel.text")); // NOI18N
+
+        guiComboBox.setModel(new DefaultComboBoxModel(GUILibrary.values()));
+        guiComboBox.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent evt) {
+                guiComboBoxActionPerformed(evt);
+            }
+        });
+
+        guiDescriptionTextArea.setEditable(false);
+        guiDescriptionTextArea.setColumns(20);
+        guiDescriptionTextArea.setLineWrap(true);
+        guiDescriptionTextArea.setRows(3);
+        guiDescriptionTextArea.setWrapStyleWord(true);
+        guiDescriptionScrollPane.setViewportView(guiDescriptionTextArea);
+
+        physicsEngineLabel.setLabelFor(physicsEngineComboBox);
+        Mnemonics.setLocalizedText(physicsEngineLabel, NbBundle.getMessage(GradleDesktopGameGuiPanelVisual.class, "GradleDesktopGameGuiPanelVisual.physicsEngineLabel.text")); // NOI18N
+
+        physicsEngineComboBox.setModel(new DefaultComboBoxModel(PhysicsLibrary.values()));
+        physicsEngineComboBox.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent evt) {
+                physicsEngineComboBoxActionPerformed(evt);
+            }
+        });
+
+        physicsEngineDescriptionTextArea.setEditable(false);
+        physicsEngineDescriptionTextArea.setColumns(20);
+        physicsEngineDescriptionTextArea.setLineWrap(true);
+        physicsEngineDescriptionTextArea.setRows(3);
+        physicsEngineDescriptionTextArea.setWrapStyleWord(true);
+        physicsEngineDescriptionScrollPane.setViewportView(physicsEngineDescriptionTextArea);
+
+        networkingLabel.setLabelFor(networkingComboBox);
+        Mnemonics.setLocalizedText(networkingLabel, NbBundle.getMessage(GradleDesktopGameGuiPanelVisual.class, "GradleDesktopGameGuiPanelVisual.networkingLabel.text")); // NOI18N
+
+        networkingComboBox.setModel(new DefaultComboBoxModel(NetworkingLibrary.values()));
+        networkingComboBox.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent evt) {
+                networkingComboBoxActionPerformed(evt);
+            }
+        });
+
+        networkingDescriptionTextArea.setEditable(false);
+        networkingDescriptionTextArea.setColumns(20);
+        networkingDescriptionTextArea.setLineWrap(true);
+        networkingDescriptionTextArea.setRows(3);
+        networkingDescriptionTextArea.setWrapStyleWord(true);
+        networkingDescriptionScrollPane.setViewportView(networkingDescriptionTextArea);
+
+        GroupLayout layout = new GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                    .addComponent(jSeparator1)
+                    .addGroup(layout.createSequentialGroup()
+                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                            .addComponent(guiDescriptionScrollPane)
+                            .addComponent(networkingDescriptionScrollPane)
+                            .addGroup(layout.createSequentialGroup()
+                                .addComponent(guiLabel)
+                                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(guiComboBox, GroupLayout.PREFERRED_SIZE, 120, GroupLayout.PREFERRED_SIZE))
+                            .addComponent(jSeparator2)
+                            .addComponent(physicsEngineDescriptionScrollPane)
+                            .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                                .addComponent(networkingLabel)
+                                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(networkingComboBox, GroupLayout.PREFERRED_SIZE, 120, GroupLayout.PREFERRED_SIZE))
+                            .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                                .addComponent(physicsEngineLabel)
+                                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(physicsEngineComboBox, GroupLayout.PREFERRED_SIZE, 120, GroupLayout.PREFERRED_SIZE)))
+                        .addContainerGap())))
+        );
+        layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                    .addComponent(guiLabel)
+                    .addComponent(guiComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(guiDescriptionScrollPane, GroupLayout.DEFAULT_SIZE, 75, Short.MAX_VALUE)
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jSeparator1, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                    .addComponent(physicsEngineComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(physicsEngineLabel))
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(physicsEngineDescriptionScrollPane, GroupLayout.DEFAULT_SIZE, 75, Short.MAX_VALUE)
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jSeparator2, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                    .addComponent(networkingComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                    .addComponent(networkingLabel))
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(networkingDescriptionScrollPane, GroupLayout.DEFAULT_SIZE, 75, Short.MAX_VALUE)
+                .addContainerGap())
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void guiComboBoxActionPerformed(ActionEvent evt) {//GEN-FIRST:event_guiComboBoxActionPerformed
+        updateGuiLibraryDescription();
+    }//GEN-LAST:event_guiComboBoxActionPerformed
+
+    private void physicsEngineComboBoxActionPerformed(ActionEvent evt) {//GEN-FIRST:event_physicsEngineComboBoxActionPerformed
+        updatePhysicsLibraryDescription();
+    }//GEN-LAST:event_physicsEngineComboBoxActionPerformed
+
+    private void networkingComboBoxActionPerformed(ActionEvent evt) {//GEN-FIRST:event_networkingComboBoxActionPerformed
+        updateNetworkingLibraryDescription();
+    }//GEN-LAST:event_networkingComboBoxActionPerformed
+
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private JComboBox<String> guiComboBox;
+    private JScrollPane guiDescriptionScrollPane;
+    private JTextArea guiDescriptionTextArea;
+    private JLabel guiLabel;
+    private JSeparator jSeparator1;
+    private JSeparator jSeparator2;
+    private JComboBox<String> networkingComboBox;
+    private JScrollPane networkingDescriptionScrollPane;
+    private JTextArea networkingDescriptionTextArea;
+    private JLabel networkingLabel;
+    private JComboBox<String> physicsEngineComboBox;
+    private JScrollPane physicsEngineDescriptionScrollPane;
+    private JTextArea physicsEngineDescriptionTextArea;
+    private JLabel physicsEngineLabel;
+    // End of variables declaration//GEN-END:variables
+}

+ 106 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameJMEVersionPanel.java

@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop;
+
+import java.awt.Component;
+import javax.swing.event.ChangeListener;
+import org.openide.WizardDescriptor;
+import org.openide.WizardValidationException;
+import org.openide.util.HelpCtx;
+import org.openide.util.NbBundle;
+
+/**
+ * New Gradle Game Wizard Panel for selecting jMonkeyEngine & LWJGL versions.
+ *
+ * @author peedeeboy
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class GradleDesktopGameJMEVersionPanel implements WizardDescriptor.Panel,
+        WizardDescriptor.ValidatingPanel, WizardDescriptor.FinishablePanel {
+
+    /**
+     * jPanel containing the JME / LWJGL options UI.
+     */
+    private GradleDesktopGameJMEVersionPanelVisual component;
+
+    @Override
+    public Component getComponent() {
+        if (component == null) {
+            component = new GradleDesktopGameJMEVersionPanelVisual(this);
+            component.setName(NbBundle.getMessage(
+                    GradleDesktopGameJMEVersionPanelVisual.class,
+                    "LBL_ChooseEngineStep"));
+        }
+        return component;
+    }
+
+    @Override
+    public HelpCtx getHelp() {
+        return new HelpCtx("sdk.project_creation");
+    }
+
+    @Override
+    public void readSettings(Object settings) {
+    }
+
+    @Override
+    public void storeSettings(Object settings) {
+        WizardDescriptor d = (WizardDescriptor) settings;
+        component.store(d);
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+    @Override
+    public void validate() throws WizardValidationException {
+        // Not required - no validation on this panel
+    }
+
+    @Override
+    public boolean isFinishPanel() {
+        return false;
+    }
+
+    @Override
+    public void addChangeListener(ChangeListener listener) {
+        // Not required - no validation on this panel
+    }
+
+    @Override
+    public void removeChangeListener(ChangeListener listener) {
+        // Not required - no validation on this panel
+    }
+}

+ 175 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameJMEVersionPanelVisual.form

@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <Properties>
+    <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[280, 360]"/>
+    </Property>
+  </Properties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="0"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" attributes="0">
+                      <Component id="lwjglDescriptionScrollPane" max="32767" attributes="0"/>
+                      <EmptySpace min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <Group type="102" attributes="0">
+                      <Group type="103" groupAlignment="0" attributes="0">
+                          <Component id="jmeVersionDescriptionScrollPane" alignment="1" max="32767" attributes="0"/>
+                          <Group type="102" alignment="1" attributes="0">
+                              <Component id="jmeVersionLabel" min="-2" max="-2" attributes="0"/>
+                              <EmptySpace max="32767" attributes="0"/>
+                              <Component id="jmeVersionComboBox" min="-2" pref="120" max="-2" attributes="0"/>
+                          </Group>
+                          <Component id="jSeparator1" alignment="1" max="32767" attributes="0"/>
+                      </Group>
+                      <EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
+                  </Group>
+                  <Group type="102" alignment="0" attributes="0">
+                      <Component id="lwjglVersionLabel" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace max="32767" attributes="0"/>
+                      <Component id="lwjglComboBox" min="-2" pref="120" max="-2" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                  </Group>
+              </Group>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="jmeVersionLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="jmeVersionComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jmeVersionDescriptionScrollPane" min="-2" pref="192" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="jSeparator1" min="-2" max="-2" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="lwjglVersionLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="lwjglComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
+              <Component id="lwjglDescriptionScrollPane" min="-2" pref="70" max="-2" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JLabel" name="jmeVersionLabel">
+      <Properties>
+        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+          <ComponentRef name="jmeVersionComboBox"/>
+        </Property>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/templates/gradledesktop/Bundle.properties" key="GradleDesktopGameJMEVersionPanelVisual.jmeVersionLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="jmeVersionComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+          <Connection code="new DefaultComboBoxModel(JMEVersion.values())" type="code"/>
+        </Property>
+        <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[100, 25]"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jmeVersionComboBoxActionPerformed"/>
+      </Events>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+    <Container class="javax.swing.JScrollPane" name="jmeVersionDescriptionScrollPane">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextPane" name="jmeVersionDescriptionTextPane">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+            <Property name="contentType" type="java.lang.String" value="text/html" noResource="true"/>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/jme3/gde/templates/gradledesktop/Bundle.properties" key="GradleDesktopGameJMEVersionPanelVisual.jmeVersionDescriptionTextPane.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="page" type="java.net.URL" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="GradleDesktopGameJMEVersionPanelVisual.class.getResource(&quot;/com/jme3/gde/templates/files/patchnotes/341-stable.html&quot;)" type="code"/>
+            </Property>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JSeparator" name="jSeparator1">
+    </Component>
+    <Component class="javax.swing.JLabel" name="lwjglVersionLabel">
+      <Properties>
+        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
+          <ComponentRef name="lwjglComboBox"/>
+        </Property>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="com/jme3/gde/templates/gradledesktop/Bundle.properties" key="GradleDesktopGameJMEVersionPanelVisual.lwjglVersionLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="lwjglComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+          <Connection code="new DefaultComboBoxModel(LWJGLVersion.values())" type="code"/>
+        </Property>
+        <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[100, 25]"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="lwjglComboBoxActionPerformed"/>
+      </Events>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+    <Container class="javax.swing.JScrollPane" name="lwjglDescriptionScrollPane">
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextArea" name="lwjglTextArea">
+          <Properties>
+            <Property name="editable" type="boolean" value="false"/>
+            <Property name="columns" type="int" value="20"/>
+            <Property name="lineWrap" type="boolean" value="true"/>
+            <Property name="rows" type="int" value="3"/>
+            <Property name="wrapStyleWord" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>

+ 266 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameJMEVersionPanelVisual.java

@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop;
+
+import com.jme3.gde.templates.gradledesktop.options.JMEVersion;
+import com.jme3.gde.templates.gradledesktop.options.LWJGLVersion;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.GroupLayout;
+import javax.swing.JComboBox;
+import javax.swing.JEditorPane;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JTextArea;
+import javax.swing.JTextPane;
+import javax.swing.LayoutStyle;
+import javax.swing.event.HyperlinkEvent;
+import org.openide.WizardDescriptor;
+import org.openide.awt.Mnemonics;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+
+/**
+ * UI Component for the New Gradle Game Wizard JME Version panel
+ *
+ * @author peedeeboy
+ */
+public class GradleDesktopGameJMEVersionPanelVisual extends JPanel {
+
+    private static final Logger LOGGER = Logger.getLogger(
+            GradleDesktopGameJMEVersionPanel.class.getName());
+
+    /**
+     * Creates new form GradleDesktopGameJMEVersion
+     */
+    public GradleDesktopGameJMEVersionPanelVisual(
+            GradleDesktopGameJMEVersionPanel panel) {
+        initComponents();
+        additionalComponentConfiguration();
+
+        loadPatchNotes();
+        updateLWJGLdescription();
+    }
+
+    private void additionalComponentConfiguration() {
+        // Set the JME Version text pane to use system fonts so it displays
+        // correctly on dark themes such as DarkMonkey
+        jmeVersionDescriptionTextPane.putClientProperty(
+                JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);
+
+        // Set a URL handler on the JME Version text pane so patch notes will
+        // open in browser
+        jmeVersionDescriptionTextPane.addHyperlinkListener(
+                (HyperlinkEvent e) -> {
+            if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+                if (Desktop.isDesktopSupported()) {
+                    try {
+                        Desktop.getDesktop().browse(e.getURL().toURI());
+                    } catch (URISyntaxException ex) {
+                        LOGGER.log(Level.WARNING, "Badly formatted URL: {0}",
+                                e.toString());
+                    } catch (IOException ex) {
+                        LOGGER.log(Level.WARNING, "Could not open browser: {0}",
+                                e.toString());
+                    }
+                }
+            }
+        });
+    }
+
+    private void loadPatchNotes() {
+        JMEVersion jmeVersionSelected = (JMEVersion) jmeVersionComboBox
+                .getSelectedItem();
+        try {
+            URL patchNotesURL = GradleDesktopGameJMEVersionPanelVisual.class
+                    .getResource(jmeVersionSelected.getPatchNotesPath());
+            jmeVersionDescriptionTextPane.setPage(patchNotesURL);
+        } catch (IOException ex) {
+            LOGGER.log(Level.SEVERE, "Could not open patch notes for JME "
+                    + "Version: {0}", jmeVersionSelected.toString());
+            jmeVersionDescriptionTextPane.setText("");
+            Exceptions.printStackTrace(ex);
+        }
+    }
+
+    private void updateLWJGLdescription() {
+        LWJGLVersion lwjglVersion = (LWJGLVersion) lwjglComboBox
+                .getSelectedItem();
+        lwjglTextArea.setText(lwjglVersion.getDescription());
+    }
+
+    protected void store(WizardDescriptor d) {
+        String jmeVersion = jmeVersionComboBox.getSelectedItem().toString();
+        LWJGLVersion lwjglVersion = (LWJGLVersion) lwjglComboBox
+                .getSelectedItem();
+        String lwjglArtifact = lwjglVersion.getArtifact();
+
+        d.putProperty("jmeVersion", jmeVersion);
+        d.putProperty("lwjglArtifact", lwjglArtifact);
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        jmeVersionLabel = new JLabel();
+        jmeVersionComboBox = new JComboBox<>();
+        jmeVersionDescriptionScrollPane = new JScrollPane();
+        jmeVersionDescriptionTextPane = new JTextPane();
+        jSeparator1 = new JSeparator();
+        lwjglVersionLabel = new JLabel();
+        lwjglComboBox = new JComboBox<>();
+        lwjglDescriptionScrollPane = new JScrollPane();
+        lwjglTextArea = new JTextArea();
+
+        setPreferredSize(new Dimension(280, 360));
+
+        jmeVersionLabel.setLabelFor(jmeVersionComboBox);
+        Mnemonics.setLocalizedText(jmeVersionLabel, NbBundle.getMessage(GradleDesktopGameJMEVersionPanelVisual.class, "GradleDesktopGameJMEVersionPanelVisual.jmeVersionLabel.text")); // NOI18N
+
+        jmeVersionComboBox.setModel(new DefaultComboBoxModel(JMEVersion.values()));
+        jmeVersionComboBox.setMaximumSize(new Dimension(100, 25));
+        jmeVersionComboBox.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent evt) {
+                jmeVersionComboBoxActionPerformed(evt);
+            }
+        });
+
+        jmeVersionDescriptionTextPane.setEditable(false);
+        jmeVersionDescriptionTextPane.setContentType("text/html"); // NOI18N
+        jmeVersionDescriptionTextPane.setText(NbBundle.getMessage(GradleDesktopGameJMEVersionPanelVisual.class, "GradleDesktopGameJMEVersionPanelVisual.jmeVersionDescriptionTextPane.text")); // NOI18N
+        try {
+            jmeVersionDescriptionTextPane.setPage(GradleDesktopGameJMEVersionPanelVisual.class.getResource("/com/jme3/gde/templates/files/patchnotes/341-stable.html"));
+        } catch (IOException e1) {
+            e1.printStackTrace();
+        }
+        jmeVersionDescriptionScrollPane.setViewportView(jmeVersionDescriptionTextPane);
+
+        lwjglVersionLabel.setLabelFor(lwjglComboBox);
+        Mnemonics.setLocalizedText(lwjglVersionLabel, NbBundle.getMessage(GradleDesktopGameJMEVersionPanelVisual.class, "GradleDesktopGameJMEVersionPanelVisual.lwjglVersionLabel.text")); // NOI18N
+
+        lwjglComboBox.setModel(new DefaultComboBoxModel(LWJGLVersion.values()));
+        lwjglComboBox.setMaximumSize(new Dimension(100, 25));
+        lwjglComboBox.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent evt) {
+                lwjglComboBoxActionPerformed(evt);
+            }
+        });
+
+        lwjglTextArea.setEditable(false);
+        lwjglTextArea.setColumns(20);
+        lwjglTextArea.setLineWrap(true);
+        lwjglTextArea.setRows(3);
+        lwjglTextArea.setWrapStyleWord(true);
+        lwjglDescriptionScrollPane.setViewportView(lwjglTextArea);
+
+        GroupLayout layout = new GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(lwjglDescriptionScrollPane)
+                        .addContainerGap())
+                    .addGroup(layout.createSequentialGroup()
+                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                            .addComponent(jmeVersionDescriptionScrollPane, GroupLayout.Alignment.TRAILING)
+                            .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                                .addComponent(jmeVersionLabel)
+                                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(jmeVersionComboBox, GroupLayout.PREFERRED_SIZE, 120, GroupLayout.PREFERRED_SIZE))
+                            .addComponent(jSeparator1, GroupLayout.Alignment.TRAILING))
+                        .addGap(6, 6, 6))
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(lwjglVersionLabel)
+                        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                        .addComponent(lwjglComboBox, GroupLayout.PREFERRED_SIZE, 120, GroupLayout.PREFERRED_SIZE)
+                        .addContainerGap())))
+        );
+        layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                    .addComponent(jmeVersionLabel)
+                    .addComponent(jmeVersionComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jmeVersionDescriptionScrollPane, GroupLayout.PREFERRED_SIZE, 192, GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(jSeparator1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
+                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                    .addComponent(lwjglVersionLabel)
+                    .addComponent(lwjglComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
+                .addGap(7, 7, 7)
+                .addComponent(lwjglDescriptionScrollPane, GroupLayout.PREFERRED_SIZE, 70, GroupLayout.PREFERRED_SIZE)
+                .addContainerGap())
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void lwjglComboBoxActionPerformed(ActionEvent evt) {//GEN-FIRST:event_lwjglComboBoxActionPerformed
+        updateLWJGLdescription();
+    }//GEN-LAST:event_lwjglComboBoxActionPerformed
+
+    private void jmeVersionComboBoxActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jmeVersionComboBoxActionPerformed
+        loadPatchNotes();
+    }//GEN-LAST:event_jmeVersionComboBoxActionPerformed
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    JSeparator jSeparator1;
+    JComboBox<String> jmeVersionComboBox;
+    JScrollPane jmeVersionDescriptionScrollPane;
+    JTextPane jmeVersionDescriptionTextPane;
+    JLabel jmeVersionLabel;
+    JComboBox<String> lwjglComboBox;
+    JScrollPane lwjglDescriptionScrollPane;
+    JTextArea lwjglTextArea;
+    JLabel lwjglVersionLabel;
+    // End of variables declaration//GEN-END:variables
+
+}

+ 65 - 3
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameWizardIterator.java

@@ -37,14 +37,26 @@ import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.Charset;
 import java.text.MessageFormat;
+import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
 import javax.swing.JComponent;
 import javax.swing.event.ChangeListener;
 import org.netbeans.api.project.ProjectManager;
@@ -68,6 +80,9 @@ public class GradleDesktopGameWizardIterator implements WizardDescriptor./*Progr
     private WizardDescriptor.Panel[] panels;
     private WizardDescriptor wiz;
 
+    private static final String TEMPLATE_SETTINGS = "com/jme3/gde/templates/files/freemarker/settings.gradle.ftl";
+    private static final String TEMPLATE_BUILDFILE = "com/jme3/gde/templates/files/freemarker/build.gradle.ftl";
+    
     public GradleDesktopGameWizardIterator() {
     }
 
@@ -77,12 +92,19 @@ public class GradleDesktopGameWizardIterator implements WizardDescriptor./*Progr
 
     private WizardDescriptor.Panel[] createPanels() {
         return new WizardDescriptor.Panel[]{
-                    new GradleDesktopGameWizardPanel(),};
+                    new GradleDesktopGameWizardPanel(),
+                    new GradleDesktopGameJMEVersionPanel(),
+                    new GradleDesktopGameGuiPanel(),
+                    new GradleDesktopGameAdditionalLibrariesPanel()
+        };
     }
 
     private String[] createSteps() {
         return new String[]{
-                    NbBundle.getMessage(GradleDesktopGameWizardIterator.class, "LBL_CreateProjectStep")
+                    NbBundle.getMessage(GradleDesktopGameWizardIterator.class, "LBL_CreateProjectStep"),
+                    NbBundle.getMessage(GradleDesktopGameWizardIterator.class, "LBL_ChooseEngineStep"),
+                    NbBundle.getMessage(GradleDesktopGameWizardIterator.class, "LBL_ChooseGuiStep"),
+                    NbBundle.getMessage(GradleDesktopGameWizardIterator.class, "LBL_ChooseAdditionalLibrariesStep")
                 };
     }
 
@@ -95,7 +117,24 @@ public class GradleDesktopGameWizardIterator implements WizardDescriptor./*Progr
         FileObject template = Templates.getTemplate(wiz);
         FileObject dir = FileUtil.toFileObject(dirF);
         unZipFile(template.getInputStream(), dir);
-
+        
+        // Create settings.gradle from template
+        File gradleSettingsFile = new File(dirF, "settings.gradle");
+        createFileFromTemplate(gradleSettingsFile, TEMPLATE_SETTINGS,
+                Collections.singletonMap("name", wiz.getProperty("name")));
+        
+        // Create build.gradle from template
+        File gradleBuildFile = new File(dirF, "build.gradle");
+        Map<String, Object> buildFileBindings = new HashMap<>();
+        buildFileBindings.put("jmeVersion", wiz.getProperty("jmeVersion"));
+        buildFileBindings.put("lwjglArtifact", wiz.getProperty("lwjglArtifact"));
+        buildFileBindings.put("guiLibrary", wiz.getProperty("guiLibrary"));
+        buildFileBindings.put("physicsLibrary", wiz.getProperty("physicsLibrary"));
+        buildFileBindings.put("networkingLibrary", wiz.getProperty("networkingLibrary"));
+        buildFileBindings.put("additionalLibraries", wiz.getProperty("additionalLibraries"));
+        
+        createFileFromTemplate(gradleBuildFile, TEMPLATE_BUILDFILE, buildFileBindings);
+        
         // Always open top dir as a project:
         resultSet.add(dir);
         // Look for nested projects to open as well:
@@ -195,6 +234,29 @@ public class GradleDesktopGameWizardIterator implements WizardDescriptor./*Progr
     public final void removeChangeListener(ChangeListener l) {
     }
 
+    private void createFileFromTemplate(File target, String templateResourcePath, Map<String, Object> tokens) throws IOException {
+        
+        // Create FreeMarker script engine
+        ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
+        ScriptEngine engine = scriptEngineManager.getEngineByName("freemarker");
+        Map<String, Object> bindings = engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
+        bindings.putAll(tokens);
+        
+        // Process template           
+        try {
+            FileObject targetFO = FileUtil.toFileObject(target);
+            Writer os = new OutputStreamWriter(targetFO.getOutputStream(), Charset.forName("UTF-8"));
+            engine.getContext().setWriter(os);
+            Reader is = new InputStreamReader(GradleDesktopGameWizardIterator.class.getResourceAsStream("/" + templateResourcePath));
+            engine.eval(is);
+            
+            os.close();
+            is.close();
+        } catch (IOException | ScriptException ex) {
+                throw new IOException(ex.getMessage(), ex);
+        }
+    }
+    
     private static void unZipFile(InputStream source, FileObject projectRoot) throws IOException {
         try (source) {
             ZipInputStream str = new ZipInputStream(source);

+ 1 - 1
jme3-templates/src/com/jme3/gde/templates/gradledesktop/GradleDesktopGameWizardPanel.java

@@ -106,7 +106,7 @@ public class GradleDesktopGameWizardPanel implements WizardDescriptor.Panel,
     }
 
     public boolean isFinishPanel() {
-        return true;
+        return false;
     }
 
     public void validate() throws WizardValidationException {

+ 194 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/AdditionalLibrary.java

@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop.options;
+
+import org.openide.util.NbBundle;
+
+/**
+ * Enum representing a recommended Additional Library (either Core or 3rd party)
+ * that can be added to a jMonkeyEngine project.
+ *
+ * <p>To add a new additional library:</p>
+ * <ul>
+ * <li>
+ * Add a new entry to this enum.
+ * </li>
+ * <li>
+ * The label is what will be displayed in the jComboBox.
+ * </li>
+ * <li>
+ * The description should be added to the <code>bundle.properties</code>, and
+ * referenced in the 2nd parameter.
+ * </li>
+ * <li>
+ *
+ * If the library is 3rd party, the artifact should contain the version number.
+ * If the library is Core, then the artifact should not contain the version
+ * number, as the correct jMonkeyEngine version number will be automatically
+ * appended during project creation.
+ * </li>
+ * <li>
+ * isCoreJmeLibrary should be set to <code>true</code> if this is a core
+ * jMonkeyEngine library, or <code>false</code> if it is 3rd party.
+ * </li>
+ * </ul>
+ *
+ * @author peedeeboy
+ */
+public enum AdditionalLibrary {
+
+    JME3_EFFECTS("jMonkeyEngine Effects (jme3-effects)",
+            NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.jme3-effects.description"),
+            "org.jmonkeyengine:jme3-effects", true),
+    JME3_TERRAIN("jMonkeyEngine TerraMonkey (jme3-terrain)",
+            NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.jme3-terrain.description"),
+            "org.jmonkeyengine:jme3-terrain", true),
+    JME3_TESTDATA("jMonkeyEngine Test Data (jme3-testdata)",
+            NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.jme3-testdata.description"),
+            "org.jmonkeyengine:jme3-testdata", true),
+    JME3_VR("jMonkeyEngine Virtual Reality (jme3-vr)",
+            NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.jme3-vr.description"),
+            "org.jmonkeyengine:jme3-vr", true),
+    HEART("Heart Library", NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.heart.description"),
+            "com.github.stephengold:Heart:8.1.0", false),
+    PARTICLE_MONKEY("Particle Monkey",
+            NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.particlemonkey.description"),
+            "com.github.Jeddic:particlemonkey:1.0.2", false),
+    SHADERBLOW_EX("ShaderBlowEx", NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.shaderblowex.description"),
+            "com.github.polincdev:ShaderBlowEx:master-SNAPSHOT", false),
+    SIO2("SiO2", NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.sio2.description"),
+            "com.simsilica:sio2:1.7.0", false),
+    ZAY_ES("Zay-ES Entity Component System",
+            NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.zayes.description"),
+            "com.simsilica:zay-es:1.4.0", false),
+    ZAY_ES_NET("Zay-ES-Net Networking Extension",
+            NbBundle.getMessage(AdditionalLibrary.class,
+            "additionalLibrary.zayesnet.description"),
+            "com.simsilica:zay-es-net:1.5.0", false),;
+
+    /**
+     * The name of the library. This will be displayed in the jComboBox in the
+     * New Project wizard.
+     */
+    private final String label;
+    /**
+     * Long description of the library. This should be stored in the
+     * <code>bundle.properties</code> file.
+     */
+    private final String description;
+    /**
+     * Gradle artifact string. If this is <strong>not</strong> a core JME
+     * library, then the artifact string should include the version number. If
+     * the library <strong>is</strong> a core JME library, then the version
+     * should be omitted, as they jMonkeyEngine version will be appended
+     * automatically by the template.
+     */
+    private final String artifact;
+    /**
+     * Is this library a core jMonkeyEngine library? True if the library is a
+     * part of jMonkeyengine, false if it is 3rd party.
+     */
+    private final boolean isCoreJmeLibrary;
+
+    /**
+     * Private constructor to create an instance of this enum.
+     *
+     * @param label The name of the library.
+     * @param description Long description of the library.
+     * @param artifact Gradle artifact string.
+     * @param isCoreJmeLibrary Is this library a core jMonkeyEngine library?
+     */
+    AdditionalLibrary(String label, String description, String artifact,
+            boolean isCoreJmeLibrary) {
+        this.label = label;
+        this.description = description;
+        this.artifact = artifact;
+        this.isCoreJmeLibrary = isCoreJmeLibrary;
+    }
+
+    /**
+     * Get the label for this Additional Library.
+     *
+     * @return the label for this Additional Library.
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Get the long description for this Additional Library.
+     *
+     * @return the long description for this Additional Library.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Get the Gradle artifact string.
+     *
+     * @return the Gradle artifact string.
+     */
+    public String getArtifact() {
+        return artifact;
+    }
+
+    /**
+     * Is this a Core jMonkeyEngine library?
+     *
+     * @return true if this is a core jMonkeyEngine library.
+     */
+    public boolean getIsCoreJmeLibrary() {
+        return isCoreJmeLibrary;
+    }
+
+    /**
+     * Override the <code>toString()</code> method to return the label, so that
+     * this enum will display nicely in a jComboBox.
+     *
+     * @return <code>label</code> as a String
+     */
+    @Override
+    public String toString() {
+        return label;
+    }
+}

+ 37 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/Bundle.properties

@@ -0,0 +1,37 @@
+additionalLibrary.jme3-effects.description=Core jMonkeyEngine post-processing effects library featuring filters and support for water effects.
+additionalLibrary.jme3-terrain.description=TerraMonkey - the core jMonkeyEngine terrain library.
+additionalLibrary.jme3-testdata.description=jMonkeyEngine test data and example projects.
+additionalLibrary.jme3-vr.description=Core jMonkeyEngine library providing Virtual Reality support.
+additionalLibrary.heart.description=The Heart Library provides an assortment of useful classes and assets to augment jMonkeyEngine.
+additionalLibrary.particlemonkey.description=Particle Monkey is a more modern particle system with better artistic controls.
+additionalLibrary.shaderblowex.description=Extended filters library for JMonkey Game Engine.
+additionalLibrary.sio2.description=A base library of useful utility code for JME-based games. \
+Includes game system management infrastructure, useful base app states, an event bus, and useful Zay-ES utilities. \
+This is a useful base library for any JME game.
+additionalLibrary.zayes.description=Zay-ES (pronounced like Doctor Zaius from Planet of the Apes) is a high-performance \
+Java-based Entity-Component-System that manages to side-step most/all of the typical disadvantages of an ES architecture without sacrificing ES "purity".
+additionalLibrary.zayesnet.description=Zay-ES-Net is the add-on extension to the Zay-ES Entity Component System that provides client/server networking.
+
+guilibrary.none.description=No Graphical User Interface.
+guilibrary.nifty.description=Nifty GUI is a Java library for building interactive graphical user interfaces (GUIs) for games or similar applications. \
+Nifty has historically been integrated into the engine but is not maintained by the Engine Team.
+guilibrary.lemur.description=Lemur is a 3rd party GUI toolkit for making user interfaces in jMonkeyEngine applications. \
+It supports standard 2D UIs as well as fully 3D UIs. \
+The modular design allows an application to use all or some of it as needed or even to build a completely new custom GUI library on top.
+
+lwjgl.lwjgl3.description=LightWeight Java Game Library 3.  Recommended for new projects. \
+A suitable version of LWJGL3 for the chosen version of jMonkeyEngine will be used.
+lwjgl.lwjgl2.description=LightWeight Java Game Library 2. \
+A suitable version of LWJGL2 for the chosen version of jMonkeyEngine will be used.
+
+networkinglibrary.none.description=No networking library.
+networkinglibrary.spidermonkey.description=Core jMonkeyEngine networking library for creating a Server, \
+Clients, and Messages.
+networkinglibrary.monkeynetty.description=A 3rd party implementation of a server-client communication \
+system for jMonkeyEngine using Netty.IO that utilizes both TCP and UDP communication.
+networkinglibrary.simethereal.description=A 3rd party high performance library for real-time networked object synching.
+
+physicslibrary.none.description=No physics library.
+physicslibrary.jbullet.description=Java implementation of the Bullet physics library included in jMonkeyEngine core. No longer actively developed.
+physicslibrary.minie.description=A 3rd party library forked from the jme3-bullet library, Minie adds many features while preserving most of the jme3-bullet API. \
+Most JME physics examples can use Minie without modification, and Minie adds its own tests, demos, documentation, and tutorials to help you get started.

+ 164 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/GUILibrary.java

@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop.options;
+
+import org.openide.util.NbBundle;
+
+/**
+ * Enum representing a Graphical User Interface (GUI) library supported by
+ * jMonkeyEngine.
+ *
+ * <p>To add a new GUI library:</p>
+ * <ul>
+ * <li>
+ * Add a new entry to this enum.
+ * </li>
+ * <li>
+ * The label is what will be displayed in the jComboBox.
+ * </li>
+ * <li>
+ * The description should be added to the <code>bundle.properties</code>, and
+ * referenced in the 2nd parameter.
+ * </li>
+ * <li>
+ * If the library is 3rd party, the artifact should contain the version number.
+ * If the library is Core, then the artifact should not contain the version
+ * number, as the correct jMonkeyEngine version number will be automatically
+ * appended during project creation.
+ * </li>
+ * <li>
+ * isCoreJmeLibrary should be set to <code>true</code> if this is a core
+ * jMonkeyEngine library, or <code>false</code> if it is 3rd party.
+ * </li>
+ * </ul>
+ *
+ * @author peedeeboy
+ */
+public enum GUILibrary {
+
+    NONE("", NbBundle.getMessage(GUILibrary.class,
+            "guilibrary.none.description"), "", false),
+    NIFTY("Nifty", NbBundle.getMessage(GUILibrary.class, 
+            "guilibrary.nifty.description"),
+            "org.jmonkeyengine:jme3-niftygui", true),
+    LEMUR("Lemur", NbBundle.getMessage(GUILibrary.class,
+            "guilibrary.lemur.description"),
+            "com.simsilica:lemur:1.16.0", false);
+
+    /**
+     * The name of the library. This will be displayed in the jComboBox in the
+     * New Project wizard.
+     */
+    private final String label;
+    /**
+     * Long description of the library. This should be stored in the
+     * <code>bundle.properties</code> file.
+     */
+    private final String description;
+    /**
+     * Gradle artifact string. If this is <strong>not</strong> a core JME
+     * library, then the artifact string should include the version number. If
+     * the library <strong>is</strong> a core JME library, then the version
+     * should be omitted, as they jMonkeyEngine version will be appended
+     * automatically by the template.
+     */
+    private final String artifact;
+    /**
+     * Is this library a core jMonkeyEngine library? True if the library is a
+     * part of jMonkeyengine, false if it is 3rd party.
+     */
+    private final boolean isCoreJmeLibrary;
+
+    /**
+     * Private constructor to create an instance of this enum.
+     *
+     * @param label The name of the library.
+     * @param description Long description of the library.
+     * @param artifact Gradle artifact string.
+     * @param isCoreJmeLibrary Is this library a core jMonkeyEngine library?
+     */
+    GUILibrary(String label, String description, String artifact,
+            boolean isCoreJmeLibrary) {
+        this.label = label;
+        this.description = description;
+        this.artifact = artifact;
+        this.isCoreJmeLibrary = isCoreJmeLibrary;
+    }
+
+    /**
+     * Get the label for this GUI Library.
+     *
+     * @return the label for this GUI Library.
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Get the long description for this GUI Library.
+     *
+     * @return the long description for this GUI Library.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Get the Gradle artifact string.
+     *
+     * @return the Gradle artifact string.
+     */
+    public String getArtifact() {
+        return artifact;
+    }
+
+    /**
+     * Is this a Core jMonkeyEngine library?
+     *
+     * @return true if this is a core jMonkeyEngine library.
+     */
+    public boolean getIsCoreJmeLibrary() {
+        return isCoreJmeLibrary;
+    }
+
+    /**
+     * Override the <code>toString()</code> method to return the label, so that
+     * this enum will display nicely in a jComboBox.
+     *
+     * @return <code>label</code> as a String
+     */
+    @Override
+    public String toString() {
+        return label;
+    }
+}

+ 126 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/JMEVersion.java

@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop.options;
+
+/**
+ * Enum representing a jMonkeyEngine version to be used when creating a new
+ * Gradle based project.
+ *
+ * <p>To add a new version of the engine to the options for a Gradle project:
+ * </p>
+ * <ul>
+ * <li>
+ * Create a new .html file in the
+ * <code>com.jme3.gde.templates.files.patchnotes</code> package containing the
+ * Patch Notes copied from GitHub. The <code>class=""</code> attributes should
+ * be removed using a regex like: class="[a-zA-Z0-9:;\.\s\(\)\-,]*"
+ * </li>
+ * <li>
+ * Add a new entry to this enum. The label should match the Maven/Gradle
+ * version. The patchNotesPath should point to the .html file containing the
+ * Patch Notes
+ * </li>
+ * </ul>
+ *
+ * @author peedeeboy
+ */
+public enum JMEVersion {
+
+    JME_3_5_2("3.5.2-stable",
+            "/com/jme3/gde/templates/files/patchnotes/352-stable.html"),
+    JME_3_5_1("3.5.1-stable",
+            "/com/jme3/gde/templates/files/patchnotes/351-stable.html"),
+    JME_3_5_0("3.5.0-stable",
+            "/com/jme3/gde/templates/files/patchnotes/350-stable.html"),
+    JME_3_4_1("3.4.1-stable",
+            "/com/jme3/gde/templates/files/patchnotes/341-stable.html"),
+    JME_3_4_0("3.4.0-stable",
+            "/com/jme3/gde/templates/files/patchnotes/340-stable.html"),
+    JME_3_3_2("3.3.2-stable",
+            "/com/jme3/gde/templates/files/patchnotes/332-stable.html"),
+    JME_3_3_0("3.3.0-stable",
+            "/com/jme3/gde/templates/files/patchnotes/330-stable.html");
+
+    /**
+     * Name of the jMonkeyEngine version. This should match the Maven/Gradle
+     * version.
+     */
+    private final String label;
+    /**
+     * Path to a .html file containing the Patch Notes for this version of the
+     * Engine.
+     */
+    private final String patchNotesPath;
+
+    /**
+     * Private constructor to create an instance of this enum.
+     *
+     * @param label Name of the jMonkeyEngine version.
+     * @param patchNotesPath Path to a .html file containing the Patch Notes for
+     * this version of the Engine.
+     */
+    JMEVersion(String label, String patchNotesPath) {
+        this.label = label;
+        this.patchNotesPath = patchNotesPath;
+    }
+
+    /**
+     * Get the label for this jMonkeyEngineVersion.
+     *
+     * @return the label for this jMonkeyEngineVersion
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Get the path to the .html file containing the Patch Notes for this
+     * jMonkeyEngine version.
+     *
+     * @return the path to the .html file containing the Patch Notes
+     */
+    public String getPatchNotesPath() {
+        return patchNotesPath;
+    }
+
+    /**
+     * Override the <code>toString()</code> method to return the label, so that
+     * this enum will display nicely in a jComboBox.
+     *
+     * @return <code>label</code> as a String
+     */
+    @Override
+    public String toString() {
+        return label;
+    }
+}

+ 135 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/LWJGLVersion.java

@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop.options;
+
+import org.openide.util.NbBundle;
+
+/**
+ * Enum representing a version of LightWeight Java Game Library (LWJGL)
+ * supported by jMonkeyEngine.
+ *
+ * <p>To add a new LWJGL version:</p>
+ * <ul>
+ * <li>
+ * Add a new entry to this enum.
+ * </li>
+ * <li>
+ * The label is what will be displayed in the jComboBox.
+ * </li>
+ * <li>
+ * The description should be added to the <code>bundle.properties</code>, and
+ * referenced in the 2nd parameter.
+ * </li>
+ * <li>
+ * The artifact should contain the Gradle artifact string for the JME wrapped
+ * LWJGL version. The correct JME version will be appended to the artifact by
+ * the template.
+ * </li>
+ * </ul>
+ *
+ * @author peedeeboy
+ */
+public enum LWJGLVersion {
+
+    LWJGL_3("LWJGL 3.x", NbBundle.getMessage(LWJGLVersion.class,
+            "lwjgl.lwjgl3.description"), "org.jmonkeyengine:jme3-lwjgl3"),
+    LWJGL_2("LWJGL 2.x", NbBundle.getMessage(LWJGLVersion.class,
+            "lwjgl.lwjgl2.description"), "org.jmonkeyengine:jme3-lwjgl");
+
+    /**
+     * The name of the LWJGL library. This will be displayed in the jComboBox in
+     * the New Project wizard.
+     */
+    private final String label;
+    /**
+     * Long description of the LWJGL version. This should be stored in the
+     * <code>bundle.properties</code> file.
+     */
+    private final String description;
+    /**
+     * Gradle artifact string. This should exclude the jMonkeyEngine version, as
+     * this will be added by the template.
+     */
+    private final String artifact;
+
+    /**
+     * Private constructor to create an instance of this enum.
+     *
+     * @param label The name of the LWJGL library.
+     * @param description Long description of the LWJGL version.
+     * @param artifact Gradle artifact string.
+     */
+    LWJGLVersion(String label, String description, String artifact) {
+        this.label = label;
+        this.description = description;
+        this.artifact = artifact;
+    }
+
+    /**
+     * Get the label for this LWJGL version.
+     *
+     * @return the label for this LWJGL version.
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Get the long description for this LWJGL version.
+     *
+     * @return the long description for this LWJGL version.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Get the Gradle artifact string.
+     *
+     * @return the Gradle artifact string.
+     */
+    public String getArtifact() {
+        return artifact;
+    }
+
+    /**
+     * Override the <code>toString()</code> method to return the label, so that
+     * this enum will display nicely in a jComboBox.
+     *
+     * @return <code>label</code> as a String
+     */
+    @Override
+    public String toString() {
+        return this.label;
+    }
+}

+ 166 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/NetworkingLibrary.java

@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop.options;
+
+import org.openide.util.NbBundle;
+
+/**
+ * Enum representing a Networking library supported by jMonkeyEngine.
+ *
+ * <p>To add a new Networking library:</p>
+ * <ul>
+ * <li>
+ * Add a new entry to this enum.
+ * </li>
+ * <li>
+ * The label is what will be displayed in the jComboBox.
+ * </li>
+ * <li>
+ * The description should be added to the <code>bundle.properties</code>, and
+ * referenced in the 2nd parameter.
+ * </li>
+ * <li>
+ * If the library is 3rd party, the artifact should contain the version number.
+ * If the library is Core, then the artifact should not contain the version
+ * number, as the correct jMonkeyEngine version number will be automatically
+ * appended during project creation.
+ * </li>
+ * <li>
+ * isCoreJmeLibrary should be set to <code>true</code> if this is a core
+ * jMonkeyEngine library, or <code>false</code> if it is 3rd party.
+ * </li>
+ * </ul>
+ *
+ * @author peedeeboy
+ */
+public enum NetworkingLibrary {
+
+    NONE("", NbBundle.getMessage(NetworkingLibrary.class,
+            "networkinglibrary.none.description"), "", false),
+    SPIDERMONKEY("SpiderMonkey", NbBundle.getMessage(NetworkingLibrary.class,
+            "networkinglibrary.spidermonkey.description"),
+            "org.jmonkeyengine:jme3-networking", true),
+    MONKEYNETTY("MonkeyNetty", NbBundle.getMessage(NetworkingLibrary.class,
+            "networkinglibrary.monkeynetty.description"),
+            "io.tlf.monkeynetty:monkey-netty:0.1.1", false),
+    SIMETHEREAL("SimEthereal", NbBundle.getMessage(NetworkingLibrary.class,
+            "networkinglibrary.simethereal.description"),
+            "com.simsilica:sim-ethereal:1.7.0", false);
+
+    /**
+     * The name of the library. This will be displayed in the jComboBox in the
+     * New Project wizard.
+     */
+    private final String label;
+    /**
+     * Long description of the library. This should be stored in the
+     * <code>bundle.properties</code> file.
+     */
+    private final String description;
+    /**
+     * Gradle artifact string. If this is <strong>not</strong> a core JME
+     * library, then the artifact string should include the version number. If
+     * the library <strong>is</strong> a core JME library, then the version
+     * should be omitted, as they jMonkeyEngine version will be appended
+     * automatically by the template.
+     */
+    private final String artifact;
+    /**
+     * Is this library a core jMonkeyEngine library? True if the library is a
+     * part of jMonkeyengine, false if it is 3rd party.
+     */
+    private final boolean isCoreJmeLibrary;
+
+    /**
+     * Private constructor to create an instance of this enum.
+     *
+     * @param label The name of the library.
+     * @param description Long description of the library.
+     * @param artifact Gradle artifact string.
+     * @param isCoreJmeLibrary Is this library a core jMonkeyEngine library?
+     */
+    NetworkingLibrary(String label, String description, String artifact,
+            boolean isCoreJmeLibrary) {
+        this.label = label;
+        this.description = description;
+        this.artifact = artifact;
+        this.isCoreJmeLibrary = isCoreJmeLibrary;
+    }
+
+    /**
+     * Get the label for this Networking Library.
+     *
+     * @return the label for this Networking Library.
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Get the long description for this Networking Library.
+     *
+     * @return the long description for this Networking Library.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Get the Gradle artifact string.
+     *
+     * @return the Gradle artifact string.
+     */
+    public String getArtifact() {
+        return artifact;
+    }
+
+    /**
+     * Is this a Core jMonkeyEngine library?
+     *
+     * @return true if this is a core jMonkeyEngine library
+     */
+    public boolean getIsCoreJmeLibrary() {
+        return isCoreJmeLibrary;
+    }
+
+    /**
+     * Override the <code>toString()</code> method to return the label, so that
+     * this enum will display nicely in a jComboBox.
+     *
+     * @return <code>label</code> as a String
+     */
+    @Override
+    public String toString() {
+        return this.label;
+    }
+}

+ 163 - 0
jme3-templates/src/com/jme3/gde/templates/gradledesktop/options/PhysicsLibrary.java

@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 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.gde.templates.gradledesktop.options;
+
+import org.openide.util.NbBundle;
+
+/**
+ * Enum representing a Physics library supported by jMonkeyEngine.
+ *
+ * <p>To add a new GUI library:</p>
+ * <ul>
+ * <li>
+ * Add a new entry to this enum.
+ * </li>
+ * <li>
+ * The label is what will be displayed in the jComboBox.
+ * </li>
+ * <li>
+ * The description should be added to the <code>bundle.properties</code>, and
+ * referenced in the 2nd parameter.
+ * </li>
+ * <li>
+ * If the library is 3rd party, the artifact should contain the version number.
+ * If the library is Core, then the artifact should not contain the version
+ * number, as the correct jMonkeyEngine version number will be automatically
+ * appended during project creation.
+ * </li>
+ * <li>
+ * isCoreJmeLibrary should be set to <code>true</code> if this is a core
+ * jMonkeyEngine library, or <code>false</code> if it is 3rd party.
+ * </li>
+ * </ul>
+ *
+ * @author peedeeboy
+ */
+public enum PhysicsLibrary {
+
+    NONE("", NbBundle.getMessage(GUILibrary.class,
+            "physicslibrary.none.description"), "", false),
+    JBULLET("jBullet", NbBundle.getMessage(GUILibrary.class,
+            "physicslibrary.jbullet.description"),
+            "org.jmonkeyengine:jme3-jbullet", true),
+    MINIE("Minie", NbBundle.getMessage(PhysicsLibrary.class,
+            "physicslibrary.minie.description"),
+            "com.github.stephengold:Minie:5.0.0", false);
+
+    /**
+     * The name of the library. This will be displayed in the jComboBox in the
+     * New Project wizard.
+     */
+    private final String label;
+    /**
+     * Long description of the library. This should be stored in the
+     * <code>bundle.properties</code> file.
+     */
+    private final String description;
+    /**
+     * Gradle artifact string. If this is <strong>not</strong> a core JME
+     * library, then the artifact string should include the version number. If
+     * the library <strong>is</strong> a core JME library, then the version
+     * should be omitted, as they jMonkeyEngine version will be appended
+     * automatically by the template.
+     */
+    private final String artifact;
+    /**
+     * Is this library a core jMonkeyEngine library? True if the library is a
+     * part of jMonkeyengine, false if it is 3rd party.
+     */
+    private final boolean isCoreJmeLibrary;
+
+    /**
+     * Private constructor to create an instance of this enum.
+     *
+     * @param label The name of the library.
+     * @param description Long description of the library.
+     * @param artifact Gradle artifact string.
+     * @param isCoreJmeLibrary Is this library a core jMonkeyEngine library?
+     */
+    PhysicsLibrary(String label, String description, String artifact,
+            boolean isCoreJmeLibrary) {
+        this.label = label;
+        this.description = description;
+        this.artifact = artifact;
+        this.isCoreJmeLibrary = isCoreJmeLibrary;
+    }
+
+    /**
+     * Get the label for this Physics Library.
+     *
+     * @return the label for this Physics Library.
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Get the long description for this Physics Library.
+     *
+     * @return the long description for this Physics Library.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Get the Gradle artifact string.
+     *
+     * @return the Gradle artifact string.
+     */
+    public String getArtifact() {
+        return artifact;
+    }
+
+    /**
+     * Is this a Core jMonkeyEngine library?
+     *
+     * @return true if this is a core jMonkeyEngine library
+     */
+    public boolean getIsCoreJmeLibrary() {
+        return isCoreJmeLibrary;
+    }
+
+    /**
+     * Override the <code>toString()</code> method to return the label, so that
+     * this enum will display nicely in a jComboBox.
+     *
+     * @return <code>label</code> as a String
+     */
+    @Override
+    public String toString() {
+        return this.label;
+    }
+}

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