Merge branch 'master' into PBRisComing

* master: (94 commits)
  First attempt to fix a bug reported by david_bernard_31, the size of the strings in the shader code was wrongly computed for the JOGL backend
  Allows to choose between the forward compatible profile and the backward compatible profile in the JOGL backend
  Displays the JOGL version instead of the NEWT version
  Updates JOGL (2.3.2)
  Bugfix: fix to importing blend files with linked content.
  native loader: set lwjgl library path for lwjgl3
  Updated lwjgl3 module to use LWJGL 3.0.0b #35 which is the current stable build.
  Change duplicated docstring in FlyByCamera.unregisterInput
  light : fixed pointLight v. bounding sphere unit test
  light : replaced duplicated code by methods from Intersection
  Removed native library jemalloc.dll for LWJGL3 as this will not be needed until 3.0.0b and after.
  light : added unit tests for the new support of bounding spheres intersections (for lightFilter)
  Fixed Issue #46 : The MaterialViewer will now simply ignore not available textures instead of crashing
  Lights (see #362) : added light v. sphere intersection, and implementations of intersectsSphere(), second attempt
  GImpactCollisionShape : fix for #188, added a call to updateBound() in native jni binding, just after creating the shape, (native createShape() method)
  Bugfix: fixed a bug that caused importer to crash when the author of the blend file assigned non existing UV coordinates group name to a mesh.
  Bugfix: fixed a bug that caused subdivision surface modifier to crash if at least one not connected vertex was in the mesh.
  The LWJGL 3 renderer was missing a call to GLContext.createFromCurrent(), sorted now.
  Changed the default app title in AppSettings to use the full name string from JmeVersion. This way no more manual changing of this will be needed for future versions. This also closes #320 which highlighted this issue. Thanks @8Keep.
  Fixed #316 where some post processing effects were not working when using OPENGL_3 renderer due to an error in the fragment shader.
  ...
define_list_fix
Rémy Bouquet 9 years ago
commit c47da8e9a1
  1. 2
      .gitignore
  2. 5
      .travis.yml
  3. 4
      common.gradle
  4. 4
      jme3-blender/src/main/java/com/jme3/asset/BlenderKey.java
  5. 85
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/AbstractBlenderHelper.java
  6. 137
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java
  7. 86
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderLoader.java
  8. 1
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/landscape/LandscapeHelper.java
  9. 8
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  10. 48
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/modifiers/SubdivisionSurfaceModifier.java
  11. 3
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java
  12. 1
      jme3-bullet-native/src/native/cpp/com_jme3_bullet_collision_shapes_GImpactCollisionShape.cpp
  13. 4
      jme3-bullet-native/src/native/cpp/jmeBulletUtil.cpp
  14. 5
      jme3-core/build.gradle
  15. 21
      jme3-core/src/main/java/com/jme3/asset/ImplHandler.java
  16. 38
      jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java
  17. 28
      jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java
  18. 38
      jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java
  19. 13
      jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java
  20. 48
      jme3-core/src/main/java/com/jme3/bounding/Intersection.java
  21. 3
      jme3-core/src/main/java/com/jme3/input/FlyByCamera.java
  22. 8
      jme3-core/src/main/java/com/jme3/light/AmbientLight.java
  23. 9
      jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java
  24. 10
      jme3-core/src/main/java/com/jme3/light/DirectionalLight.java
  25. 17
      jme3-core/src/main/java/com/jme3/light/Light.java
  26. 18
      jme3-core/src/main/java/com/jme3/light/PointLight.java
  27. 48
      jme3-core/src/main/java/com/jme3/light/SpotLight.java
  28. 19
      jme3-core/src/main/java/com/jme3/math/FastMath.java
  29. 13
      jme3-core/src/main/java/com/jme3/post/Filter.java
  30. 12
      jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java
  31. 2
      jme3-core/src/main/java/com/jme3/renderer/Limits.java
  32. 9
      jme3-core/src/main/java/com/jme3/renderer/RenderContext.java
  33. 1
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java
  34. 2
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL2.java
  35. 1
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java
  36. 365
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  37. 378
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java
  38. 1
      jme3-core/src/main/java/com/jme3/scene/debug/Grid.java
  39. 2
      jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java
  40. 1
      jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java
  41. 1
      jme3-core/src/main/java/com/jme3/scene/shape/Quad.java
  42. 2
      jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java
  43. 29
      jme3-core/src/main/java/com/jme3/system/AppSettings.java
  44. 14
      jme3-core/src/main/java/com/jme3/texture/Texture.java
  45. 9
      jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java
  46. 15
      jme3-core/src/main/java/com/jme3/util/ListMap.java
  47. 10
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag
  48. 1
      jme3-core/src/main/resources/Common/MatDefs/Misc/ColoredTextured.j3md
  49. 2
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.frag
  50. 2
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.vert
  51. 14
      jme3-core/src/main/resources/Common/ShaderLib/GLSL150Compat.glsllib
  52. 34
      jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib
  53. 6
      jme3-core/src/main/resources/joystick-mapping.properties
  54. 306
      jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
  55. 203
      jme3-core/src/test/java/com/jme3/collision/BoundingCollisionTest.java
  56. 80
      jme3-core/src/test/java/com/jme3/collision/CollisionUtil.java
  57. 271
      jme3-core/src/test/java/com/jme3/light/LightFilterTest.java
  58. 115
      jme3-core/src/test/java/com/jme3/light/LightSortTest.java
  59. 117
      jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java
  60. 59
      jme3-core/src/test/java/com/jme3/math/FastMathTest.java
  61. 73
      jme3-core/src/test/java/com/jme3/util/ListMapTest.java
  62. 11
      jme3-core/src/test/resources/texture-parameters-newstyle.j3m
  63. 6
      jme3-core/src/test/resources/texture-parameters-oldstyle.j3m
  64. 11
      jme3-desktop/src/main/java/com/jme3/app/state/MjpegFileWriter.java
  65. 51
      jme3-desktop/src/main/java/com/jme3/system/NativeLibraryLoader.java
  66. 6
      jme3-effects/src/main/java/com/jme3/post/filters/FXAAFilter.java
  67. 4
      jme3-effects/src/main/resources/Common/MatDefs/Post/CrossHatch15.frag
  68. 6
      jme3-effects/src/main/resources/Common/MatDefs/Post/DepthOfField15.frag
  69. 3
      jme3-effects/src/main/resources/Common/MatDefs/Post/Fade15.frag
  70. 3
      jme3-effects/src/main/resources/Common/MatDefs/Post/Fog15.frag
  71. 3
      jme3-effects/src/main/resources/Common/MatDefs/Post/Overlay15.frag
  72. 3
      jme3-effects/src/main/resources/Common/MatDefs/Post/Posterization15.frag
  73. 3
      jme3-effects/src/main/resources/Common/MatDefs/Post/bloomFinal15.frag
  74. 3
      jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssaoBlur15.frag
  75. 1
      jme3-examples/build.gradle
  76. 33
      jme3-examples/src/main/java/jme3test/asset/TestOnlineJar.java
  77. 7
      jme3-examples/src/main/java/jme3test/model/anim/TestCustomAnim.java
  78. 2
      jme3-examples/src/main/java/jme3test/renderer/TestInconsistentCompareDetection.java
  79. 1
      jme3-jbullet/build.gradle
  80. 6
      jme3-jogl/build.gradle
  81. 603
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java
  82. 88
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGLExt.java
  83. 97
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGLFbo.java
  84. 2699
      jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java
  85. 76
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglAbstractDisplay.java
  86. 33
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglCanvas.java
  87. 114
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java
  88. 80
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglGLDebugOutputHandler.java
  89. 76
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtAbstractDisplay.java
  90. 34
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtCanvas.java
  91. 2
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglNewtDisplay.java
  92. 6
      jme3-jogl/src/main/java/com/jme3/system/jogl/JoglOffscreenBuffer.java
  93. 2
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java
  94. 2
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java
  95. 2
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java
  96. 2
      jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java
  97. 1
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
  98. 9
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
  99. 1
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java
  100. 15
      jme3-lwjgl3/build.gradle
  101. Some files were not shown because too many files have changed in this diff Show More

2
.gitignore vendored

@ -1,6 +1,7 @@
/.gradle/ /.gradle/
/.nb-gradle/private/ /.nb-gradle/private/
/.nb-gradle/profiles/private/ /.nb-gradle/profiles/private/
/.idea/
/dist/ /dist/
/build/ /build/
/netbeans/ /netbeans/
@ -127,6 +128,7 @@
*.so *.so
*.jnilib *.jnilib
*.dylib *.dylib
*.iml
/sdk/dist/ /sdk/dist/
!/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll !/jme3-bullet-native/libs/native/windows/x86_64/bulletjme.dll
!/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll !/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll

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

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

@ -230,18 +230,22 @@ public class BlenderKey extends ModelKey {
} }
/** /**
* Not used any more.
* This method sets the asset root path. * This method sets the asset root path.
* @param assetRootPath * @param assetRootPath
* the assets root path * the assets root path
*/ */
@Deprecated
public void setAssetRootPath(String assetRootPath) { public void setAssetRootPath(String assetRootPath) {
this.assetRootPath = assetRootPath; this.assetRootPath = assetRootPath;
} }
/** /**
* Not used any more.
* This method returns the asset root path. * This method returns the asset root path.
* @return the asset root path * @return the asset root path
*/ */
@Deprecated
public String getAssetRootPath() { public String getAssetRootPath() {
return assetRootPath; return assetRootPath;
} }

@ -31,8 +31,6 @@
*/ */
package com.jme3.scene.plugins.blender; package com.jme3.scene.plugins.blender;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -40,25 +38,17 @@ import java.util.Map.Entry;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.animation.Animation;
import com.jme3.asset.AssetNotFoundException; import com.jme3.asset.AssetNotFoundException;
import com.jme3.asset.BlenderKey; import com.jme3.asset.BlenderKey;
import com.jme3.export.Savable; import com.jme3.export.Savable;
import com.jme3.light.Light;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType; import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
import com.jme3.scene.plugins.blender.objects.Properties; import com.jme3.scene.plugins.blender.objects.Properties;
import com.jme3.texture.Texture;
/** /**
* A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can * A purpose of the helper class is to split calculation code into several classes. Each helper after use should be cleared because it can
@ -157,7 +147,6 @@ public abstract class AbstractBlenderHelper {
* @throws BlenderFileException * @throws BlenderFileException
* and exception is throw when problems with reading a blend file occur * and exception is throw when problems with reading a blend file occur
*/ */
@SuppressWarnings("unchecked")
protected Object loadLibrary(Structure id) throws BlenderFileException { protected Object loadLibrary(Structure id) throws BlenderFileException {
Pointer pLib = (Pointer) id.getFieldValue("lib"); Pointer pLib = (Pointer) id.getFieldValue("lib");
if (pLib.isNotNull()) { if (pLib.isNotNull()) {
@ -167,79 +156,21 @@ public abstract class AbstractBlenderHelper {
String path = library.getFieldValue("filepath").toString(); String path = library.getFieldValue("filepath").toString();
if (!blenderContext.getLinkedFeatures().keySet().contains(path)) { if (!blenderContext.getLinkedFeatures().keySet().contains(path)) {
File file = new File(path);
List<String> pathsToCheck = new ArrayList<String>();
String currentPath = file.getName();
do {
pathsToCheck.add(currentPath);
file = file.getParentFile();
if (file != null) {
currentPath = file.getName() + '/' + currentPath;
}
} while (file != null);
Spatial loadedAsset = null; Spatial loadedAsset = null;
BlenderKey blenderKey = null; BlenderKey blenderKey = new BlenderKey(path);
for (String p : pathsToCheck) { blenderKey.setLoadUnlinkedAssets(true);
blenderKey = new BlenderKey(p); try {
blenderKey.setLoadUnlinkedAssets(true); loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey);
try { } catch (AssetNotFoundException e) {
loadedAsset = blenderContext.getAssetManager().loadAsset(blenderKey); LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", path);
break;// break if no exception was thrown
} catch (AssetNotFoundException e) {
LOGGER.log(Level.FINEST, "Cannot locate linked resource at path: {0}.", p);
}
} }
if (loadedAsset != null) { if (loadedAsset != null) {
Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData"); Map<String, Map<String, Object>> linkedData = loadedAsset.getUserData("linkedData");
for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) { for (Entry<String, Map<String, Object>> entry : linkedData.entrySet()) {
String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey(); String linkedDataFilePath = "this".equals(entry.getKey()) ? path : entry.getKey();
blenderContext.getLinkedFeatures().put(linkedDataFilePath, entry.getValue());
List<Node> scenes = (List<Node>) entry.getValue().get("scenes");
for (Node scene : scenes) {
blenderContext.addLinkedFeature(linkedDataFilePath, "SC" + scene.getName(), scene);
}
List<Node> objects = (List<Node>) entry.getValue().get("objects");
for (Node object : objects) {
blenderContext.addLinkedFeature(linkedDataFilePath, "OB" + object.getName(), object);
}
List<TemporalMesh> meshes = (List<TemporalMesh>) entry.getValue().get("meshes");
for (TemporalMesh mesh : meshes) {
blenderContext.addLinkedFeature(linkedDataFilePath, "ME" + mesh.getName(), mesh);
}
List<MaterialContext> materials = (List<MaterialContext>) entry.getValue().get("materials");
for (MaterialContext materialContext : materials) {
blenderContext.addLinkedFeature(linkedDataFilePath, "MA" + materialContext.getName(), materialContext);
}
List<Texture> textures = (List<Texture>) entry.getValue().get("textures");
for (Texture texture : textures) {
blenderContext.addLinkedFeature(linkedDataFilePath, "TE" + texture.getName(), texture);
}
List<Texture> images = (List<Texture>) entry.getValue().get("images");
for (Texture image : images) {
blenderContext.addLinkedFeature(linkedDataFilePath, "IM" + image.getName(), image);
}
List<Animation> animations = (List<Animation>) entry.getValue().get("animations");
for (Animation animation : animations) {
blenderContext.addLinkedFeature(linkedDataFilePath, "AC" + animation.getName(), animation);
}
List<Camera> cameras = (List<Camera>) entry.getValue().get("cameras");
for (Camera camera : cameras) {
blenderContext.addLinkedFeature(linkedDataFilePath, "CA" + camera.getName(), camera);
}
List<Light> lights = (List<Light>) entry.getValue().get("lights");
for (Light light : lights) {
blenderContext.addLinkedFeature(linkedDataFilePath, "LA" + light.getName(), light);
}
Spatial sky = (Spatial) entry.getValue().get("sky");
if (sky != null) {
blenderContext.addLinkedFeature(linkedDataFilePath, sky.getName(), sky);
}
List<Filter> filters = (List<Filter>) entry.getValue().get("filters");
for (Filter filter : filters) {
blenderContext.addLinkedFeature(linkedDataFilePath, filter.getName(), filter);
}
} }
} else { } else {
LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path); LOGGER.log(Level.WARNING, "No features loaded from path: {0}.", path);

@ -44,8 +44,11 @@ import com.jme3.animation.Bone;
import com.jme3.animation.Skeleton; import com.jme3.animation.Skeleton;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey; import com.jme3.asset.BlenderKey;
import com.jme3.light.Light;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.plugins.blender.animations.BlenderAction; import com.jme3.scene.plugins.blender.animations.BlenderAction;
import com.jme3.scene.plugins.blender.animations.BoneContext; import com.jme3.scene.plugins.blender.animations.BoneContext;
@ -55,6 +58,8 @@ import com.jme3.scene.plugins.blender.file.DnaBlockData;
import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.FileBlockHeader;
import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode; import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode;
import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialContext;
import com.jme3.texture.Texture;
/** /**
* The class that stores temporary data and manages it during loading the belnd * The class that stores temporary data and manages it during loading the belnd
@ -77,7 +82,7 @@ public class BlenderContext {
/** The asset manager. */ /** The asset manager. */
private AssetManager assetManager; private AssetManager assetManager;
/** The blocks read from the file. */ /** The blocks read from the file. */
protected List<FileBlockHeader> blocks; protected List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
/** /**
* A map containing the file block headers. The key is the old memory address. * A map containing the file block headers. The key is the old memory address.
*/ */
@ -233,6 +238,7 @@ public class BlenderContext {
* the block header to store * the block header to store
*/ */
public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) { public void addFileBlockHeader(Long oldMemoryAddress, FileBlockHeader fileBlockHeader) {
blocks.add(fileBlockHeader);
fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader); fileBlockHeadersByOma.put(oldMemoryAddress, fileBlockHeader);
List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode()); List<FileBlockHeader> headers = fileBlockHeadersByCode.get(fileBlockHeader.getCode());
if (headers == null) { if (headers == null) {
@ -242,6 +248,13 @@ public class BlenderContext {
headers.add(fileBlockHeader); headers.add(fileBlockHeader);
} }
/**
* @return the block headers
*/
public List<FileBlockHeader> getBlocks() {
return blocks;
}
/** /**
* This method returns the block header of a given memory address. If the * This method returns the block header of a given memory address. If the
* header is not present then null is returned. * header is not present then null is returned.
@ -332,22 +345,14 @@ public class BlenderContext {
* The method adds linked content to the blender context. * The method adds linked content to the blender context.
* @param blenderFilePath * @param blenderFilePath
* the path of linked blender file * the path of linked blender file
* @param featureName * @param featureGroup
* the linked feature name * the linked feature group (ie. scenes, materials, meshes, etc.)
* @param feature * @param feature
* the linked feature * the linked feature
*/ */
public void addLinkedFeature(String blenderFilePath, String featureName, Object feature) { @Deprecated
if (feature != null) { public void addLinkedFeature(String blenderFilePath, String featureGroup, Object feature) {
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath); // the method is deprecated and empty at the moment
if (linkedFeatures == null) {
linkedFeatures = new HashMap<String, Object>();
this.linkedFeatures.put(blenderFilePath, linkedFeatures);
}
if (!linkedFeatures.containsKey(featureName)) {
linkedFeatures.put(featureName, feature);
}
}
} }
/** /**
@ -358,9 +363,106 @@ public class BlenderContext {
* the feature name we want to get * the feature name we want to get
* @return linked feature or null if none was found * @return linked feature or null if none was found
*/ */
@SuppressWarnings("unchecked")
public Object getLinkedFeature(String blenderFilePath, String featureName) { public Object getLinkedFeature(String blenderFilePath, String featureName) {
Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath); Map<String, Object> linkedFeatures = this.linkedFeatures.get(blenderFilePath);
return linkedFeatures != null ? linkedFeatures.get(featureName) : null; if(linkedFeatures != null) {
String namePrefix = (featureName.charAt(0) + "" + featureName.charAt(1)).toUpperCase();
featureName = featureName.substring(2);
if("SC".equals(namePrefix)) {
List<Node> scenes = (List<Node>) linkedFeatures.get("scenes");
if(scenes != null) {
for(Node scene : scenes) {
if(featureName.equals(scene.getName())) {
return scene;
}
}
}
} else if("OB".equals(namePrefix)) {
List<Node> features = (List<Node>) linkedFeatures.get("objects");
if(features != null) {
for(Node feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("ME".equals(namePrefix)) {
List<Node> features = (List<Node>) linkedFeatures.get("meshes");
if(features != null) {
for(Node feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("MA".equals(namePrefix)) {
List<MaterialContext> features = (List<MaterialContext>) linkedFeatures.get("materials");
if(features != null) {
for(MaterialContext feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("TX".equals(namePrefix)) {
List<Texture> features = (List<Texture>) linkedFeatures.get("textures");
if(features != null) {
for(Texture feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("IM".equals(namePrefix)) {
List<Texture> features = (List<Texture>) linkedFeatures.get("images");
if(features != null) {
for(Texture feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("AC".equals(namePrefix)) {
List<Animation> features = (List<Animation>) linkedFeatures.get("animations");
if(features != null) {
for(Animation feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("CA".equals(namePrefix)) {
List<Camera> features = (List<Camera>) linkedFeatures.get("cameras");
if(features != null) {
for(Camera feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("LA".equals(namePrefix)) {
List<Light> features = (List<Light>) linkedFeatures.get("lights");
if(features != null) {
for(Light feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
} else if("FI".equals(featureName)) {
List<Filter> features = (List<Filter>) linkedFeatures.get("lights");
if(features != null) {
for(Filter feature : features) {
if(featureName.equals(feature.getName())) {
return feature;
}
}
}
}
}
return null;
} }
/** /**
@ -659,4 +761,9 @@ public class BlenderContext {
public static enum LoadedDataType { public static enum LoadedDataType {
STRUCTURE, FEATURE, TEMPORAL_MESH; STRUCTURE, FEATURE, TEMPORAL_MESH;
} }
@Override
public String toString() {
return blenderKey == null ? "BlenderContext [key = null]" : "BlenderContext [ key = " + blenderKey.toString() + " ]";
}
} }

@ -31,6 +31,9 @@
*/ */
package com.jme3.scene.plugins.blender; package com.jme3.scene.plugins.blender;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -41,9 +44,13 @@ import java.util.logging.Logger;
import com.jme3.animation.Animation; import com.jme3.animation.Animation;
import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetLoader; import com.jme3.asset.AssetLoader;
import com.jme3.asset.AssetLocator;
import com.jme3.asset.AssetManager;
import com.jme3.asset.BlenderKey; import com.jme3.asset.BlenderKey;
import com.jme3.asset.ModelKey; import com.jme3.asset.ModelKey;
import com.jme3.asset.StreamAssetInfo;
import com.jme3.light.Light; import com.jme3.light.Light;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.post.Filter; import com.jme3.post.Filter;
@ -81,22 +88,17 @@ import com.jme3.texture.Texture;
public class BlenderLoader implements AssetLoader { public class BlenderLoader implements AssetLoader {
private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName()); private static final Logger LOGGER = Logger.getLogger(BlenderLoader.class.getName());
/** The blocks read from the file. */
protected List<FileBlockHeader> blocks;
/** The blender context. */
protected BlenderContext blenderContext;
@Override @Override
public Spatial load(AssetInfo assetInfo) throws IOException { public Spatial load(AssetInfo assetInfo) throws IOException {
try { try {
this.setup(assetInfo); BlenderContext blenderContext = this.setup(assetInfo);
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class); AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.loadAnimations(); animationHelper.loadAnimations();
BlenderKey blenderKey = blenderContext.getBlenderKey(); BlenderKey blenderKey = blenderContext.getBlenderKey();
LoadedFeatures loadedFeatures = new LoadedFeatures(); LoadedFeatures loadedFeatures = new LoadedFeatures();
for (FileBlockHeader block : blocks) { for (FileBlockHeader block : blenderContext.getBlocks()) {
switch (block.getCode()) { switch (block.getCode()) {
case BLOCK_OB00: case BLOCK_OB00:
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
@ -181,7 +183,7 @@ public class BlenderLoader implements AssetLoader {
LOGGER.fine("Loading scenes and attaching them to the root object."); LOGGER.fine("Loading scenes and attaching them to the root object.");
for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) { for (FileBlockHeader sceneBlock : loadedFeatures.sceneBlocks) {
loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext))); loadedFeatures.scenes.add(this.toScene(sceneBlock.getStructure(blenderContext), blenderContext));
} }
LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it."); LOGGER.fine("Creating the root node of the model and applying loaded nodes of the scene and loaded features to it.");
@ -220,7 +222,7 @@ public class BlenderLoader implements AssetLoader {
} catch (Exception e) { } catch (Exception e) {
throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e); throw new IOException("Unexpected importer exception occured: " + e.getLocalizedMessage(), e);
} finally { } finally {
this.clear(); this.clear(assetInfo);
} }
} }
@ -228,11 +230,12 @@ public class BlenderLoader implements AssetLoader {
* This method converts the given structure to a scene node. * This method converts the given structure to a scene node.
* @param structure * @param structure
* structure of a scene * structure of a scene
* @param blenderContext the blender context
* @return scene's node * @return scene's node
* @throws BlenderFileException * @throws BlenderFileException
* an exception throw when problems with blender file occur * an exception throw when problems with blender file occur
*/ */
private Node toScene(Structure structure) throws BlenderFileException { private Node toScene(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class); ObjectHelper objectHelper = blenderContext.getHelper(ObjectHelper.class);
Node result = new Node(structure.getName()); Node result = new Node(structure.getName());
List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase(); List<Structure> base = ((Structure) structure.getFieldValue("base")).evaluateListBase();
@ -265,7 +268,7 @@ public class BlenderLoader implements AssetLoader {
* @throws BlenderFileException * @throws BlenderFileException
* an exception is throw when something wrong happens with blender file * an exception is throw when something wrong happens with blender file
*/ */
protected void setup(AssetInfo assetInfo) throws BlenderFileException { protected BlenderContext setup(AssetInfo assetInfo) throws BlenderFileException {
// registering loaders // registering loaders
ModelKey modelKey = (ModelKey) assetInfo.getKey(); ModelKey modelKey = (ModelKey) assetInfo.getKey();
BlenderKey blenderKey; BlenderKey blenderKey;
@ -273,16 +276,15 @@ public class BlenderLoader implements AssetLoader {
blenderKey = (BlenderKey) modelKey; blenderKey = (BlenderKey) modelKey;
} else { } else {
blenderKey = new BlenderKey(modelKey.getName()); blenderKey = new BlenderKey(modelKey.getName());
blenderKey.setAssetRootPath(modelKey.getFolder());
} }
// opening stream // opening stream
BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream()); BlenderInputStream inputStream = new BlenderInputStream(assetInfo.openStream());
// reading blocks // reading blocks
blocks = new ArrayList<FileBlockHeader>(); List<FileBlockHeader> blocks = new ArrayList<FileBlockHeader>();
FileBlockHeader fileBlock; FileBlockHeader fileBlock;
blenderContext = new BlenderContext(); BlenderContext blenderContext = new BlenderContext();
blenderContext.setBlenderVersion(inputStream.getVersionNumber()); blenderContext.setBlenderVersion(inputStream.getVersionNumber());
blenderContext.setAssetManager(assetInfo.getManager()); blenderContext.setAssetManager(assetInfo.getManager());
blenderContext.setInputStream(inputStream); blenderContext.setInputStream(inputStream);
@ -317,15 +319,19 @@ public class BlenderLoader implements AssetLoader {
if (sceneFileBlock != null) { if (sceneFileBlock != null) {
blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext)); blenderContext.setSceneStructure(sceneFileBlock.getStructure(blenderContext));
} }
// adding locator for linked content
assetInfo.getManager().registerLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
return blenderContext;
} }
/** /**
* The internal data is only needed during loading so make it unreachable so that the GC can release * The internal data is only needed during loading so make it unreachable so that the GC can release
* that memory (which can be quite large amount). * that memory (which can be quite large amount).
*/ */
protected void clear() { protected void clear(AssetInfo assetInfo) {
blenderContext = null; assetInfo.getManager().unregisterLocator(assetInfo.getKey().getName(), LinkedContentLocator.class);
blocks = null;
} }
/** /**
@ -362,4 +368,50 @@ public class BlenderLoader implements AssetLoader {
*/ */
private ColorRGBA backgroundColor = ColorRGBA.Gray; private ColorRGBA backgroundColor = ColorRGBA.Gray;
} }
public static class LinkedContentLocator implements AssetLocator {
private File rootFolder;
@Override
public void setRootPath(String rootPath) {
rootFolder = new File(rootPath);
if(rootFolder.isFile()) {
rootFolder = rootFolder.getParentFile();
}
}
@SuppressWarnings("rawtypes")
@Override
public AssetInfo locate(AssetManager manager, AssetKey key) {
if(key instanceof BlenderKey) {
File linkedAbsoluteFile = new File(key.getName());
if(linkedAbsoluteFile.exists() && linkedAbsoluteFile.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedAbsoluteFile));
} catch (FileNotFoundException e) {
return null;
}
}
File linkedFileInCurrentAssetFolder = new File(rootFolder, linkedAbsoluteFile.getName());
if(linkedFileInCurrentAssetFolder.exists() && linkedFileInCurrentAssetFolder.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentAssetFolder));
} catch (FileNotFoundException e) {
return null;
}
}
File linkedFileInCurrentFolder = new File(".", linkedAbsoluteFile.getName());
if(linkedFileInCurrentFolder.exists() && linkedFileInCurrentFolder.isFile()) {
try {
return new StreamAssetInfo(manager, key, new FileInputStream(linkedFileInCurrentFolder));
} catch (FileNotFoundException e) {
return null;
}
}
}
return null;
}
}
} }

@ -81,6 +81,7 @@ public class LandscapeHelper extends AbstractBlenderHelper {
if ((mode & MODE_MIST) != 0) { if ((mode & MODE_MIST) != 0) {
LOGGER.fine("Loading fog."); LOGGER.fine("Loading fog.");
result = new FogFilter(); result = new FogFilter();
result.setName("FIfog");
result.setFogColor(this.toBackgroundColor(worldStructure)); result.setFogColor(this.toBackgroundColor(worldStructure));
} }
return result; return result;

@ -168,9 +168,11 @@ public final class MaterialContext implements Savable {
this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture()); this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture());
List<Vector2f> uvs = combinedTexture.getResultUVS(); List<Vector2f> uvs = combinedTexture.getResultUVS();
VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]); if(uvs != null && uvs.size() > 0) {
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()]))); VertexBuffer uvCoordsBuffer = new VertexBuffer(TextureHelper.TEXCOORD_TYPES[textureIndex++]);
geometry.getMesh().setBuffer(uvCoordsBuffer); uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
geometry.getMesh().setBuffer(uvCoordsBuffer);
}//uvs might be null if the user assigned non existing UV coordinates group name to the mesh (this should be fixed in blender file)
} else { } else {
LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length); LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length);
} }

@ -2,6 +2,7 @@ package com.jme3.scene.plugins.blender.modifiers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -129,13 +130,18 @@ public class SubdivisionSurfaceModifier extends Modifier {
for (int i = 0; i < temporalMesh.getVertexCount(); ++i) { for (int i = 0; i < temporalMesh.getVertexCount(); ++i) {
// finding adjacent edges that were created by dividing original edges // finding adjacent edges that were created by dividing original edges
List<Edge> adjacentOriginalEdges = new ArrayList<Edge>(); List<Edge> adjacentOriginalEdges = new ArrayList<Edge>();
for (Edge edge : temporalMesh.getAdjacentEdges(i)) { Collection<Edge> adjacentEdges = temporalMesh.getAdjacentEdges(i);
if (verticesOnOriginalEdges.contains(edge.getFirstIndex()) || verticesOnOriginalEdges.contains(edge.getSecondIndex())) { if(adjacentEdges != null) {// this can be null if a vertex with index 'i' is not connected to any face nor edge
adjacentOriginalEdges.add(edge); for (Edge edge : temporalMesh.getAdjacentEdges(i)) {
if (verticesOnOriginalEdges.contains(edge.getFirstIndex()) || verticesOnOriginalEdges.contains(edge.getSecondIndex())) {
adjacentOriginalEdges.add(edge);
}
} }
}
creasePoints.add(new CreasePoint(i, boundaryVertices.contains(i), adjacentOriginalEdges, temporalMesh)); creasePoints.add(new CreasePoint(i, boundaryVertices.contains(i), adjacentOriginalEdges, temporalMesh));
} else {
creasePoints.add(null);//the count of crease points must be equal to vertex count; otherwise we'll get IndexOutofBoundsException later
}
} }
Vector3f[] averageVert = new Vector3f[temporalMesh.getVertexCount()]; Vector3f[] averageVert = new Vector3f[temporalMesh.getVertexCount()];
@ -174,23 +180,25 @@ public class SubdivisionSurfaceModifier extends Modifier {
} }
for (int i = 0; i < averageVert.length; ++i) { for (int i = 0; i < averageVert.length; ++i) {
Vector3f v = temporalMesh.getVertices().get(i); if(averageVert[i] != null && averageCount[i] > 0) {
averageVert[i].divideLocal(averageCount[i]); Vector3f v = temporalMesh.getVertices().get(i);
averageVert[i].divideLocal(averageCount[i]);
// computing translation vector
Vector3f t = averageVert[i].subtract(v); // computing translation vector
if (!boundaryVertices.contains(i)) { Vector3f t = averageVert[i].subtract(v);
t.multLocal(4 / (float) averageCount[i]); if (!boundaryVertices.contains(i)) {
} t.multLocal(4 / (float) averageCount[i]);
}
// moving the vertex
v.addLocal(t);
// applying crease weight if neccessary // moving the vertex
CreasePoint creasePoint = creasePoints.get(i);
if (creasePoint.getTarget() != null && creasePoint.getWeight() != 0) {
t = creasePoint.getTarget().subtractLocal(v).multLocal(creasePoint.getWeight());
v.addLocal(t); v.addLocal(t);
// applying crease weight if neccessary
CreasePoint creasePoint = creasePoints.get(i);
if (creasePoint.getTarget() != null && creasePoint.getWeight() != 0) {
t = creasePoint.getTarget().subtractLocal(v).multLocal(creasePoint.getWeight());
v.addLocal(t);
}
} }
} }
} }

@ -158,6 +158,9 @@ public class CombinedTexture {
} else { } else {
resultUVS = userDefinedUVCoordinates.get(textureData.uvCoordinatesName); resultUVS = userDefinedUVCoordinates.get(textureData.uvCoordinatesName);
} }
if(resultUVS == null && LOGGER.isLoggable(Level.WARNING)) {
LOGGER.warning("The texture " + textureData.texture.getName() + " has assigned non existing UV coordinates group: " + textureData.uvCoordinatesName + ".");
}
masterUserUVSetName = textureData.uvCoordinatesName; masterUserUVSetName = textureData.uvCoordinatesName;
} else { } else {
TemporalMesh temporalMesh = (TemporalMesh) blenderContext.getLoadedFeature(geometriesOMA, LoadedDataType.TEMPORAL_MESH); TemporalMesh temporalMesh = (TemporalMesh) blenderContext.getLoadedFeature(geometriesOMA, LoadedDataType.TEMPORAL_MESH);

@ -51,6 +51,7 @@ extern "C" {
jmeClasses::initJavaClasses(env); jmeClasses::initJavaClasses(env);
btTriangleIndexVertexArray* array = reinterpret_cast<btTriangleIndexVertexArray*>(meshId); btTriangleIndexVertexArray* array = reinterpret_cast<btTriangleIndexVertexArray*>(meshId);
btGImpactMeshShape* shape = new btGImpactMeshShape(array); btGImpactMeshShape* shape = new btGImpactMeshShape(array);
shape->updateBound();
return reinterpret_cast<jlong>(shape); return reinterpret_cast<jlong>(shape);
} }

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

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

@ -75,7 +75,7 @@ final class ImplHandler {
this.assetManager = assetManager; this.assetManager = assetManager;
} }
protected static class ImplThreadLocal<T> extends ThreadLocal { protected static class ImplThreadLocal<T> extends ThreadLocal<T> {
private final Class<T> type; private final Class<T> type;
private final String path; private final String path;
@ -112,9 +112,13 @@ final class ImplHandler {
} }
@Override @Override
protected Object initialValue(){ protected T initialValue(){
try { try {
return type.newInstance(); T obj = type.newInstance();
if (path != null) {
((AssetLocator)obj).setRootPath(path);
}
return obj;
} catch (InstantiationException ex) { } catch (InstantiationException ex) {
logger.log(Level.SEVERE,"Cannot create locator of type {0}, does" logger.log(Level.SEVERE,"Cannot create locator of type {0}, does"
+ " the class have an empty and publically accessible"+ + " the class have an empty and publically accessible"+
@ -169,14 +173,11 @@ final class ImplHandler {
return null; return null;
} }
for (ImplThreadLocal local : locatorsList){ for (ImplThreadLocal<AssetLocator> local : locatorsList){
AssetLocator locator = (AssetLocator) local.get(); AssetInfo info = local.get().locate(assetManager, key);
if (local.getPath() != null){ if (info != null) {
locator.setRootPath((String) local.getPath());
}
AssetInfo info = locator.locate(assetManager, key);
if (info != null)
return info; return info;
}
} }
return null; return null;

@ -31,22 +31,20 @@
*/ */
package com.jme3.audio.openal; package com.jme3.audio.openal;
import com.jme3.audio.AudioSource.Status;
import com.jme3.audio.*; import com.jme3.audio.*;
import com.jme3.audio.AudioSource.Status;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.NativeObjectManager; import com.jme3.util.NativeObjectManager;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static com.jme3.audio.openal.AL.*; import static com.jme3.audio.openal.AL.*;
import static com.jme3.audio.openal.ALC.*;
import static com.jme3.audio.openal.EFX.*;
public class ALAudioRenderer implements AudioRenderer, Runnable { public class ALAudioRenderer implements AudioRenderer, Runnable {
@ -102,16 +100,6 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
return; return;
} }
String deviceName = alc.alcGetString(ALC.ALC_DEVICE_SPECIFIER);
logger.log(Level.INFO, "Audio Device: {0}", deviceName);
logger.log(Level.INFO, "Audio Vendor: {0}", al.alGetString(AL_VENDOR));
logger.log(Level.INFO, "Audio Renderer: {0}", al.alGetString(AL_RENDERER));
logger.log(Level.INFO, "Audio Version: {0}", al.alGetString(AL_VERSION));
logger.log(Level.INFO, "ALC extensions: {0}", alc.alcGetString(ALC.ALC_EXTENSIONS));
logger.log(Level.INFO, "AL extensions: {0}", al.alGetString(AL_EXTENSIONS));
// Find maximum # of sources supported by this implementation // Find maximum # of sources supported by this implementation
ArrayList<Integer> channelList = new ArrayList<Integer>(); ArrayList<Integer> channelList = new ArrayList<Integer>();
for (int i = 0; i < MAX_NUM_CHANNELS; i++) { for (int i = 0; i < MAX_NUM_CHANNELS; i++) {
@ -131,7 +119,25 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
ib = BufferUtils.createIntBuffer(channels.length); ib = BufferUtils.createIntBuffer(channels.length);
chanSrcs = new AudioSource[channels.length]; chanSrcs = new AudioSource[channels.length];
logger.log(Level.INFO, "AudioRenderer supports {0} channels", channels.length); final String deviceName = alc.alcGetString(ALC.ALC_DEVICE_SPECIFIER);
logger.log(Level.INFO, "Audio Renderer Information\n" +
" * Device: {0}\n" +
" * Vendor: {1}\n" +
" * Renderer: {2}\n" +
" * Version: {3}\n" +
" * Supported channels: {4}\n" +
" * ALC extensions: {5}\n" +
" * AL extensions: {6}",
new Object[]{
deviceName,
al.alGetString(AL_VENDOR),
al.alGetString(AL_RENDERER),
al.alGetString(AL_VERSION),
channels.length,
alc.alcGetString(ALC.ALC_EXTENSIONS),
al.alGetString(AL_EXTENSIONS)
});
// Pause device is a feature used specifically on Android // Pause device is a feature used specifically on Android
// where the application could be closed but still running, // where the application could be closed but still running,
@ -153,7 +159,7 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
alc.alcGetInteger(EFX.ALC_MAX_AUXILIARY_SENDS, ib, 1); alc.alcGetInteger(EFX.ALC_MAX_AUXILIARY_SENDS, ib, 1);
auxSends = ib.get(0); auxSends = ib.get(0);
logger.log(Level.INFO, "Audio max auxilary sends: {0}", auxSends); logger.log(Level.INFO, "Audio max auxiliary sends: {0}", auxSends);
// create slot // create slot
ib.position(0).limit(1); ib.position(0).limit(1);

@ -41,6 +41,7 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.math.*; import com.jme3.math.*;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import java.io.IOException; import java.io.IOException;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
@ -313,7 +314,7 @@ public class BoundingBox extends BoundingVolume {
transMatrix.absoluteLocal(); transMatrix.absoluteLocal();
Vector3f scale = trans.getScale(); Vector3f scale = trans.getScale();
vars.vect1.set(xExtent * scale.x, yExtent * scale.y, zExtent * scale.z); vars.vect1.set(xExtent * FastMath.abs(scale.x), yExtent * FastMath.abs(scale.y), zExtent * FastMath.abs(scale.z));
transMatrix.mult(vars.vect1, vars.vect2); transMatrix.mult(vars.vect1, vars.vect2);
// Assign the biggest rotations after scales. // Assign the biggest rotations after scales.
box.xExtent = FastMath.abs(vars.vect2.getX()); box.xExtent = FastMath.abs(vars.vect2.getX());
@ -593,18 +594,7 @@ public class BoundingBox extends BoundingVolume {
* @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere) * @see BoundingVolume#intersectsSphere(com.jme3.bounding.BoundingSphere)
*/ */
public boolean intersectsSphere(BoundingSphere bs) { public boolean intersectsSphere(BoundingSphere bs) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center); return bs.intersectsBoundingBox(this);
if (FastMath.abs(center.x - bs.center.x) < bs.getRadius()
+ xExtent
&& FastMath.abs(center.y - bs.center.y) < bs.getRadius()
+ yExtent
&& FastMath.abs(center.z - bs.center.z) < bs.getRadius()
+ zExtent) {
return true;
}
return false;
} }
/** /**
@ -790,6 +780,7 @@ public class BoundingBox extends BoundingVolume {
} }
} }
@Override
public int collideWith(Collidable other, CollisionResults results) { public int collideWith(Collidable other, CollisionResults results) {
if (other instanceof Ray) { if (other instanceof Ray) {
Ray ray = (Ray) other; Ray ray = (Ray) other;
@ -802,6 +793,15 @@ public class BoundingBox extends BoundingVolume {
return 1; return 1;
} }
return 0; return 0;
} else if (other instanceof BoundingVolume) {
if (intersects((BoundingVolume) other)) {
CollisionResult r = new CollisionResult();
results.addCollision(r);
return 1;
}
return 0;
} else if (other instanceof Spatial) {
return ((Spatial)other).collideWith(this, results);
} else { } else {
throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName()); throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName());
} }
@ -818,6 +818,8 @@ public class BoundingBox extends BoundingVolume {
return 1; return 1;
} }
return 0; return 0;
} else if (other instanceof BoundingVolume) {
return intersects((BoundingVolume) other) ? 1 : 0;
} else { } else {
throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName()); throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName());
} }

@ -38,6 +38,7 @@ import com.jme3.collision.UnsupportedCollisionException;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.math.*; import com.jme3.math.*;
import com.jme3.scene.Spatial;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import java.io.IOException; import java.io.IOException;
@ -670,15 +671,7 @@ public class BoundingSphere extends BoundingVolume {
* @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere) * @see com.jme.bounding.BoundingVolume#intersectsSphere(com.jme.bounding.BoundingSphere)
*/ */
public boolean intersectsSphere(BoundingSphere bs) { public boolean intersectsSphere(BoundingSphere bs) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bs.center); return Intersection.intersect(bs, center, radius);
TempVars vars = TempVars.get();
Vector3f diff = center.subtract(bs.center, vars.vect1);
float rsum = getRadius() + bs.getRadius();
boolean eq = (diff.dot(diff) <= rsum * rsum);
vars.release();
return eq;
} }
/* /*
@ -687,18 +680,7 @@ public class BoundingSphere extends BoundingVolume {
* @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox) * @see com.jme.bounding.BoundingVolume#intersectsBoundingBox(com.jme.bounding.BoundingBox)
*/ */
public boolean intersectsBoundingBox(BoundingBox bb) { public boolean intersectsBoundingBox(BoundingBox bb) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bb.center); return Intersection.intersect(bb, center, radius);
if (FastMath.abs(bb.center.x - center.x) < getRadius()
+ bb.xExtent
&& FastMath.abs(bb.center.y - center.y) < getRadius()
+ bb.yExtent
&& FastMath.abs(bb.center.z - center.z) < getRadius()
+ bb.zExtent) {
return true;
}
return false;
} }
/* /*
@ -1013,17 +995,29 @@ public class BoundingSphere extends BoundingVolume {
} else if (other instanceof Triangle){ } else if (other instanceof Triangle){
Triangle t = (Triangle) other; Triangle t = (Triangle) other;
return collideWithTri(t, results); return collideWithTri(t, results);
} else if (other instanceof BoundingVolume) {
if (intersects((BoundingVolume)other)) {
CollisionResult result = new CollisionResult();
results.addCollision(result);
return 1;
}
return 0;
} else if (other instanceof Spatial) {
return ((Spatial)other).collideWith(this, results);
} else { } else {
throw new UnsupportedCollisionException(); throw new UnsupportedCollisionException();
} }
} }
@Override public int collideWith(Collidable other) { @Override
public int collideWith(Collidable other) {
if (other instanceof Ray) { if (other instanceof Ray) {
Ray ray = (Ray) other; Ray ray = (Ray) other;
return collideWithRay(ray); return collideWithRay(ray);
} else if (other instanceof Triangle){ } else if (other instanceof Triangle){
return super.collideWith(other); return super.collideWith(other);
} else if (other instanceof BoundingVolume) {
return intersects((BoundingVolume)other) ? 1 : 0;
} else { } else {
throw new UnsupportedCollisionException(); throw new UnsupportedCollisionException();
} }

@ -327,12 +327,13 @@ public abstract class BoundingVolume implements Savable, Cloneable, Collidable {
public int collideWith(Collidable other) { public int collideWith(Collidable other) {
TempVars tempVars = TempVars.get(); TempVars tempVars = TempVars.get();
CollisionResults tempResults = tempVars.collisionResults; try {
tempResults.clear(); CollisionResults tempResults = tempVars.collisionResults;
int retval = collideWith(other, tempResults); tempResults.clear();
tempVars.release(); return collideWith(other, tempResults);
return retval; } finally {
tempVars.release();
}
} }
} }

@ -41,9 +41,55 @@ import static java.lang.Math.min;
/** /**
* This class includes some utility methods for computing intersection * This class includes some utility methods for computing intersection
* between bounding volumes and triangles. * between bounding volumes and triangles.
*
* @author Kirill * @author Kirill
*/ */
public class Intersection { public final class Intersection {
private Intersection() {
}
public static boolean intersect(BoundingSphere sphere, Vector3f center, float radius) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(sphere.center);
TempVars vars = TempVars.get();
try {
Vector3f diff = center.subtract(sphere.center, vars.vect1);
float rsum = sphere.getRadius() + radius;
return (diff.dot(diff) <= rsum * rsum);
} finally {
vars.release();
}
}
public static boolean intersect(BoundingBox bbox, Vector3f center, float radius) {
assert Vector3f.isValidVector(center) && Vector3f.isValidVector(bbox.center);
// Arvo's algorithm
float distSqr = radius * radius;
float minX = bbox.center.x - bbox.xExtent;
float maxX = bbox.center.x + bbox.xExtent;
float minY = bbox.center.y - bbox.yExtent;
float maxY = bbox.center.y + bbox.yExtent;
float minZ = bbox.center.z - bbox.zExtent;
float maxZ = bbox.center.z + bbox.zExtent;
if (center.x < minX) distSqr -= FastMath.sqr(center.x - minX);
else if (center.x > maxX) distSqr -= FastMath.sqr(center.x - maxX);
if (center.y < minY) distSqr -= FastMath.sqr(center.y - minY);
else if (center.y > maxY) distSqr -= FastMath.sqr(center.y - maxY);
if (center.z < minZ) distSqr -= FastMath.sqr(center.z - minZ);
else if (center.z > maxZ) distSqr -= FastMath.sqr(center.z - maxZ);
return distSqr > 0;
}
private static final void findMinMax(float x0, float x1, float x2, Vector3f minMax) { private static final void findMinMax(float x0, float x1, float x2, Vector3f minMax) {
minMax.set(x0, x0, 0); minMax.set(x0, x0, 0);

@ -277,8 +277,7 @@ public class FlyByCamera implements AnalogListener, ActionListener {
} }
/** /**
* Registers the FlyByCamera to receive input events from the provided * Unregisters the FlyByCamera from the event Dispatcher.
* Dispatcher.
*/ */
public void unregisterInput(){ public void unregisterInput(){

@ -32,6 +32,7 @@
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
@ -62,6 +63,11 @@ public class AmbientLight extends Light {
return true; return true;
} }
@Override
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
return true;
}
@Override @Override
public boolean intersectsFrustum(Camera camera, TempVars vars) { public boolean intersectsFrustum(Camera camera, TempVars vars) {
return true; return true;
@ -69,6 +75,8 @@ public class AmbientLight extends Light {
@Override @Override
public void computeLastDistance(Spatial owner) { public void computeLastDistance(Spatial owner) {
// ambient lights must always be before directional lights.
lastDistance = -2;
} }
@Override @Override

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2014 jMonkeyEngine * Copyright (c) 2009-2015 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -44,6 +44,7 @@ public final class DefaultLightFilter implements LightFilter {
private Camera camera; private Camera camera;
private final HashSet<Light> processedLights = new HashSet<Light>(); private final HashSet<Light> processedLights = new HashSet<Light>();
@Override
public void setCamera(Camera camera) { public void setCamera(Camera camera) {
this.camera = camera; this.camera = camera;
for (Light light : processedLights) { for (Light light : processedLights) {
@ -51,6 +52,7 @@ public final class DefaultLightFilter implements LightFilter {
} }
} }
@Override
public void filterLights(Geometry geometry, LightList filteredLightList) { public void filterLights(Geometry geometry, LightList filteredLightList) {
TempVars vars = TempVars.get(); TempVars vars = TempVars.get();
try { try {
@ -77,8 +79,9 @@ public final class DefaultLightFilter implements LightFilter {
} }
} else if (bv instanceof BoundingSphere) { } else if (bv instanceof BoundingSphere) {
if (!Float.isInfinite( ((BoundingSphere)bv).getRadius() )) { if (!Float.isInfinite( ((BoundingSphere)bv).getRadius() )) {
// Non-infinite bounding sphere... Not supported yet. if (!light.intersectsSphere((BoundingSphere)bv, vars)) {
throw new UnsupportedOperationException("Only AABB supported for now"); continue;
}
} }
} }

@ -32,6 +32,7 @@
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.export.InputCapsule; import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
@ -80,7 +81,9 @@ public class DirectionalLight extends Light {
@Override @Override
public void computeLastDistance(Spatial owner) { public void computeLastDistance(Spatial owner) {
lastDistance = 0; // directional lights are always closest to their owner // directional lights are after ambient lights
// but before all other lights.
lastDistance = -1;
} }
/** /**
@ -114,6 +117,11 @@ public class DirectionalLight extends Light {
return true; return true;
} }
@Override
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
return true;
}
@Override @Override
public boolean intersectsFrustum(Camera camera, TempVars vars) { public boolean intersectsFrustum(Camera camera, TempVars vars) {
return true; return true;

@ -32,6 +32,7 @@
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.export.*; import com.jme3.export.*;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
@ -204,7 +205,21 @@ public abstract class Light implements Savable, Cloneable {
public abstract boolean intersectsBox(BoundingBox box, TempVars vars); public abstract boolean intersectsBox(BoundingBox box, TempVars vars);
/** /**
* Determines if the lgiht intersects with the given camera frustum. * Determines if the light intersects with the given bounding sphere.
* <p>
* For non-local lights, such as {@link DirectionalLight directional lights},
* {@link AmbientLight ambient lights}, or {@link PointLight point lights}
* without influence radius, this method should always return true.
*
* @param sphere The sphere to check intersection against.
* @param vars TempVars in case it is needed.
*
* @return True if the light intersects the sphere, false otherwise.
*/
public abstract boolean intersectsSphere(BoundingSphere sphere, TempVars vars);
/**
* Determines if the light intersects with the given camera frustum.
* *
* For non-local lights, such as {@link DirectionalLight directional lights}, * For non-local lights, such as {@link DirectionalLight directional lights},
* {@link AmbientLight ambient lights}, or {@link PointLight point lights} * {@link AmbientLight ambient lights}, or {@link PointLight point lights}

@ -32,13 +32,15 @@
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume; import com.jme3.bounding.BoundingVolume;
import com.jme3.bounding.Intersection;
import com.jme3.export.InputCapsule; import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
@ -191,9 +193,17 @@ public class PointLight extends Light {
return true; return true;
} else { } else {
// Sphere v. box collision // Sphere v. box collision
return FastMath.abs(box.getCenter().x - position.x) < radius + box.getXExtent() return Intersection.intersect(box, position, radius);
&& FastMath.abs(box.getCenter().y - position.y) < radius + box.getYExtent() }
&& FastMath.abs(box.getCenter().z - position.z) < radius + box.getZExtent(); }
@Override
public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
if (this.radius == 0) {
return true;
} else {
// Sphere v. sphere collision
return Intersection.intersect(sphere, position, radius);
} }
} }

@ -32,7 +32,9 @@
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume; import com.jme3.bounding.BoundingVolume;
import com.jme3.bounding.Intersection;
import com.jme3.export.*; import com.jme3.export.*;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
@ -188,9 +190,7 @@ public class SpotLight extends Light {
if (this.spotRange > 0f) { if (this.spotRange > 0f) {
// Check spot range first. // Check spot range first.
// Sphere v. box collision // Sphere v. box collision
if (FastMath.abs(box.getCenter().x - position.x) >= spotRange + box.getXExtent() if (!Intersection.intersect(box, position, spotRange)) {
|| FastMath.abs(box.getCenter().y - position.y) >= spotRange + box.getYExtent()
|| FastMath.abs(box.getCenter().z - position.z) >= spotRange + box.getZExtent()) {
return false; return false;
} }
} }
@ -226,8 +226,48 @@ public class SpotLight extends Light {
} }
@Override @Override
public boolean intersectsFrustum(Camera cam, TempVars vars) { public boolean intersectsSphere(BoundingSphere sphere, TempVars vars) {
if (this.spotRange > 0f) {
// Check spot range first.
// Sphere v. sphere collision
if (!Intersection.intersect(sphere, position, spotRange)) {
return false;
}
}
float otherRadiusSquared = FastMath.sqr(sphere.getRadius());
float otherRadius = sphere.getRadius();
// Check if sphere is within spot angle.
// Cone v. sphere collision.
Vector3f E = direction.mult(otherRadius * outerAngleSinRcp, vars.vect1);
Vector3f U = position.subtract(E, vars.vect2);
Vector3f D = sphere.getCenter().subtract(U, vars.vect3);
float dsqr = D.dot(D);
float e = direction.dot(D);
if (e > 0f && e * e >= dsqr * outerAngleCosSqr) {
D = sphere.getCenter().subtract(position, vars.vect3);
dsqr = D.dot(D);
e = -direction.dot(D);
if (e > 0f && e * e >= dsqr * outerAngleSinSqr) {
return dsqr <= otherRadiusSquared;
} else {
return true;
}
}
return false;
}
@Override
public boolean intersectsFrustum(Camera cam, TempVars vars) {
if (spotRange == 0) {
// The algorithm below does not support infinite spot range.
return true;
}
Vector3f farPoint = vars.vect1.set(position).addLocal(vars.vect2.set(direction).multLocal(spotRange)); Vector3f farPoint = vars.vect1.set(position).addLocal(vars.vect2.set(direction).multLocal(spotRange));
for (int i = 5; i >= 0; i--) { for (int i = 5; i >= 0; i--) {
//check origin against the plane //check origin against the plane

@ -87,8 +87,25 @@ final public class FastMath {
return (number > 0) && (number & (number - 1)) == 0; return (number > 0) && (number & (number - 1)) == 0;
} }
/**
* Get the next power of two of the given number.
*
* E.g. for an input 100, this returns 128.
* Returns 1 for all numbers <= 1.
*
* @param number The number to obtain the POT for.
* @return The next power of two.
*/
public static int nearestPowerOfTwo(int number) { public static int nearestPowerOfTwo(int number) {
return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2))); number--;
number |= number >> 1;
number |= number >> 2;
number |= number >> 4;
number |= number >> 8;
number |= number >> 16;
number++;
number += (number == 0) ? 1 : 0;
return number;
} }
/** /**

@ -409,6 +409,19 @@ public abstract class Filter implements Savable {
return true; return true;
} }
/**
* Override this method and return true if you want the scene (input) texture
* to use bilinear filtering or false to use nearest filtering.
*
* Typically filters that perform samples <em>in between</em> pixels
* should enable filtering.
*
* @return true to use linear filtering, false to use nearest filtering.
*/
protected boolean isRequiresBilinear() {
return false;
}
/** /**
* returns the list of the postRender passes * returns the list of the postRender passes
* @return * @return

@ -38,6 +38,7 @@ import com.jme3.renderer.*;
import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue;
import com.jme3.texture.FrameBuffer; import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format; import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D; import com.jme3.texture.Texture2D;
import com.jme3.ui.Picture; import com.jme3.ui.Picture;
import com.jme3.util.SafeArrayList; import com.jme3.util.SafeArrayList;
@ -285,6 +286,12 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
} }
} }
boolean wantsBilinear = filter.isRequiresBilinear();
if (wantsBilinear) {
tex.setMagFilter(Texture.MagFilter.Bilinear);
tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
}
buff = outputBuffer; buff = outputBuffer;
if (i != lastFilterIndex) { if (i != lastFilterIndex) {
buff = filter.getRenderFrameBuffer(); buff = filter.getRenderFrameBuffer();
@ -293,6 +300,11 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
} }
renderProcessing(r, buff, mat); renderProcessing(r, buff, mat);
filter.postFilter(r, buff); filter.postFilter(r, buff);
if (wantsBilinear) {
tex.setMagFilter(Texture.MagFilter.Nearest);
tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
}
} }
} }
} }

@ -77,4 +77,6 @@ public enum Limits {
DepthTextureSamples, DepthTextureSamples,
VertexUniformVectors, VertexUniformVectors,
TextureAnisotropy,
} }

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

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

@ -44,7 +44,7 @@ public interface GL2 extends GL {
public static final int GL_ALPHA_TEST = 0xBC0; public static final int GL_ALPHA_TEST = 0xBC0;
public static final int GL_BGR = 0x80E0; public static final int GL_BGR = 0x80E0;
public static final int GL_BGRA = 0x80E1; public static final int GL_BGRA = 0x80E1;
public static final int GL_COMPARE_R_TO_TEXTURE = 0x884E; public static final int GL_COMPARE_REF_TO_TEXTURE = 0x884E;
public static final int GL_DEPTH_COMPONENT24 = 0x81A6; public static final int GL_DEPTH_COMPONENT24 = 0x81A6;
public static final int GL_DEPTH_COMPONENT32 = 0x81A7; public static final int GL_DEPTH_COMPONENT32 = 0x81A7;
public static final int GL_DEPTH_TEXTURE_MODE = 0x884B; public static final int GL_DEPTH_TEXTURE_MODE = 0x884B;

@ -68,6 +68,7 @@ public interface GLExt {
public static final int GL_MAX_DEPTH_TEXTURE_SAMPLES = 0x910F; public static final int GL_MAX_DEPTH_TEXTURE_SAMPLES = 0x910F;
public static final int GL_MAX_DRAW_BUFFERS_ARB = 0x8824; public static final int GL_MAX_DRAW_BUFFERS_ARB = 0x8824;
public static final int GL_MAX_SAMPLES_EXT = 0x8D57; public static final int GL_MAX_SAMPLES_EXT = 0x8D57;
public static final int GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF;
public static final int GL_MULTISAMPLE_ARB = 0x809D; public static final int GL_MULTISAMPLE_ARB = 0x809D;
public static final int GL_NUM_PROGRAM_BINARY_FORMATS = 0x87FE; public static final int GL_NUM_PROGRAM_BINARY_FORMATS = 0x87FE;
public static final int GL_PIXEL_PACK_BUFFER_ARB = 0x88EB; public static final int GL_PIXEL_PACK_BUFFER_ARB = 0x88EB;

@ -51,7 +51,9 @@ import com.jme3.texture.FrameBuffer;
import com.jme3.texture.FrameBuffer.RenderBuffer; import com.jme3.texture.FrameBuffer.RenderBuffer;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.Texture.ShadowCompareMode;
import com.jme3.texture.Texture.WrapAxis; import com.jme3.texture.Texture.WrapAxis;
import com.jme3.texture.image.LastTextureState;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import com.jme3.util.ListMap; import com.jme3.util.ListMap;
import com.jme3.util.MipMapGenerator; import com.jme3.util.MipMapGenerator;
@ -68,7 +70,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jme3tools.shader.ShaderDebug; import jme3tools.shader.ShaderDebug;
public class GLRenderer implements Renderer { public final class GLRenderer implements Renderer {
private static final Logger logger = Logger.getLogger(GLRenderer.class.getName()); private static final Logger logger = Logger.getLogger(GLRenderer.class.getName());
private static final boolean VALIDATE_SHADER = false; private static final boolean VALIDATE_SHADER = false;
@ -374,17 +376,18 @@ public class GLRenderer implements Renderer {
if (hasExtension("GL_EXT_texture_filter_anisotropic")) { if (hasExtension("GL_EXT_texture_filter_anisotropic")) {
caps.add(Caps.TextureFilterAnisotropic); caps.add(Caps.TextureFilterAnisotropic);
limits.put(Limits.TextureAnisotropy, getInteger(GLExt.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT));
} }
if (hasExtension("GL_EXT_framebuffer_object") if (hasExtension("GL_EXT_framebuffer_object")
|| gl3 != null || caps.contains(Caps.OpenGL30)
|| caps.contains(Caps.OpenGLES20)) { || caps.contains(Caps.OpenGLES20)) {
caps.add(Caps.FrameBuffer); caps.add(Caps.FrameBuffer);
limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT)); limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT));
limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT)); limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT));
if (hasExtension("GL_EXT_framebuffer_blit")) { if (hasExtension("GL_EXT_framebuffer_blit") || caps.contains(Caps.OpenGL30)) {
caps.add(Caps.FrameBufferBlit); caps.add(Caps.FrameBufferBlit);
} }
@ -403,7 +406,7 @@ public class GLRenderer implements Renderer {
} }
} }
if (hasExtension("GL_ARB_draw_buffers") || gl3 != null) { if (hasExtension("GL_ARB_draw_buffers") || caps.contains(Caps.OpenGL30)) {
limits.put(Limits.FrameBufferMrtAttachments, getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB)); limits.put(Limits.FrameBufferMrtAttachments, getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB));
if (limits.get(Limits.FrameBufferMrtAttachments) > 1) { if (limits.get(Limits.FrameBufferMrtAttachments) > 1) {
caps.add(Caps.FrameBufferMRT); caps.add(Caps.FrameBufferMRT);
@ -504,6 +507,11 @@ public class GLRenderer implements Renderer {
// Initialize default state.. // Initialize default state..
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
if (caps.contains(Caps.SeamlessCubemap)) {
// Enable this globally. Should be OK.
gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
if (caps.contains(Caps.CoreProfile)) { if (caps.contains(Caps.CoreProfile)) {
// Core Profile requires VAO to be bound. // Core Profile requires VAO to be bound.
gl3.glGenVertexArrays(intBuf16); gl3.glGenVertexArrays(intBuf16);
@ -609,13 +617,12 @@ public class GLRenderer implements Renderer {
if (state.isDepthTest() && !context.depthTestEnabled) { if (state.isDepthTest() && !context.depthTestEnabled) {
gl.glEnable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_DEPTH_TEST);
gl.glDepthFunc(convertTestFunction(context.depthFunc));
context.depthTestEnabled = true; context.depthTestEnabled = true;
} else if (!state.isDepthTest() && context.depthTestEnabled) { } else if (!state.isDepthTest() && context.depthTestEnabled) {
gl.glDisable(GL.GL_DEPTH_TEST); gl.glDisable(GL.GL_DEPTH_TEST);
context.depthTestEnabled = false; context.depthTestEnabled = false;
} }
if (state.getDepthFunc() != context.depthFunc) { if (state.isDepthTest() && state.getDepthFunc() != context.depthFunc) {
gl.glDepthFunc(convertTestFunction(state.getDepthFunc())); gl.glDepthFunc(convertTestFunction(state.getDepthFunc()));
context.depthFunc = state.getDepthFunc(); context.depthFunc = state.getDepthFunc();
} }
@ -1428,7 +1435,7 @@ public class GLRenderer implements Renderer {
// NOTE: For depth textures, sets nearest/no-mips mode // NOTE: For depth textures, sets nearest/no-mips mode
// Required to fix "framebuffer unsupported" // Required to fix "framebuffer unsupported"
// for old NVIDIA drivers! // for old NVIDIA drivers!
setupTextureParams(tex); setupTextureParams(0, tex);
} }
glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT, glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT,
@ -1456,24 +1463,43 @@ public class GLRenderer implements Renderer {
} }
} }
private void bindFrameBuffer(FrameBuffer fb) {
if (fb == null) {
if (context.boundFBO != 0) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
statistics.onFrameBufferUse(null, true);
context.boundFBO = 0;
context.boundFB = null;
}
} else {
assert fb.getId() != -1 && fb.getId() != 0;
if (context.boundFBO != fb.getId()) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId());
context.boundFBO = fb.getId();
context.boundFB = fb;
statistics.onFrameBufferUse(fb, true);
} else {
statistics.onFrameBufferUse(fb, false);
}
}
}
public void updateFrameBuffer(FrameBuffer fb) { public void updateFrameBuffer(FrameBuffer fb) {
if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) {
throw new IllegalArgumentException("The framebuffer: " + fb
+ "\nDoesn't have any color/depth buffers");
}
int id = fb.getId(); int id = fb.getId();
if (id == -1) { if (id == -1) {
// create FBO
glfbo.glGenFramebuffersEXT(intBuf1); glfbo.glGenFramebuffersEXT(intBuf1);
id = intBuf1.get(0); id = intBuf1.get(0);
fb.setId(id); fb.setId(id);
objManager.registerObject(fb); objManager.registerObject(fb);
statistics.onNewFrameBuffer(); statistics.onNewFrameBuffer();
} }
if (context.boundFBO != id) { bindFrameBuffer(fb);
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, id);
// binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
context.boundDrawBuf = 0;
context.boundFBO = id;
}
FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer(); FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
if (depthBuf != null) { if (depthBuf != null) {
@ -1485,6 +1511,7 @@ public class GLRenderer implements Renderer {
updateFrameBufferAttachment(fb, colorBuf); updateFrameBufferAttachment(fb, colorBuf);
} }
setReadDrawBuffers(fb);
checkFrameBufferError(); checkFrameBufferError();
fb.clearUpdateNeeded(); fb.clearUpdateNeeded();
@ -1512,93 +1539,45 @@ public class GLRenderer implements Renderer {
} }
public void setMainFrameBufferOverride(FrameBuffer fb) { public void setMainFrameBufferOverride(FrameBuffer fb) {
mainFbOverride = null;
if (context.boundFBO == 0) {
// Main FB is now set to fb, make sure its bound
setFrameBuffer(fb);
}
mainFbOverride = fb; mainFbOverride = fb;
} }
public void setFrameBuffer(FrameBuffer fb) { public void setReadDrawBuffers(FrameBuffer fb) {
if (fb == null && mainFbOverride != null) { if (gl2 == null) {
fb = mainFbOverride; return;
} }
if (context.boundFB == fb) { final int NONE = -2;
if (fb == null || !fb.isUpdateNeeded()) { final int INITIAL = -1;
return; final int MRT_OFF = 100;
}
}
if (!caps.contains(Caps.FrameBuffer)) {
throw new RendererException("Framebuffer objects are not supported"
+ " by the video hardware");
}
// generate mipmaps for last FB if needed
if (context.boundFB != null) {
for (int i = 0; i < context.boundFB.getNumColorBuffers(); i++) {
RenderBuffer rb = context.boundFB.getColorBuffer(i);
Texture tex = rb.getTexture();
if (tex != null
&& tex.getMinFilter().usesMipMapLevels()) {
setTexture(0, rb.getTexture());
int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace());
glfbo.glGenerateMipmapEXT(textureType);
}
}
}
if (fb == null) { if (fb == null) {
// unbind any fbos // Set Read/Draw buffers to initial value.
if (context.boundFBO != 0) { if (context.boundDrawBuf != INITIAL) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0); gl2.glDrawBuffer(context.initialDrawBuf);
statistics.onFrameBufferUse(null, true); context.boundDrawBuf = INITIAL;
context.boundFBO = 0;
} }
// select back buffer if (context.boundReadBuf != INITIAL) {
if (gl2 != null) { gl2.glReadBuffer(context.initialReadBuf);
if (context.boundDrawBuf != -1) { context.boundReadBuf = INITIAL;
gl2.glDrawBuffer(context.initialDrawBuf);
context.boundDrawBuf = -1;
}
if (context.boundReadBuf != -1) {
gl2.glReadBuffer(context.initialReadBuf);
context.boundReadBuf = -1;
}
} }
context.boundFB = null;
} else { } else {
if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) {
throw new IllegalArgumentException("The framebuffer: " + fb
+ "\nDoesn't have any color/depth buffers");
}
if (fb.isUpdateNeeded()) {
updateFrameBuffer(fb);
}
// update viewport to reflect framebuffer's resolution
setViewPort(0, 0, fb.getWidth(), fb.getHeight());
if (context.boundFBO != fb.getId()) {
glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId());
statistics.onFrameBufferUse(fb, true);
context.boundFBO = fb.getId();
} else {
statistics.onFrameBufferUse(fb, false);
}
if (fb.getNumColorBuffers() == 0) { if (fb.getNumColorBuffers() == 0) {
// make sure to select NONE as draw buf // make sure to select NONE as draw buf
// no color buffer attached. select NONE // no color buffer attached.
if (gl2 != null) { if (gl2 != null) {
if (context.boundDrawBuf != -2) { if (context.boundDrawBuf != NONE) {
gl2.glDrawBuffer(GL.GL_NONE); gl2.glDrawBuffer(GL.GL_NONE);
context.boundDrawBuf = -2; context.boundDrawBuf = NONE;
} }
if (context.boundReadBuf != -2) { if (context.boundReadBuf != NONE) {
gl2.glReadBuffer(GL.GL_NONE); gl2.glReadBuffer(GL.GL_NONE);
context.boundReadBuf = -2; context.boundReadBuf = NONE;
} }
} }
} else { } else {
@ -1618,7 +1597,7 @@ public class GLRenderer implements Renderer {
+ " by the video hardware!"); + " by the video hardware!");
} }
if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) { if (context.boundDrawBuf != MRT_OFF + fb.getNumColorBuffers()) {
intBuf16.clear(); intBuf16.clear();
for (int i = 0; i < fb.getNumColorBuffers(); i++) { for (int i = 0; i < fb.getNumColorBuffers(); i++) {
intBuf16.put(GLFbo.GL_COLOR_ATTACHMENT0_EXT + i); intBuf16.put(GLFbo.GL_COLOR_ATTACHMENT0_EXT + i);
@ -1626,7 +1605,7 @@ public class GLRenderer implements Renderer {
intBuf16.flip(); intBuf16.flip();
glext.glDrawBuffers(intBuf16); glext.glDrawBuffers(intBuf16);
context.boundDrawBuf = 100 + fb.getNumColorBuffers(); context.boundDrawBuf = MRT_OFF + fb.getNumColorBuffers();
} }
} else { } else {
RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex()); RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex());
@ -1639,8 +1618,56 @@ public class GLRenderer implements Renderer {
} }
} }
} }
}
}
assert fb.getId() >= 0; public void setFrameBuffer(FrameBuffer fb) {
if (fb == null && mainFbOverride != null) {
fb = mainFbOverride;
}
if (context.boundFB == fb) {
if (fb == null || !fb.isUpdateNeeded()) {
return;
}
}
if (!caps.contains(Caps.FrameBuffer)) {
throw new RendererException("Framebuffer objects are not supported"
+ " by the video hardware");
}
// generate mipmaps for last FB if needed
if (context.boundFB != null) {
for (int i = 0; i < context.boundFB.getNumColorBuffers(); i++) {
RenderBuffer rb = context.boundFB.getColorBuffer(i);
Texture tex = rb.getTexture();
if (tex != null
&& tex.getMinFilter().usesMipMapLevels()) {
setTexture(0, rb.getTexture());
int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace());
glfbo.glGenerateMipmapEXT(textureType);
}
}
}
if (fb == null) {
bindFrameBuffer(null);
setReadDrawBuffers(null);
} else {
if (fb.isUpdateNeeded()) {
updateFrameBuffer(fb);
} else {
bindFrameBuffer(fb);
setReadDrawBuffers(fb);
}
// update viewport to reflect framebuffer's resolution
setViewPort(0, 0, fb.getWidth(), fb.getHeight());
assert fb.getId() > 0;
assert context.boundFBO == fb.getId(); assert context.boundFBO == fb.getId();
context.boundFB = fb; context.boundFB = fb;
@ -1812,87 +1839,80 @@ public class GLRenderer implements Renderer {
} }
@SuppressWarnings("fallthrough") @SuppressWarnings("fallthrough")
private void setupTextureParams(Texture tex) { private void setupTextureParams(int unit, Texture tex) {
Image image = tex.getImage(); Image image = tex.getImage();
int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1); int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1);
boolean haveMips = true; boolean haveMips = true;
if (image != null) { if (image != null) {
haveMips = image.isGeneratedMipmapsRequired() || image.hasMipmaps(); haveMips = image.isGeneratedMipmapsRequired() || image.hasMipmaps();
} }
// filter things LastTextureState curState = image.getLastTextureState();
if (image.getLastTextureState().magFilter != tex.getMagFilter()) {
int magFilter = convertMagFilter(tex.getMagFilter()); if (curState.magFilter != tex.getMagFilter()) {
gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter); bindTextureAndUnit(target, image, unit);
image.getLastTextureState().magFilter = tex.getMagFilter(); gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, convertMagFilter(tex.getMagFilter()));
} curState.magFilter = tex.getMagFilter();
if (image.getLastTextureState().minFilter != tex.getMinFilter()) {
int minFilter = convertMinFilter(tex.getMinFilter(), haveMips);
gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter);
image.getLastTextureState().minFilter = tex.getMinFilter();
} }
if (caps.contains(Caps.SeamlessCubemap) && tex.getType() == Texture.Type.CubeMap) { if (curState.minFilter != tex.getMinFilter()) {
if (haveMips && !context.seamlessCubemap) { bindTextureAndUnit(target, image, unit);
// We can enable seamless cubemap filtering. gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, convertMinFilter(tex.getMinFilter(), haveMips));
gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS); curState.minFilter = tex.getMinFilter();
context.seamlessCubemap = true;
} else if (!haveMips && context.seamlessCubemap) {
// For skyboxes (no mipmaps), disable seamless cubemap filtering.
gl.glDisable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
context.seamlessCubemap = false;
}
} }
if (caps.contains(Caps.TextureFilterAnisotropic)
if (tex.getAnisotropicFilter() > 1) { && curState.anisoFilter != tex.getAnisotropicFilter()) {
if (caps.contains(Caps.TextureFilterAnisotropic)) { bindTextureAndUnit(target, image, unit);
gl.glTexParameterf(target, gl.glTexParameterf(target,
GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT, GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT,
tex.getAnisotropicFilter()); tex.getAnisotropicFilter());
} curState.anisoFilter = tex.getAnisotropicFilter();
} }
// repeat modes
switch (tex.getType()) { switch (tex.getType()) {
case ThreeDimensional: case ThreeDimensional:
case CubeMap: // cubemaps use 3D coords case CubeMap: // cubemaps use 3D coords
if (gl2 != null && image.getLastTextureState().rWrap != tex.getWrap(WrapAxis.R)) { if (gl2 != null && curState.rWrap != tex.getWrap(WrapAxis.R)) {
bindTextureAndUnit(target, image, unit);
gl2.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); gl2.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
image.getLastTextureState().rWrap = tex.getWrap(WrapAxis.R); curState.rWrap = tex.getWrap(WrapAxis.R);
} }
//There is no break statement on purpose here //There is no break statement on purpose here
case TwoDimensional: case TwoDimensional:
case TwoDimensionalArray: case TwoDimensionalArray:
if (image.getLastTextureState().tWrap != tex.getWrap(WrapAxis.T)) { if (curState.tWrap != tex.getWrap(WrapAxis.T)) {
bindTextureAndUnit(target, image, unit);
gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
image.getLastTextureState().tWrap = tex.getWrap(WrapAxis.T); image.getLastTextureState().tWrap = tex.getWrap(WrapAxis.T);
} }
if (image.getLastTextureState().sWrap != tex.getWrap(WrapAxis.S)) { if (curState.sWrap != tex.getWrap(WrapAxis.S)) {
bindTextureAndUnit(target, image, unit);
gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
image.getLastTextureState().sWrap = tex.getWrap(WrapAxis.S); curState.sWrap = tex.getWrap(WrapAxis.S);
} }
break; break;
default: default:
throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
} }
if(tex.isNeedCompareModeUpdate() && gl2 != null){ ShadowCompareMode texCompareMode = tex.getShadowCompareMode();
// R to Texture compare mode if (gl2 != null && curState.shadowCompareMode != texCompareMode) {
if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) { bindTextureAndUnit(target, image, unit);
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE); if (texCompareMode != ShadowCompareMode.Off) {
gl2.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_REF_TO_TEXTURE);
if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) { if (texCompareMode == ShadowCompareMode.GreaterOrEqual) {
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL);
} else { } else {
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL);
} }
}else{ } else {
//restoring default value
gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE);
} }
tex.compareModeUpdated(); curState.shadowCompareMode = texCompareMode;
} }
// If at this point we didn't bind the texture, bind it now
bindTextureOnly(target, image, unit);
} }
/** /**
@ -1950,6 +1970,50 @@ public class GLRenderer implements Renderer {
} }
} }
/**
* Ensures that the texture is bound to the given unit
* and that the unit is currently active (for modification).
*
* @param target The texture target, one of GL_TEXTURE_***
* @param img The image texture to bind
* @param unit At what unit to bind the texture.
*/
private void bindTextureAndUnit(int target, Image img, int unit) {
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
if (context.boundTextures[unit] != img) {
gl.glBindTexture(target, img.getId());
context.boundTextures[unit] = img;
statistics.onTextureUse(img, true);
} else {
statistics.onTextureUse(img, false);
}
}
/**
* Ensures that the texture is bound to the given unit,
* but does not care if the unit is active (for rendering).
*
* @param target The texture target, one of GL_TEXTURE_***
* @param img The image texture to bind
* @param unit At what unit to bind the texture.
*/
private void bindTextureOnly(int target, Image img, int unit) {
if (context.boundTextures[unit] != img) {
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
gl.glBindTexture(target, img.getId());
context.boundTextures[unit] = img;
statistics.onTextureUse(img, true);
} else {
statistics.onTextureUse(img, false);
}
}
/** /**
* Uploads the given image to the GL driver. * Uploads the given image to the GL driver.
* *
@ -1973,17 +2037,7 @@ public class GLRenderer implements Renderer {
// bind texture // bind texture
int target = convertTextureType(type, img.getMultiSamples(), -1); int target = convertTextureType(type, img.getMultiSamples(), -1);
if (context.boundTextures[unit] != img) { bindTextureAndUnit(target, img, unit);
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
gl.glBindTexture(target, texId);
context.boundTextures[unit] = img;
statistics.onTextureUse(img, true);
}
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
// Image does not have mipmaps, but they are required. // Image does not have mipmaps, but they are required.
@ -2092,6 +2146,7 @@ public class GLRenderer implements Renderer {
img.clearUpdateNeeded(); img.clearUpdateNeeded();
} }
@Override
public void setTexture(int unit, Texture tex) { public void setTexture(int unit, Texture tex) {
Image image = tex.getImage(); Image image = tex.getImage();
if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) {
@ -2118,24 +2173,7 @@ public class GLRenderer implements Renderer {
int texId = image.getId(); int texId = image.getId();
assert texId != -1; assert texId != -1;
Image[] textures = context.boundTextures; setupTextureParams(unit, tex);
int type = convertTextureType(tex.getType(), image.getMultiSamples(), -1);
if (textures[unit] != image) {
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
gl.glBindTexture(type, texId);
textures[unit] = image;
statistics.onTextureUse(image, true);
} else {
statistics.onTextureUse(image, false);
}
setupTextureParams(tex);
} }
public void modifyTexture(Texture tex, Image pixels, int x, int y) { public void modifyTexture(Texture tex, Image pixels, int x, int y) {
@ -2629,12 +2667,13 @@ public class GLRenderer implements Renderer {
} }
} }
clearVertexAttribs();
if (indices != null) { if (indices != null) {
drawTriangleList(indices, mesh, count); drawTriangleList(indices, mesh, count);
} else { } else {
drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
} }
clearVertexAttribs();
} }
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {

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

@ -99,6 +99,7 @@ public class Grid extends Mesh {
updateBound(); updateBound();
updateCounts(); updateCounts();
setStatic();
} }
} }

@ -35,6 +35,7 @@ import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.util.BufferUtils; import com.jme3.util.BufferUtils;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
@ -62,6 +63,7 @@ public class WireFrustum extends Mesh {
3, 7, 3, 7,
} }
); );
getBuffer(Type.Index).setUsage(Usage.Static);
setMode(Mode.Lines); setMode(Mode.Lines);
} }

@ -389,6 +389,7 @@ public class Cylinder extends Mesh {
} }
updateBound(); updateBound();
setStatic();
} }
@Override @Override

@ -124,6 +124,7 @@ public class Quad extends Mesh {
} }
updateBound(); updateBound();
setStatic();
} }

@ -299,7 +299,6 @@ public class Sphere extends Mesh {
} }
updateBound(); updateBound();
setStatic();
} }
/** /**
@ -400,6 +399,7 @@ public class Sphere extends Mesh {
this.interior = interior; this.interior = interior;
setGeometryData(); setGeometryData();
setIndexData(); setIndexData();
setStatic();
} }
public void read(JmeImporter e) throws IOException { public void read(JmeImporter e) throws IOException {

@ -111,6 +111,33 @@ public final class AppSettings extends HashMap<String, Object> {
*/ */
public static final String ANDROID_OPENAL_SOFT = "OpenAL_SOFT"; public static final String ANDROID_OPENAL_SOFT = "OpenAL_SOFT";
/**
* Use JogAmp's JOGL as the display system, with the OpenGL forward compatible profile
* <p>
* N.B: This backend is EXPERIMENTAL
*
* @see AppSettings#setRenderer(java.lang.String)
*/
public static final String JOGL_OPENGL_FORWARD_COMPATIBLE = "JOGL_OPENGL_FORWARD_COMPATIBLE";
/**
* Use JogAmp's JOGL as the display system with the backward compatible profile
* <p>
* N.B: This backend is EXPERIMENTAL
*
* @see AppSettings#setRenderer(java.lang.String)
*/
public static final String JOGL_OPENGL_BACKWARD_COMPATIBLE = "JOGL_OPENGL_BACKWARD_COMPATIBLE";
/**
* Use JogAmp's JOAL as the display system
* <p>
* N.B: This backend is EXPERIMENTAL
*
* @see AppSettings#setRenderer(java.lang.String)
*/
public static final String JOAL = "JOAL";
static { static {
defaults.put("Width", 640); defaults.put("Width", 640);
defaults.put("Height", 480); defaults.put("Height", 480);
@ -120,7 +147,7 @@ public final class AppSettings extends HashMap<String, Object> {
defaults.put("StencilBits", 0); defaults.put("StencilBits", 0);
defaults.put("Samples", 0); defaults.put("Samples", 0);
defaults.put("Fullscreen", false); defaults.put("Fullscreen", false);
defaults.put("Title", "jMonkey Engine 3.0"); defaults.put("Title", JmeVersion.FULL_NAME);
defaults.put("Renderer", LWJGL_OPENGL2); defaults.put("Renderer", LWJGL_OPENGL2);
defaults.put("AudioRenderer", LWJGL_OPENAL); defaults.put("AudioRenderer", LWJGL_OPENAL);
defaults.put("DisableJoysticks", true); defaults.put("DisableJoysticks", true);

@ -316,7 +316,6 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps; private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps;
private MagFilter magnificationFilter = MagFilter.Bilinear; private MagFilter magnificationFilter = MagFilter.Bilinear;
private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off; private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off;
private boolean needCompareModeUpdate = false;
private int anisotropicFilter; private int anisotropicFilter;
/** /**
@ -404,7 +403,6 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
"compareMode can not be null."); "compareMode can not be null.");
} }
this.shadowCompareMode = compareMode; this.shadowCompareMode = compareMode;
needCompareModeUpdate = true;
} }
/** /**
@ -489,7 +487,7 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
/** /**
* @return the anisotropic filtering level for this texture. Default value * @return the anisotropic filtering level for this texture. Default value
* is 0 (use value from config), * is 0 (use value from config),
* 1 means 1x (no anisotrophy), 2 means x2, 4 is x4, etc. * 1 means 1x (no anisotropy), 2 means x2, 4 is x4, etc.
*/ */
public int getAnisotropicFilter() { public int getAnisotropicFilter() {
return anisotropicFilter; return anisotropicFilter;
@ -636,14 +634,4 @@ public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable
magnificationFilter = capsule.readEnum("magnificationFilter", magnificationFilter = capsule.readEnum("magnificationFilter",
MagFilter.class, MagFilter.Bilinear); MagFilter.class, MagFilter.Bilinear);
} }
public boolean isNeedCompareModeUpdate() {
return needCompareModeUpdate;
}
public void compareModeUpdated() {
this.needCompareModeUpdate = false;
}
} }

@ -45,9 +45,11 @@ public final class LastTextureState {
public Texture.WrapMode sWrap, tWrap, rWrap; public Texture.WrapMode sWrap, tWrap, rWrap;
public Texture.MagFilter magFilter; public Texture.MagFilter magFilter;
public Texture.MinFilter minFilter; public Texture.MinFilter minFilter;
public int anisoFilter;
public Texture.ShadowCompareMode shadowCompareMode;
public LastTextureState() { public LastTextureState() {
// All parameters initialized to null (meaning unset). reset();
} }
public void reset() { public void reset() {
@ -56,5 +58,10 @@ public final class LastTextureState {
rWrap = null; rWrap = null;
magFilter = null; magFilter = null;
minFilter = null; minFilter = null;
anisoFilter = 0;
// The default in OpenGL is OFF, so we avoid setting this per texture
// if its not used.
shadowCompareMode = Texture.ShadowCompareMode.Off;
} }
} }

@ -43,21 +43,6 @@ import java.util.Map.Entry;
*/ */
public final class ListMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable { public final class ListMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable {
public static void main(String[] args){
Map<String, String> map = new ListMap<String, String>();
map.put( "bob", "hello");
System.out.println(map.get("bob"));
map.remove("bob");
System.out.println(map.size());
map.put("abc", "1");
map.put("def", "2");
map.put("ghi", "3");
map.put("jkl", "4");
map.put("mno", "5");
System.out.println(map.get("ghi"));
}
private final static class ListMapEntry<K, V> implements Map.Entry<K, V>, Cloneable { private final static class ListMapEntry<K, V> implements Map.Entry<K, V>, Cloneable {
private final K key; private final K key;

@ -154,8 +154,9 @@ void main(){
#ifdef VERTEX_LIGHTING #ifdef VERTEX_LIGHTING
vec2 light = vertexLightValues.xy; vec2 light = vertexLightValues.xy;
#ifdef COLORRAMP #ifdef COLORRAMP
light.x = texture2D(m_ColorRamp, vec2(light.x, 0.0)).r; diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
light.y = texture2D(m_ColorRamp, vec2(light.y, 0.0)).r; specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
light.xy = vec2(1.0);
#endif #endif
gl_FragColor.rgb = AmbientSum * diffuseColor.rgb + gl_FragColor.rgb = AmbientSum * diffuseColor.rgb +
@ -183,8 +184,9 @@ void main(){
vec2 light = computeLighting(normal, viewDir, lightDir.xyz, lightDir.w * spotFallOff, m_Shininess) ; vec2 light = computeLighting(normal, viewDir, lightDir.xyz, lightDir.w * spotFallOff, m_Shininess) ;
#ifdef COLORRAMP #ifdef COLORRAMP
diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb; diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb; specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
light.xy = vec2(1.0);
#endif #endif
// Workaround, since it is not possible to modify varying variables // Workaround, since it is not possible to modify varying variables

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

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

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

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

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

@ -63,3 +63,9 @@ Xbox\ 360\ Wireless\ Receiver.AXIS_RX=z
Xbox\ 360\ Wireless\ Receiver.AXIS_RY=rz Xbox\ 360\ Wireless\ Receiver.AXIS_RY=rz
Xbox\ 360\ Wireless\ Receiver.z=AXIS_RX Xbox\ 360\ Wireless\ Receiver.z=AXIS_RX
Xbox\ 360\ Wireless\ Receiver.rz=AXIS_RY Xbox\ 360\ Wireless\ Receiver.rz=AXIS_RY
# Microsoft PC-joystick driver
Microsoft\ PC-joystick\ driver.12=POV +Y
Microsoft\ PC-joystick\ driver.13=POV +X
Microsoft\ PC-joystick\ driver.14=POV -Y
Microsoft\ PC-joystick\ driver.15=POV -X

@ -43,7 +43,6 @@ import com.jme3.math.Vector3f;
import com.jme3.shader.Shader; import com.jme3.shader.Shader;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.texture.Texture2D; import com.jme3.texture.Texture2D;
import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ColorSpace;
import com.jme3.util.PlaceholderAssets; import com.jme3.util.PlaceholderAssets;
@ -52,10 +51,13 @@ import com.jme3.util.blockparser.Statement;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class J3MLoader implements AssetLoader { public class J3MLoader implements AssetLoader {
@ -137,59 +139,146 @@ public class J3MLoader implements AssetLoader {
technique.setShadowMode(sm); technique.setShadowMode(sm);
} }
private Object readValue(VarType type, String value) throws IOException{ private List<String> tokenizeTextureValue(final String value) {
if (type.isTextureType()){ final List<String> matchList = new ArrayList<String>();
// String texturePath = readString("[\n;(//)(\\})]"); final Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
String texturePath = value.trim(); final Matcher regexMatcher = regex.matcher(value.trim());
boolean flipY = false;
boolean repeat = false; while (regexMatcher.find()) {
if (texturePath.startsWith("Flip Repeat ")){ if (regexMatcher.group(1) != null) {
texturePath = texturePath.substring(12).trim(); matchList.add(regexMatcher.group(1));
flipY = true; } else if (regexMatcher.group(2) != null) {
repeat = true; matchList.add(regexMatcher.group(2));
}else if (texturePath.startsWith("Flip ")){ } else {
texturePath = texturePath.substring(5).trim(); matchList.add(regexMatcher.group());
flipY = true;
}else if (texturePath.startsWith("Repeat ")){
texturePath = texturePath.substring(7).trim();
repeat = true;
} }
}
TextureKey texKey = new TextureKey(texturePath, flipY); return matchList;
switch (type) { }
case Texture3D:
texKey.setTextureTypeHint(Texture.Type.ThreeDimensional); private List<TextureOptionValue> parseTextureOptions(final List<String> values) {
break; final List<TextureOptionValue> matchList = new ArrayList<TextureOptionValue>();
case TextureArray:
texKey.setTextureTypeHint(Texture.Type.TwoDimensionalArray); if (values.isEmpty() || values.size() == 1) {
break; return matchList;
case TextureCubeMap: }
texKey.setTextureTypeHint(Texture.Type.CubeMap);
break; // Loop through all but the last value, the last one is going to be the path.
} for (int i = 0; i < values.size() - 1; i++) {
texKey.setGenerateMips(true); final String value = values.get(i);
final TextureOption textureOption = TextureOption.getTextureOption(value);
Texture tex;
try { if (textureOption == null && !value.contains("\\") && !value.contains("/") && !values.get(0).equals("Flip") && !values.get(0).equals("Repeat")) {
tex = assetManager.loadTexture(texKey); logger.log(Level.WARNING, "Unknown texture option \"{0}\" encountered for \"{1}\" in material \"{2}\"", new Object[]{value, key, material.getKey().getName()});
} catch (AssetNotFoundException ex){ } else if (textureOption != null){
logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, key}); final String option = textureOption.getOptionValue(value);
tex = null; matchList.add(new TextureOptionValue(textureOption, option));
} }
if (tex != null){ }
if (repeat){
tex.setWrap(WrapMode.Repeat); return matchList;
}
private boolean isTexturePathDeclaredTheTraditionalWay(final int numberOfValues, final int numberOfTextureOptions, final String texturePath) {
return (numberOfValues > 1 && (texturePath.startsWith("Flip Repeat ") || texturePath.startsWith("Flip ") ||
texturePath.startsWith("Repeat ") || texturePath.startsWith("Repeat Flip "))) || numberOfTextureOptions == 0;
}
private Texture parseTextureType(final VarType type, final String value) {
final List<String> textureValues = tokenizeTextureValue(value);
final List<TextureOptionValue> textureOptionValues = parseTextureOptions(textureValues);
TextureKey textureKey = null;
// If there is only one token on the value, it must be the path to the texture.
if (textureValues.size() == 1) {
textureKey = new TextureKey(textureValues.get(0), false);
} else {
String texturePath = value.trim();
// If there are no valid "new" texture options specified but the path is split into several parts, lets parse the old way.
if (isTexturePathDeclaredTheTraditionalWay(textureValues.size(), textureOptionValues.size(), texturePath)) {
boolean flipY = false;
if (texturePath.startsWith("Flip Repeat ") || texturePath.startsWith("Repeat Flip ")) {
texturePath = texturePath.substring(12).trim();
flipY = true;
} else if (texturePath.startsWith("Flip ")) {
texturePath = texturePath.substring(5).trim();
flipY = true;
} else if (texturePath.startsWith("Repeat ")) {
texturePath = texturePath.substring(7).trim();
} }
}else{
tex = new Texture2D(PlaceholderAssets.getPlaceholderImage(assetManager)); // Support path starting with quotes (double and single)
if (repeat){ if (texturePath.startsWith("\"") || texturePath.startsWith("'")) {
tex.setWrap(WrapMode.Repeat); texturePath = texturePath.substring(1);
}
// Support path ending with quotes (double and single)
if (texturePath.endsWith("\"") || texturePath.endsWith("'")) {
texturePath = texturePath.substring(0, texturePath.length() - 1);
} }
tex.setKey(texKey);
tex.setName(texKey.getName()); textureKey = new TextureKey(texturePath, flipY);
} }
return tex;
}else{ if (textureKey == null) {
textureKey = new TextureKey(textureValues.get(textureValues.size() - 1), false);
}
// Apply texture options to the texture key
if (!textureOptionValues.isEmpty()) {
for (final TextureOptionValue textureOptionValue : textureOptionValues) {
textureOptionValue.applyToTextureKey(textureKey);
}
}
}
switch (type) {
case Texture3D:
textureKey.setTextureTypeHint(Texture.Type.ThreeDimensional);
break;
case TextureArray:
textureKey.setTextureTypeHint(Texture.Type.TwoDimensionalArray);
break;
case TextureCubeMap:
textureKey.setTextureTypeHint(Texture.Type.CubeMap);
break;
}
textureKey.setGenerateMips(true);
Texture texture;
try {
texture = assetManager.loadTexture(textureKey);
} catch (AssetNotFoundException ex){
logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{textureKey, key});
texture = null;
}
if (texture == null){
texture = new Texture2D(PlaceholderAssets.getPlaceholderImage(assetManager));
texture.setKey(textureKey);
texture.setName(textureKey.getName());
}
// Apply texture options to the texture
if (!textureOptionValues.isEmpty()) {
for (final TextureOptionValue textureOptionValue : textureOptionValues) {
textureOptionValue.applyToTexture(texture);
}
}
return texture;
}
private Object readValue(final VarType type, final String value) throws IOException{
if (type.isTextureType()) {
return parseTextureType(type, value);
} else {
String[] split = value.trim().split(whitespacePattern); String[] split = value.trim().split(whitespacePattern);
switch (type){ switch (type){
case Float: case Float:
@ -632,4 +721,125 @@ public class J3MLoader implements AssetLoader {
} }
} }
/**
* Texture options allow you to specify how a texture should be initialized by including an option before
* the path to the texture in the .j3m file.
* <p>
* <b>Example:</b>
* <pre>
* DiffuseMap: MinTrilinear MagBilinear WrapRepeat_S "some/path/to a/texture.png"
* </pre>
* This would apply a minification filter of "Trilinear", a magnification filter of "Bilinear" and set the wrap mode to "Repeat".
* </p>
* <p>
* <b>Note:</b> If several filters of the same type are added, eg. MinTrilinear MinNearestLinearMipMap, the last one will win.
* </p>
*/
private enum TextureOption {
/**
* Applies a {@link com.jme3.texture.Texture.MinFilter} to the texture.
*/
Min {
@Override
public void applyToTexture(final String option, final Texture texture) {
texture.setMinFilter(Texture.MinFilter.valueOf(option));
}
},
/**
* Applies a {@link com.jme3.texture.Texture.MagFilter} to the texture.
*/
Mag {
@Override
public void applyToTexture(final String option, final Texture texture) {
texture.setMagFilter(Texture.MagFilter.valueOf(option));
}
},
/**
* Applies a {@link com.jme3.texture.Texture.WrapMode} to the texture. This also supports {@link com.jme3.texture.Texture.WrapAxis}
* by adding "_AXIS" to the texture option. For instance if you wanted to repeat on the S (horizontal) axis, you
* would use <pre>WrapRepeat_S</pre> as a texture option.
*/
Wrap {
@Override
public void applyToTexture(final String option, final Texture texture) {
final int separatorPosition = option.indexOf("_");
if (separatorPosition >= option.length() - 2) {
final String axis = option.substring(separatorPosition + 1);
final String mode = option.substring(0, separatorPosition);
final Texture.WrapAxis wrapAxis = Texture.WrapAxis.valueOf(axis);
texture.setWrap(wrapAxis, Texture.WrapMode.valueOf(mode));
} else {
texture.setWrap(Texture.WrapMode.valueOf(option));
}
}
},
/**
* Applies a {@link com.jme3.texture.Texture.WrapMode#Repeat} to the texture. This is simply an alias for
* WrapRepeat, please use WrapRepeat instead if possible as this may become deprecated later on.
*/
Repeat {
@Override
public void applyToTexture(final String option, final Texture texture) {
Wrap.applyToTexture("Repeat", texture);
}
},
/**
* Applies flipping on the Y axis to the {@link TextureKey#setFlipY(boolean)}.
*/
Flip {
@Override
public void applyToTextureKey(final String option, final TextureKey textureKey) {
textureKey.setFlipY(true);
}
};
public String getOptionValue(final String option) {
return option.substring(name().length());
}
public void applyToTexture(final String option, final Texture texture) {
}
public void applyToTextureKey(final String option, final TextureKey textureKey) {
}
public static TextureOption getTextureOption(final String option) {
for(final TextureOption textureOption : TextureOption.values()) {
if (option.startsWith(textureOption.name())) {
return textureOption;
}
}
return null;
}
}
/**
* Internal object used for holding a {@link com.jme3.material.plugins.J3MLoader.TextureOption} and it's value. Also
* contains a couple of convenience methods for applying the TextureOption to either a TextureKey or a Texture.
*/
private static class TextureOptionValue {
private final TextureOption textureOption;
private final String value;
public TextureOptionValue(TextureOption textureOption, String value) {
this.textureOption = textureOption;
this.value = value;
}
public void applyToTextureKey(final TextureKey textureKey) {
textureOption.applyToTextureKey(value, textureKey);
}
public void applyToTexture(final Texture texture) {
textureOption.applyToTexture(value, texture);
}
}
} }

@ -0,0 +1,203 @@
/*
* Copyright (c) 2009-2015 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.collision;
import static com.jme3.collision.CollisionUtil.*;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.math.FastMath;
import com.jme3.math.Ray;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import org.junit.Test;
/**
* Tests collision detection between bounding volumes.
*
* @author Kirill Vainer
*/
public class BoundingCollisionTest {
@Test
public void testBoxBoxCollision() {
BoundingBox box1 = new BoundingBox(Vector3f.ZERO, 1, 1, 1);
BoundingBox box2 = new BoundingBox(Vector3f.ZERO, 1, 1, 1);
checkCollision(box1, box2, 1);
// Put it at the very edge - should still intersect.
box2.setCenter(new Vector3f(2f, 0f, 0f));
checkCollision(box1, box2, 1);
// Put it a wee bit farther - no intersection expected
box2.setCenter(new Vector3f(2f + FastMath.ZERO_TOLERANCE, 0, 0));
checkCollision(box1, box2, 0);
// Check the corners.
box2.setCenter(new Vector3f(2f, 2f, 2f));
checkCollision(box1, box2, 1);
box2.setCenter(new Vector3f(2f, 2f, 2f + FastMath.ZERO_TOLERANCE));
checkCollision(box1, box2, 0);
}
@Test
public void testSphereSphereCollision() {
BoundingSphere sphere1 = new BoundingSphere(1, Vector3f.ZERO);
BoundingSphere sphere2 = new BoundingSphere(1, Vector3f.ZERO);
checkCollision(sphere1, sphere2, 1);
// Put it at the very edge - should still intersect.
sphere2.setCenter(new Vector3f(2f, 0f, 0f));
checkCollision(sphere1, sphere2, 1);
// Put it a wee bit farther - no intersection expected
sphere2.setCenter(new Vector3f(2f + FastMath.ZERO_TOLERANCE, 0, 0));
checkCollision(sphere1, sphere2, 0);
}
@Test
public void testBoxSphereCollision() {
BoundingBox box1 = new BoundingBox(Vector3f.ZERO, 1, 1, 1);
BoundingSphere sphere2 = new BoundingSphere(1, Vector3f.ZERO);
checkCollision(box1, sphere2, 1);
// Put it at the very edge - for sphere vs. box, it will not intersect
sphere2.setCenter(new Vector3f(2f, 0f, 0f));
checkCollision(box1, sphere2, 0);
// Put it a wee bit closer - should intersect.
sphere2.setCenter(new Vector3f(2f - FastMath.ZERO_TOLERANCE, 0, 0));
checkCollision(box1, sphere2, 1);
// Test if the algorithm converts the sphere
// to a box before testing the collision (incorrect)
float sqrt3 = FastMath.sqrt(3);
sphere2.setCenter(Vector3f.UNIT_XYZ.mult(2));
sphere2.setRadius(sqrt3);
checkCollision(box1, sphere2, 0);
// Make it a wee bit larger.
sphere2.setRadius(sqrt3 + FastMath.ZERO_TOLERANCE);
checkCollision(box1, sphere2, 1);
}
@Test
public void testBoxRayCollision() {
BoundingBox box = new BoundingBox(Vector3f.ZERO, 1, 1, 1);
Ray ray = new Ray(Vector3f.ZERO, Vector3f.UNIT_Z);
// XXX: seems incorrect, ray inside box should only generate
// one result...
checkCollision(box, ray, 2);
ray.setOrigin(new Vector3f(0, 0, -5));
checkCollision(box, ray, 2);
// XXX: is this right? the ray origin is on the box's side..
ray.setOrigin(new Vector3f(0, 0, 2f));
checkCollision(box, ray, 0);
ray.setOrigin(new Vector3f(0, 0, -2f));
checkCollision(box, ray, 2);
// parallel to the edge, touching the side
ray.setOrigin(new Vector3f(0, 1f, -2f));
checkCollision(box, ray, 2);
// still parallel, but not touching the side
ray.setOrigin(new Vector3f(0, 1f + FastMath.ZERO_TOLERANCE, -2f));
checkCollision(box, ray, 0);
}
@Test
public void testBoxTriangleCollision() {
BoundingBox box = new BoundingBox(Vector3f.ZERO, 1, 1, 1);
Geometry geom = new Geometry("geom", new Quad(1, 1));
checkCollision(box, geom, 2); // Both triangles intersect
// The box touches the edges of the triangles.
box.setCenter(new Vector3f(-1f, 0, 0));
checkCollision(box, geom, 2);
// Move it slightly farther..
box.setCenter(new Vector3f(-1f - FastMath.ZERO_TOLERANCE, 0, 0));
checkCollision(box, geom, 0);
// Parallel triangle / box side, touching
box.setCenter(new Vector3f(0, 0, -1f));
checkCollision(box, geom, 2);
// Not touching
box.setCenter(new Vector3f(0, 0, -1f - FastMath.ZERO_TOLERANCE));
checkCollision(box, geom, 0);
// Test collisions only against one of the triangles
box.setCenter(new Vector3f(-1f, 1.5f, 0f));
checkCollision(box, geom, 1);
box.setCenter(new Vector3f(1.5f, -1f, 0f));
checkCollision(box, geom, 1);
}
@Test
public void testSphereTriangleCollision() {
BoundingSphere sphere = new BoundingSphere(1, Vector3f.ZERO);
Geometry geom = new Geometry("geom", new Quad(1, 1));
checkCollision(sphere, geom, 2);
// The box touches the edges of the triangles.
sphere.setCenter(new Vector3f(-1f + FastMath.ZERO_TOLERANCE, 0, 0));
checkCollision(sphere, geom, 2);
// Move it slightly farther..
sphere.setCenter(new Vector3f(-1f - FastMath.ZERO_TOLERANCE, 0, 0));
checkCollision(sphere, geom, 0);
// Parallel triangle / box side, touching
sphere.setCenter(new Vector3f(0, 0, -1f));
checkCollision(sphere, geom, 2);
// Not touching
sphere.setCenter(new Vector3f(0, 0, -1f - FastMath.ZERO_TOLERANCE));
checkCollision(sphere, geom, 0);
// Test collisions only against one of the triangles
sphere.setCenter(new Vector3f(-0.9f, 1.2f, 0f));
checkCollision(sphere, geom, 1);
sphere.setCenter(new Vector3f(1.2f, -0.9f, 0f));
checkCollision(sphere, geom, 1);
}
}

@ -0,0 +1,80 @@
/*
* Copyright (c) 2009-2015 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.collision;
import com.jme3.bounding.BoundingVolume;
/**
* Utilities for testing collision.
*
* @author Kirill Vainer
*/
final class CollisionUtil {
private static void checkCollisionBase(Collidable a, Collidable b, int expected) {
// Test bounding volume methods
if (a instanceof BoundingVolume && b instanceof BoundingVolume) {
BoundingVolume bv1 = (BoundingVolume) a;
BoundingVolume bv2 = (BoundingVolume) b;
assert bv1.intersects(bv2) == (expected != 0);
}
// Test standard collideWith method
CollisionResults results = new CollisionResults();
int numCollisions = a.collideWith(b, results);
assert results.size() == numCollisions;
assert numCollisions == expected;
// force the results to be sorted here..
results.getClosestCollision();
if (results.size() > 0) {
assert results.getCollision(0) == results.getClosestCollision();
}
if (results.size() == 1) {
assert results.getClosestCollision() == results.getFarthestCollision();
}
}
/**
* Tests various collisions between the two collidables and
* the transitive property.
*
* @param a First collidable
* @param b Second collidable
* @param expect Number of expected results
*/
public static void checkCollision(Collidable a, Collidable b, int expected) {
checkCollisionBase(a, b, expected);
checkCollisionBase(b, a, expected);
}
}

@ -0,0 +1,271 @@
/*
* Copyright (c) 2009-2015 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.light;
import com.jme3.bounding.BoundingSphere;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.util.TempVars;
import org.junit.Before;
import org.junit.Test;
/**
* Test light filtering for various light types.
*
* @author Kirill Vainer
*/
public class LightFilterTest {
private DefaultLightFilter filter;
private Camera cam;
private Geometry geom;
private LightList list;
private void checkFilteredLights(int expected) {
geom.updateGeometricState();
filter.setCamera(cam); // setCamera resets the intersection cache
list.clear();
filter.filterLights(geom, list);
assert list.size() == expected;
}
@Before
public void setUp() {
filter = new DefaultLightFilter();
cam = new Camera(512, 512);
cam.setFrustumPerspective(45, 1, 1, 1000);
cam.setLocation(Vector3f.ZERO);
cam.lookAtDirection(Vector3f.UNIT_Z, Vector3f.UNIT_Y);
filter.setCamera(cam);
Box box = new Box(1, 1, 1);
geom = new Geometry("geom", box);
geom.setLocalTranslation(0, 0, 10);
geom.updateGeometricState();
list = new LightList(geom);
}
@Test
public void testAmbientFiltering() {
geom.addLight(new AmbientLight());
checkFilteredLights(1); // Ambient lights must never be filtered
// Test for bounding Sphere
geom.setModelBound(new BoundingSphere(0.5f, Vector3f.ZERO));
checkFilteredLights(1); // Ambient lights must never be filtered
}
@Test
public void testDirectionalFiltering() {
geom.addLight(new DirectionalLight(Vector3f.UNIT_Y));
checkFilteredLights(1); // Directional lights must never be filtered
// Test for bounding Sphere
geom.setModelBound(new BoundingSphere(0.5f, Vector3f.ZERO));
checkFilteredLights(1); // Directional lights must never be filtered
}
@Test
public void testPointFiltering() {
PointLight pl = new PointLight(Vector3f.ZERO);
geom.addLight(pl);
checkFilteredLights(1); // Infinite point lights must never be filtered
// Light at origin does not intersect geom which is at Z=10
pl.setRadius(1);
checkFilteredLights(0);
// Put it closer to geom, the very edge of the sphere touches the box.
// Still not considered an intersection though.
pl.setPosition(new Vector3f(0, 0, 8f));
checkFilteredLights(0);
// And more close - now its an intersection.
pl.setPosition(new Vector3f(0, 0, 8f + FastMath.ZERO_TOLERANCE));
checkFilteredLights(1);
// Move the geometry away
geom.move(0, 0, FastMath.ZERO_TOLERANCE);
checkFilteredLights(0);
// Test if the algorithm converts the sphere
// to a box before testing the collision (incorrect)
float sqrt3 = FastMath.sqrt(3);
pl.setPosition(new Vector3f(2, 2, 8));
pl.setRadius(sqrt3);
checkFilteredLights(0);
// Make it a wee bit larger.
pl.setRadius(sqrt3 + FastMath.ZERO_TOLERANCE);
checkFilteredLights(1);
// Rotate the camera so it is up, light is outside frustum.
cam.lookAtDirection(Vector3f.UNIT_Y, Vector3f.UNIT_Y);
checkFilteredLights(0);
// ==================================
// Tests for bounding Sphere
geom.setModelBound(new BoundingSphere(1f, Vector3f.ZERO));
geom.setLocalTranslation(0, 0, 2);
pl.setPosition(new Vector3f(0, 0, 2f));
// Infinite point lights must never be filtered
pl.setRadius(0);
checkFilteredLights(1);
pl.setRadius(1f);
// Put the light at the very close to the geom,
// the very edge of the sphere touches the other bounding sphere
// Still not considered an intersection though.
pl.setPosition(new Vector3f(0, 0, 0));
checkFilteredLights(0);
// And more close - now its an intersection.
pl.setPosition(new Vector3f(0, 0, 0f + FastMath.ZERO_TOLERANCE));
checkFilteredLights(1);
geom.setLocalTranslation(0, 0, 0);
// In this case its an intersection for pointLight v. box
// But not for pointLight v. sphere
// Vector3f(0, 0.5f, 0.5f).normalize().mult(2) ~ >= (0.0, 1.4142135, 1.4142135)
//pl.setPosition(new Vector3f(0, 0.5f, 0.5f).normalizeLocal().multLocal(2 + FastMath.ZERO_TOLERANCE));
pl.setPosition(new Vector3f(0f, 1.4142135f, 1.4142135f).multLocal(1+FastMath.ZERO_TOLERANCE));
checkFilteredLights(0);
// Make the distance a wee bit closer, now its an intersection
//pl.setPosition(new Vector3f(0, 0.5f, 0.5f).normalizeLocal().multLocal(2 - FastMath.ZERO_TOLERANCE));
pl.setPosition(new Vector3f(0f, 1.4142135f, 1.4142135f).multLocal(1-FastMath.ZERO_TOLERANCE));
checkFilteredLights(1);
// it's a point light, also test for the other corner
pl.setPosition(new Vector3f(0f, -1.4142135f, -1.4142135f).multLocal(1-FastMath.ZERO_TOLERANCE));
checkFilteredLights(0);
}
@Test
public void testSpotFiltering() {
SpotLight sl = new SpotLight(Vector3f.ZERO, Vector3f.UNIT_Z);
sl.setSpotRange(0);
geom.addLight(sl);
checkFilteredLights(1); // Infinite spot lights are only filtered
// if the geometry is outside the infinite cone.
TempVars vars = TempVars.get();
try {
// The spot is not touching the near plane of the camera yet,
// should still be culled.
sl.setSpotRange(1f - FastMath.ZERO_TOLERANCE);
assert !sl.intersectsFrustum(cam, vars);
// should be culled from the geometry's PoV
checkFilteredLights(0);
// Now it touches the near plane.
sl.setSpotRange(1f);
// still culled from the geometry's PoV
checkFilteredLights(0);
assert sl.intersectsFrustum(cam, vars);
} finally {
vars.release();
}
// make it barely reach the geometry
sl.setSpotRange(9f);
checkFilteredLights(0);
// make it reach the geometry (touching its bound)
sl.setSpotRange(9f + FastMath.ZERO_TOLERANCE);
checkFilteredLights(1);
// rotate the cone a bit so it no longer faces the geom
sl.setDirection(new Vector3f(0.316f, 0, 0.948f).normalizeLocal());
checkFilteredLights(0);
// extent the range much farther
sl.setSpotRange(20);
checkFilteredLights(0);
// Create box of size X=10 (double the extent)
// now, the spot will touch the box.
geom.setMesh(new Box(5, 1, 1));
checkFilteredLights(1);
// ==================================
// Tests for bounding sphere, with a radius of 1f (in the box geom)
sl.setPosition(Vector3f.ZERO);
sl.setDirection(Vector3f.UNIT_Z);
geom.setLocalTranslation(Vector3f.ZERO);
geom.setModelBound(new BoundingSphere(1f, Vector3f.ZERO));
// Infinit spot lights are only filtered
// if the geometry is outside the infinite cone.
sl.setSpotRange(0);
checkFilteredLights(1);
//the geommetry is outside the infinit cone (cone direction going away from the geom)
sl.setPosition(Vector3f.UNIT_Z.mult(1+FastMath.ZERO_TOLERANCE));
checkFilteredLights(0);
//place the spote ligth in the corner of the box geom, (in order to test bounding sphere)
sl.setDirection(new Vector3f(1, 1, 0).normalizeLocal());
geom.setLocalTranslation(0, 0, 10);
sl.setPosition(sl.getDirection().mult(-2f).add(geom.getLocalTranslation()));
// make it barely reach the sphere, incorect with a box
sl.setSpotRange(1f - FastMath.ZERO_TOLERANCE);
checkFilteredLights(0);
// make it reach the sphere
sl.setSpotRange(1f + FastMath.ZERO_TOLERANCE);
checkFilteredLights(1);
// extent the range
sl.setPosition(Vector3f.ZERO);
sl.setDirection(Vector3f.UNIT_Z);
sl.setSpotRange(20);
checkFilteredLights(1);
// rotate the cone a bit so it no longer faces the geom
sl.setDirection(new Vector3f(0, 0.3f, 0.7f).normalizeLocal());
checkFilteredLights(0);
// Create sphere of size X=10 (double the radius)
// now, the spot will touch the sphere.
geom.setModelBound(new BoundingSphere(5f, Vector3f.ZERO));
checkFilteredLights(1);
}
}

@ -0,0 +1,115 @@
/*
* Copyright (c) 2009-2015 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.light;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import org.junit.Test;
/**
* Test light sorting (in the scene graph) for various light types.
*
* @author Kirill Vainer
*/
public class LightSortTest {
@Test
public void testSimpleSort() {
Geometry g = new Geometry("test", new Mesh());
LightList list = new LightList(g);
list.add(new SpotLight(Vector3f.ZERO, Vector3f.UNIT_X));
list.add(new PointLight(Vector3f.UNIT_X));
list.add(new DirectionalLight(Vector3f.UNIT_X));
list.add(new AmbientLight());
list.sort(true);
assert list.get(0) instanceof AmbientLight; // Ambients always first
assert list.get(1) instanceof DirectionalLight; // .. then directionals
assert list.get(2) instanceof SpotLight; // Spot is 0 units away from geom
assert list.get(3) instanceof PointLight; // .. and point is 1 unit away.
}
@Test
public void testSceneGraphSort() {
Node n = new Node("node");
Geometry g = new Geometry("geom", new Mesh());
SpotLight spot = new SpotLight(Vector3f.ZERO, Vector3f.UNIT_X);
PointLight point = new PointLight(Vector3f.UNIT_X);
DirectionalLight directional = new DirectionalLight(Vector3f.UNIT_X);
AmbientLight ambient = new AmbientLight();
// Some lights are on the node
n.addLight(spot);
n.addLight(point);
// .. and some on the geometry.
g.addLight(directional);
g.addLight(ambient);
n.attachChild(g);
n.updateGeometricState();
LightList list = g.getWorldLightList();
// check the sorting (when geom is at 0,0,0)
assert list.get(0) instanceof AmbientLight;
assert list.get(1) instanceof DirectionalLight;
assert list.get(2) instanceof SpotLight;
assert list.get(3) instanceof PointLight;
// move the geometry closer to the point light
g.setLocalTranslation(Vector3f.UNIT_X);
n.updateGeometricState();
assert list.get(0) instanceof AmbientLight;
assert list.get(1) instanceof DirectionalLight;
assert list.get(2) instanceof PointLight;
assert list.get(3) instanceof SpotLight;
// now move the point light away from the geometry
// and the spot light closer
// XXX: doesn't work! jME can't detect that the light moved!
// point.setPosition(Vector3f.ZERO);
// spot.setPosition(Vector3f.UNIT_X);
// n.updateGeometricState();
//
// assert list.get(0) instanceof AmbientLight;
// assert list.get(1) instanceof DirectionalLight;
// assert list.get(2) instanceof SpotLight;
// assert list.get(3) instanceof PointLight;
}
}

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

@ -0,0 +1,59 @@
/*
* Copyright (c) 2009-2015 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.math;
import org.junit.Test;
/**
* Verifies that algorithms in {@link FastMath} are working correctly.
*
* @author Kirill Vainer
*/
public class FastMathTest {
private int nearestPowerOfTwoSlow(int number) {
return (int) Math.pow(2, Math.ceil(Math.log(number) / Math.log(2)));
}
@Test
public void testNearestPowerOfTwo() {
for (int i = -100; i < 1; i++) {
assert FastMath.nearestPowerOfTwo(i) == 1;
}
for (int i = 1; i < 10000; i++) {
int nextPowerOf2 = FastMath.nearestPowerOfTwo(i);
assert i <= nextPowerOf2;
assert FastMath.isPowerOfTwo(nextPowerOf2);
assert nextPowerOf2 == nearestPowerOfTwoSlow(i);
}
}
}

@ -0,0 +1,73 @@
/*
* Copyright (c) 2009-2015 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.util;
import java.util.Map.Entry;
import org.junit.Test;
/**
* Check if the {@link ListMap} class is working correctly.
*
* @author Kirill Vainer
*/
public class ListMapTest {
@Test
public void testListMap() {
ListMap<String, String> listMap = new ListMap<String, String>();
listMap.put("bob", "hello");
assert "hello".equals(listMap.get("bob"));
assert "hello".equals(listMap.remove("bob"));
assert listMap.size() == 0;
assert listMap.isEmpty();
listMap.put("abc", "1");
listMap.put("def", "2");
listMap.put("ghi", "3");
listMap.put("jkl", "4");
listMap.put("mno", "5");
assert "3".equals(listMap.get("ghi"));
assert listMap.size() == 5;
assert !listMap.isEmpty();
// check iteration order, should be consistent
for (int i = 0; i < listMap.size(); i++) {
String expectedValue = Integer.toString(i + 1);
String key = listMap.getKey(i);
String value = listMap.getValue(i);
Entry<String, String> entry = listMap.getEntry(i);
assert key.equals(entry.getKey());
assert value.equals(entry.getValue());
assert expectedValue.equals(value);
}
}
}

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

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

@ -205,7 +205,6 @@ public class MjpegFileWriter {
baos.write(fcc3); baos.write(fcc3);
baos.write(intBytes(swapInt(listSize))); baos.write(intBytes(swapInt(listSize)));
baos.write(fcc4); baos.write(fcc4);
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
@ -275,7 +274,6 @@ public class MjpegFileWriter {
baos.write(intBytes(swapInt(dwReserved[1]))); baos.write(intBytes(swapInt(dwReserved[1])));
baos.write(intBytes(swapInt(dwReserved[2]))); baos.write(intBytes(swapInt(dwReserved[2])));
baos.write(intBytes(swapInt(dwReserved[3]))); baos.write(intBytes(swapInt(dwReserved[3])));
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
@ -295,7 +293,6 @@ public class MjpegFileWriter {
baos.write(fcc); baos.write(fcc);
baos.write(intBytes(swapInt(size))); baos.write(intBytes(swapInt(size)));
baos.write(fcc2); baos.write(fcc2);
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
@ -365,7 +362,6 @@ public class MjpegFileWriter {
baos.write(intBytes(swapInt(top))); baos.write(intBytes(swapInt(top)));
baos.write(intBytes(swapInt(right))); baos.write(intBytes(swapInt(right)));
baos.write(intBytes(swapInt(bottom))); baos.write(intBytes(swapInt(bottom)));
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
@ -420,7 +416,6 @@ public class MjpegFileWriter {
baos.write(intBytes(swapInt(biYPelsPerMeter))); baos.write(intBytes(swapInt(biYPelsPerMeter)));
baos.write(intBytes(swapInt(biClrUsed))); baos.write(intBytes(swapInt(biClrUsed)));
baos.write(intBytes(swapInt(biClrImportant))); baos.write(intBytes(swapInt(biClrImportant)));
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
@ -445,7 +440,6 @@ public class MjpegFileWriter {
baos.write(fcc); baos.write(fcc);
baos.write(intBytes(swapInt(listSize))); baos.write(intBytes(swapInt(listSize)));
baos.write(fcc2); baos.write(fcc2);
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
@ -480,8 +474,6 @@ public class MjpegFileWriter {
baos.write(in.toBytes()); baos.write(in.toBytes());
} }
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
} }
@ -504,7 +496,6 @@ public class MjpegFileWriter {
baos.write(intBytes(swapInt(dwFlags))); baos.write(intBytes(swapInt(dwFlags)));
baos.write(intBytes(swapInt(dwOffset))); baos.write(intBytes(swapInt(dwOffset)));
baos.write(intBytes(swapInt(dwSize))); baos.write(intBytes(swapInt(dwSize)));
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
@ -525,7 +516,6 @@ public class MjpegFileWriter {
baos.write(fcc); baos.write(fcc);
baos.write(intBytes(swapInt(size))); baos.write(intBytes(swapInt(size)));
baos.write(data); baos.write(data);
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
@ -552,7 +542,6 @@ public class MjpegFileWriter {
imgWrtr.write(null, new IIOImage(bi, null, null), jpgWrtPrm); imgWrtr.write(null, new IIOImage(bi, null, null), jpgWrtPrm);
imgOutStrm.close(); imgOutStrm.close();
baos.close();
return baos.toByteArray(); return baos.toByteArray();
} }
} }

@ -31,11 +31,7 @@
*/ */
package com.jme3.system; package com.jme3.system;
import java.io.File; import java.io.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
@ -48,9 +44,8 @@ import java.util.logging.Logger;
/** /**
* Utility class to register, extract, and load native libraries. * Utility class to register, extract, and load native libraries.
* <br> * <br>
* Register your own libraries via the * Register your own libraries via the {@link #registerNativeLibrary(String, Platform, String, String)} method, for
* {@link #registerNativeLibrary(java.lang.String, com.jme3.system.Platform, java.lang.String, boolean) } * each platform.
* method, for each platform.
* You can then extract this library (depending on platform), by * You can then extract this library (depending on platform), by
* using {@link #loadNativeLibrary(java.lang.String, boolean) }. * using {@link #loadNativeLibrary(java.lang.String, boolean) }.
* <br> * <br>
@ -142,6 +137,39 @@ public final class NativeLibraryLoader {
registerNativeLibrary("openal", Platform.MacOSX32, "native/macosx/openal.dylib", "libopenal.dylib"); registerNativeLibrary("openal", Platform.MacOSX32, "native/macosx/openal.dylib", "libopenal.dylib");
registerNativeLibrary("openal", Platform.MacOSX64, "native/macosx/openal.dylib", "libopenal.dylib"); registerNativeLibrary("openal", Platform.MacOSX64, "native/macosx/openal.dylib", "libopenal.dylib");
// LWJGL 3.x
registerNativeLibrary("lwjgl3", Platform.Windows32, "native/windows/lwjgl32.dll");
registerNativeLibrary("lwjgl3", Platform.Windows64, "native/windows/lwjgl.dll");
registerNativeLibrary("lwjgl3", Platform.Linux32, "native/linux/liblwjgl32.so");
registerNativeLibrary("lwjgl3", Platform.Linux64, "native/linux/liblwjgl.so");
registerNativeLibrary("lwjgl3", Platform.MacOSX32, "native/macosx/liblwjgl.dylib");
registerNativeLibrary("lwjgl3", Platform.MacOSX64, "native/macosx/liblwjgl.dylib");
// GLFW for LWJGL 3.x
registerNativeLibrary("glfw-lwjgl3", Platform.Windows32, "native/windows/glfw32.dll");
registerNativeLibrary("glfw-lwjgl3", Platform.Windows64, "native/windows/glfw.dll");
registerNativeLibrary("glfw-lwjgl3", Platform.Linux32, "native/linux/libglfw32.so");
registerNativeLibrary("glfw-lwjgl3", Platform.Linux64, "native/linux/libglfw.dll");
registerNativeLibrary("glfw-lwjgl3", Platform.MacOSX32, "native/macosx/libglfw.dylib");
registerNativeLibrary("glfw-lwjgl3", Platform.MacOSX64, "native/macosx/libglfw.dylib");
// jemalloc for LWJGL 3.x
registerNativeLibrary("jemalloc-lwjgl3", Platform.Windows32, "native/windows/jemalloc32.dll");
registerNativeLibrary("jemalloc-lwjgl3", Platform.Windows64, "native/windows/jemalloc.dll");
registerNativeLibrary("jemalloc-lwjgl3", Platform.Linux32, "native/linux/libjemalloc32.so");
registerNativeLibrary("jemalloc-lwjgl3", Platform.Linux64, "native/linux/libjemalloc.so");
registerNativeLibrary("jemalloc-lwjgl3", Platform.MacOSX32, "native/macosx/libjemalloc.dylib");
registerNativeLibrary("jemalloc-lwjgl3", Platform.MacOSX64, "native/macosx/libjemalloc.dylib");
// OpenAL for LWJGL 3.x
// For OSX: Need to add lib prefix when extracting
registerNativeLibrary("openal-lwjgl3", Platform.Windows32, "native/windows/OpenAL32.dll");
registerNativeLibrary("openal-lwjgl3", Platform.Windows64, "native/windows/OpenAL.dll");
registerNativeLibrary("openal-lwjgl3", Platform.Linux32, "native/linux/libopenal32.so");
registerNativeLibrary("openal-lwjgl3", Platform.Linux64, "native/linux/libopenal.so");
registerNativeLibrary("openal-lwjgl3", Platform.MacOSX32, "native/macosx/openal.dylib", "libopenal.dylib");
registerNativeLibrary("openal-lwjgl3", Platform.MacOSX64, "native/macosx/openal.dylib", "libopenal.dylib");
// BulletJme // BulletJme
registerNativeLibrary("bulletjme", Platform.Windows32, "native/windows/x86/bulletjme.dll"); registerNativeLibrary("bulletjme", Platform.Windows32, "native/windows/x86/bulletjme.dll");
registerNativeLibrary("bulletjme", Platform.Windows64, "native/windows/x86_64/bulletjme.dll"); registerNativeLibrary("bulletjme", Platform.Windows64, "native/windows/x86_64/bulletjme.dll");
@ -526,14 +554,15 @@ public final class NativeLibraryLoader {
} }
} }
String pathInJar = library.getPathInNativesJar(); final String pathInJar = library.getPathInNativesJar();
if (pathInJar == null) { if (pathInJar == null) {
// This platform does not require the native library to be loaded. // This platform does not require the native library to be loaded.
return; return;
} }
String fileNameInJar; final String fileNameInJar;
if (pathInJar.contains("/")) { if (pathInJar.contains("/")) {
fileNameInJar = pathInJar.substring(pathInJar.lastIndexOf("/") + 1); fileNameInJar = pathInJar.substring(pathInJar.lastIndexOf("/") + 1);
} else { } else {
@ -642,7 +671,7 @@ public final class NativeLibraryLoader {
} finally { } finally {
// XXX: HACK. Vary loading method based on library name.. // XXX: HACK. Vary loading method based on library name..
// lwjgl and jinput handle loading by themselves. // lwjgl and jinput handle loading by themselves.
if (name.equals("lwjgl")) { if (name.equals("lwjgl") || name.equals("lwjgl3")) {
System.setProperty("org.lwjgl.librarypath", System.setProperty("org.lwjgl.librarypath",
extactionDirectory.getAbsolutePath()); extactionDirectory.getAbsolutePath());
} else if (name.equals("jinput")) { } else if (name.equals("jinput")) {

@ -70,6 +70,12 @@ public class FXAAFilter extends Filter {
return material; return material;
} }
@Override
protected boolean isRequiresBilinear() {
// FXAA wants the input texture to be filtered.
return true;
}
public void setSpanMax(float spanMax) { public void setSpanMax(float spanMax) {
this.spanMax = spanMax; this.spanMax = spanMax;
if (material != null) { if (material != null) {

@ -18,6 +18,8 @@ uniform float m_Luminance5;
uniform float m_LineDistance; uniform float m_LineDistance;
uniform float m_LineThickness; uniform float m_LineThickness;
out vec4 fragColor;
void main() { void main() {
vec4 texVal = getColor(m_Texture, texCoord); vec4 texVal = getColor(m_Texture, texCoord);
float linePixel = 0; float linePixel = 0;
@ -49,5 +51,5 @@ void main() {
// Mix paper color with existing color information // Mix paper color with existing color information
vec4 paperColor = mix(m_PaperColor, texVal, m_ColorInfluencePaper); vec4 paperColor = mix(m_PaperColor, texVal, m_ColorInfluencePaper);
gl_FragColor = mix(paperColor, lineColor, linePixel); fragColor = mix(paperColor, lineColor, linePixel);
} }

@ -11,6 +11,8 @@ uniform float m_YScale;
vec2 m_NearFar = vec2( 0.1, 1000.0 ); vec2 m_NearFar = vec2( 0.1, 1000.0 );
out vec4 fragColor;
void main() { void main() {
vec4 texVal = getColor( m_Texture, texCoord ); vec4 texVal = getColor( m_Texture, texCoord );
@ -44,7 +46,7 @@ void main() {
if( unfocus < 0.2 ) { if( unfocus < 0.2 ) {
// If we are mostly in focus then don't bother with the // If we are mostly in focus then don't bother with the
// convolution filter // convolution filter
gl_FragColor = texVal; fragColor = texVal;
} else { } else {
// Perform a wide convolution filter and we scatter it // Perform a wide convolution filter and we scatter it
// a bit to avoid some texture look-ups. Instead of // a bit to avoid some texture look-ups. Instead of
@ -83,7 +85,7 @@ void main() {
sum = sum / 12.0; sum = sum / 12.0;
gl_FragColor = mix( texVal, sum, unfocus ); fragColor = mix( texVal, sum, unfocus );
// I used this for debugging the range // I used this for debugging the range
// gl_FragColor.r = unfocus; // gl_FragColor.r = unfocus;

@ -4,8 +4,9 @@ uniform COLORTEXTURE m_Texture;
uniform float m_Value; uniform float m_Value;
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor;
void main() { void main() {
vec4 texVal = getColor(m_Texture, texCoord); vec4 texVal = getColor(m_Texture, texCoord);
gl_FragColor = texVal * m_Value; fragColor = texVal * m_Value;
} }

@ -8,6 +8,7 @@ uniform float m_FogDensity;
uniform float m_FogDistance; uniform float m_FogDistance;
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor;
vec2 m_FrustumNearFar=vec2(1.0,m_FogDistance); vec2 m_FrustumNearFar=vec2(1.0,m_FogDistance);
const float LOG2 = 1.442695; const float LOG2 = 1.442695;
@ -19,6 +20,6 @@ void main() {
float fogFactor = exp2( -m_FogDensity * m_FogDensity * depth * depth * LOG2 ); float fogFactor = exp2( -m_FogDensity * m_FogDensity * depth * depth * LOG2 );
fogFactor = clamp(fogFactor, 0.0, 1.0); fogFactor = clamp(fogFactor, 0.0, 1.0);
gl_FragColor =mix(m_FogColor,texVal,fogFactor); fragColor =mix(m_FogColor,texVal,fogFactor);
} }

@ -3,9 +3,10 @@
uniform COLORTEXTURE m_Texture; uniform COLORTEXTURE m_Texture;
uniform vec4 m_Color; uniform vec4 m_Color;
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor;
void main() { void main() {
vec4 texVal = getColor(m_Texture, texCoord); vec4 texVal = getColor(m_Texture, texCoord);
gl_FragColor = texVal * m_Color; fragColor = texVal * m_Color;
} }

@ -2,6 +2,7 @@
uniform COLORTEXTURE m_Texture; uniform COLORTEXTURE m_Texture;
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor;
uniform int m_NumColors; uniform int m_NumColors;
uniform float m_Gamma; uniform float m_Gamma;
@ -16,5 +17,5 @@ void main() {
texVal = texVal / m_NumColors; texVal = texVal / m_NumColors;
texVal = pow(texVal, vec4(1.0/m_Gamma)); texVal = pow(texVal, vec4(1.0/m_Gamma));
gl_FragColor = mix(getColor(m_Texture, texCoord), texVal, m_Strength); fragColor = mix(getColor(m_Texture, texCoord), texVal, m_Strength);
} }

@ -6,10 +6,11 @@ uniform sampler2D m_BloomTex;
uniform float m_BloomIntensity; uniform float m_BloomIntensity;
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor;
void main(){ void main(){
vec4 colorRes = getColor(m_Texture,texCoord); vec4 colorRes = getColor(m_Texture,texCoord);
vec4 bloom = texture2D(m_BloomTex, texCoord); vec4 bloom = texture2D(m_BloomTex, texCoord);
gl_FragColor = bloom * m_BloomIntensity + colorRes; fragColor = bloom * m_BloomIntensity + colorRes;
} }

@ -10,6 +10,7 @@ uniform float m_YScale;
uniform vec2 m_FrustumNearFar; uniform vec2 m_FrustumNearFar;
in vec2 texCoord; in vec2 texCoord;
out vec4 fragColor;
vec4 getResult(vec4 color){ vec4 getResult(vec4 color){
@ -125,7 +126,7 @@ float readDepth(in vec2 uv){
void main(){ void main(){
// float depth =texture2D(m_DepthTexture,uv).r; // float depth =texture2D(m_DepthTexture,uv).r;
gl_FragColor=getResult(convolutionFilter()); fragColor=getResult(convolutionFilter());
// gl_FragColor=getResult(bilateralFilter()); // gl_FragColor=getResult(bilateralFilter());
// gl_FragColor=getColor(m_SSAOMap,texCoord); // gl_FragColor=getColor(m_SSAOMap,texCoord);

@ -21,6 +21,7 @@ dependencies {
// compile project(':jme3-bullet-native') // compile project(':jme3-bullet-native')
compile project(':jme3-jbullet') compile project(':jme3-jbullet')
compile project(':jme3-jogg') compile project(':jme3-jogg')
compile project(':jme3-jogl')
compile project(':jme3-lwjgl') compile project(':jme3-lwjgl')
compile project(':jme3-networking') compile project(':jme3-networking')
compile project(':jme3-niftygui') compile project(':jme3-niftygui')

@ -36,10 +36,10 @@ import com.jme3.app.SimpleApplication;
import com.jme3.asset.TextureKey; import com.jme3.asset.TextureKey;
import com.jme3.asset.plugins.HttpZipLocator; import com.jme3.asset.plugins.HttpZipLocator;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad; import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.ui.Picture;
/** /**
* This tests loading a file from a jar stored online. * This tests loading a file from a jar stored online.
@ -59,22 +59,27 @@ public class TestOnlineJar extends SimpleApplication {
quadMesh.updateGeometry(1, 1, true); quadMesh.updateGeometry(1, 1, true);
Geometry quad = new Geometry("Textured Quad", quadMesh); Geometry quad = new Geometry("Textured Quad", quadMesh);
assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/town.zip",
HttpZipLocator.class);
TextureKey key = new TextureKey("grass.jpg", false);
key.setGenerateMips(true);
Texture tex = assetManager.loadTexture(key);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/town.zip",
mat.setTexture("ColorMap", tex); HttpZipLocator.class);
quad.setMaterial(mat); assetManager.registerLocator("http://jmonkeyengine.googlecode.com/files/wildhouse.zip",
HttpZipLocator.class);
float aspect = tex.getImage().getWidth() / (float) tex.getImage().getHeight(); Picture pic1 = new Picture("Picture1");
quad.setLocalScale(new Vector3f(aspect * 1.5f, 1.5f, 1)); pic1.move(0, 0, -1);
quad.center(); pic1.setPosition(0, 0);
pic1.setWidth(128);
pic1.setHeight(128);
pic1.setImage(assetManager, "grass.jpg", false);
guiNode.attachChild(pic1);
rootNode.attachChild(quad); Picture pic2 = new Picture("Picture1");
pic2.move(0, 0, -1);
pic2.setPosition(128, 0);
pic2.setWidth(128);
pic2.setHeight(128);
pic2.setImage(assetManager, "glasstile2.png", false);
guiNode.attachChild(pic2);
} }
} }

@ -73,6 +73,13 @@ public class TestCustomAnim extends SimpleApplication {
Box box = new Box(1, 1, 1); Box box = new Box(1, 1, 1);
VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight);
VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex);
indicesHW.setUsage(Usage.CpuOnly);
weightsHW.setUsage(Usage.CpuOnly);
box.setBuffer(weightsHW);
box.setBuffer(indicesHW);
// Setup bone weight buffer // Setup bone weight buffer
FloatBuffer weights = FloatBuffer.allocate( box.getVertexCount() * 4 ); FloatBuffer weights = FloatBuffer.allocate( box.getVertexCount() * 4 );
VertexBuffer weightsBuf = new VertexBuffer(Type.BoneWeight); VertexBuffer weightsBuf = new VertexBuffer(Type.BoneWeight);

@ -63,7 +63,7 @@ public class TestInconsistentCompareDetection extends SimpleApplication {
cam.setLocation(new Vector3f(-11.674385f, 7.892636f, 33.133106f)); cam.setLocation(new Vector3f(-11.674385f, 7.892636f, 33.133106f));
cam.setRotation(new Quaternion(0.06426433f, 0.90940624f, -0.15329266f, 0.38125014f)); cam.setRotation(new Quaternion(0.06426433f, 0.90940624f, -0.15329266f, 0.38125014f));
Material m = new Material(assetManager, "Common/MatDefs/Misc/ColoredTextured.j3md"); Material m = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
m.setColor("Color", ColorRGBA.White); m.setColor("Color", ColorRGBA.White);
t1 = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"); t1 = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg");

@ -16,4 +16,5 @@ dependencies {
compile files('../lib/jbullet.jar', '../lib/stack-alloc.jar') compile files('../lib/jbullet.jar', '../lib/stack-alloc.jar')
compile project(':jme3-core') compile project(':jme3-core')
compile project(':jme3-terrain') compile project(':jme3-terrain')
// compile project(':jme3-bullet')
} }

@ -5,7 +5,7 @@ if (!hasProperty('mainClass')) {
dependencies { dependencies {
compile project(':jme3-core') compile project(':jme3-core')
compile project(':jme3-desktop') compile project(':jme3-desktop')
compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.1' compile 'org.jogamp.gluegen:gluegen-rt-main:2.3.2'
compile 'org.jogamp.jogl:jogl-all-main:2.3.1' compile 'org.jogamp.jogl:jogl-all-main:2.3.2'
compile 'org.jogamp.joal:joal-main:2.3.1' compile 'org.jogamp.joal:joal-main:2.3.2'
} }

@ -0,0 +1,603 @@
package com.jme3.renderer.jogl;
import com.jme3.renderer.RendererException;
import com.jme3.renderer.opengl.GL;
import com.jme3.renderer.opengl.GL2;
import com.jme3.renderer.opengl.GL3;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import com.jme3.renderer.opengl.GL4;
import com.jogamp.opengl.GLContext;
public class JoglGL implements GL, GL2, GL3, GL4 {
private static int getLimitBytes(ByteBuffer buffer) {
checkLimit(buffer);
return buffer.limit();
}
private static int getLimitBytes(ShortBuffer buffer) {
checkLimit(buffer);
return buffer.limit() * 2;
}
private static int getLimitBytes(FloatBuffer buffer) {
checkLimit(buffer);
return buffer.limit() * 4;
}
private static int getLimitCount(Buffer buffer, int elementSize) {
checkLimit(buffer);
return buffer.limit() / elementSize;
}
private static void checkLimit(Buffer buffer) {
if (buffer == null) {
return;
}
if (buffer.limit() == 0) {
throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
}
if (buffer.remaining() == 0) {
throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
}
}
@Override
public void resetStats() {
}
@Override
public void glActiveTexture(int param1) {
GLContext.getCurrentGL().glActiveTexture(param1);
}
@Override
public void glAlphaFunc(int param1, float param2) {
GLContext.getCurrentGL().getGL2ES1().glAlphaFunc(param1, param2);
}
@Override
public void glAttachShader(int param1, int param2) {
GLContext.getCurrentGL().getGL2ES2().glAttachShader(param1, param2);
}
@Override
public void glBindBuffer(int param1, int param2) {
GLContext.getCurrentGL().glBindBuffer(param1, param2);
}
@Override
public void glBindTexture(int param1, int param2) {
GLContext.getCurrentGL().glBindTexture(param1, param2);
}
@Override
public void glBlendFunc(int param1, int param2) {
GLContext.getCurrentGL().glBlendFunc(param1, param2);
}
@Override
public void glBufferData(int param1, long param2, int param3) {
GLContext.getCurrentGL().glBufferData(param1, param2, null, param3);
}
@Override
public void glBufferData(int param1, FloatBuffer param2, int param3) {
checkLimit(param2);
GLContext.getCurrentGL().glBufferData(param1, getLimitBytes(param2), param2, param3);
}
@Override
public void glBufferData(int param1, ShortBuffer param2, int param3) {
checkLimit(param2);
GLContext.getCurrentGL().glBufferData(param1, getLimitBytes(param2), param2, param3);
}
@Override
public void glBufferData(int param1, ByteBuffer param2, int param3) {
checkLimit(param2);
GLContext.getCurrentGL().glBufferData(param1, getLimitBytes(param2), param2, param3);
}
@Override
public void glBufferSubData(int param1, long param2, FloatBuffer param3) {
checkLimit(param3);
GLContext.getCurrentGL().glBufferSubData(param1, param2, getLimitBytes(param3), param3);
}
@Override
public void glBufferSubData(int param1, long param2, ShortBuffer param3) {
checkLimit(param3);
GLContext.getCurrentGL().glBufferSubData(param1, param2, getLimitBytes(param3), param3);
}
@Override
public void glBufferSubData(int param1, long param2, ByteBuffer param3) {
checkLimit(param3);
GLContext.getCurrentGL().glBufferSubData(param1, param2, getLimitBytes(param3), param3);
}
@Override
public void glClear(int param1) {
GLContext.getCurrentGL().glClear(param1);
}
@Override
public void glClearColor(float param1, float param2, float param3, float param4) {
GLContext.getCurrentGL().glClearColor(param1, param2, param3, param4);
}
@Override
public void glColorMask(boolean param1, boolean param2, boolean param3, boolean param4) {
GLContext.getCurrentGL().glColorMask(param1, param2, param3, param4);
}
@Override
public void glCompileShader(int param1) {
GLContext.getCurrentGL().getGL2ES2().glCompileShader(param1);
}
@Override
public void glCompressedTexImage2D(int param1, int param2, int param3, int param4, int param5, int param6, ByteBuffer param7) {
checkLimit(param7);
GLContext.getCurrentGL().glCompressedTexImage2D(param1, param2, param3, param4, param5, param6, getLimitBytes(param7), param7);
}
@Override
public void glCompressedTexImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, ByteBuffer param8) {
checkLimit(param8);
GLContext.getCurrentGL().getGL2ES2().glCompressedTexImage3D(param1, param2, param3, param4, param5, param6, param7, getLimitBytes(param8), param8);
}
@Override
public void glCompressedTexSubImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, ByteBuffer param8) {
checkLimit(param8);
GLContext.getCurrentGL().glCompressedTexSubImage2D(param1, param2, param3, param4, param5, param6, param7, getLimitBytes(param8), param8);
}
@Override
public void glCompressedTexSubImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, ByteBuffer param10) {
checkLimit(param10);
GLContext.getCurrentGL().getGL2ES2().glCompressedTexSubImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, getLimitBytes(param10), param10);
}
@Override
public int glCreateProgram() {
return GLContext.getCurrentGL().getGL2ES2().glCreateProgram();
}
@Override
public int glCreateShader(int param1) {
return GLContext.getCurrentGL().getGL2ES2().glCreateShader(param1);
}
@Override
public void glCullFace(int param1) {
GLContext.getCurrentGL().glCullFace(param1);
}
@Override
public void glDeleteBuffers(IntBuffer param1) {
checkLimit(param1);
GLContext.getCurrentGL().glDeleteBuffers(param1.limit(), param1);
}
@Override
public void glDeleteProgram(int param1) {
GLContext.getCurrentGL().getGL2ES2().glDeleteProgram(param1);
}
@Override
public void glDeleteShader(int param1) {
GLContext.getCurrentGL().getGL2ES2().glDeleteShader(param1);
}
@Override
public void glDeleteTextures(IntBuffer param1) {
checkLimit(param1);
GLContext.getCurrentGL().glDeleteTextures(param1.limit() ,param1);
}
@Override
public void glDepthFunc(int param1) {
GLContext.getCurrentGL().glDepthFunc(param1);
}
@Override
public void glDepthMask(boolean param1) {
GLContext.getCurrentGL().glDepthMask(param1);
}
@Override
public void glDepthRange(double param1, double param2) {
GLContext.getCurrentGL().glDepthRange(param1, param2);
}
@Override
public void glDetachShader(int param1, int param2) {
GLContext.getCurrentGL().getGL2ES2().glDetachShader(param1, param2);
}
@Override
public void glDisable(int param1) {
GLContext.getCurrentGL().glDisable(param1);
}
@Override
public void glDisableVertexAttribArray(int param1) {
GLContext.getCurrentGL().getGL2ES2().glDisableVertexAttribArray(param1);
}
@Override
public void glDrawArrays(int param1, int param2, int param3) {
GLContext.getCurrentGL().glDrawArrays(param1, param2, param3);
}
@Override
public void glDrawBuffer(int param1) {
GLContext.getCurrentGL().getGL2GL3().glDrawBuffer(param1);
}
@Override
public void glDrawRangeElements(int param1, int param2, int param3, int param4, int param5, long param6) {
GLContext.getCurrentGL().getGL2ES3().glDrawRangeElements(param1, param2, param3, param4, param5, param6);
}
@Override
public void glEnable(int param1) {
GLContext.getCurrentGL().glEnable(param1);
}
@Override
public void glEnableVertexAttribArray(int param1) {
GLContext.getCurrentGL().getGL2ES2().glEnableVertexAttribArray(param1);
}
@Override
public void glGenBuffers(IntBuffer param1) {
checkLimit(param1);
GLContext.getCurrentGL().glGenBuffers(param1.limit(), param1);
}
@Override
public void glGenTextures(IntBuffer param1) {
checkLimit(param1);
GLContext.getCurrentGL().glGenTextures(param1.limit(), param1);
}
@Override
public void glGetBoolean(int param1, ByteBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().glGetBooleanv(param1, param2);
}
@Override
public void glGetBufferSubData(int target, long offset, ByteBuffer data) {
checkLimit(data);
GLContext.getCurrentGL().getGL2GL3().glGetBufferSubData(target, offset, getLimitBytes(data), data);
}
@Override
public int glGetError() {
return GLContext.getCurrentGL().glGetError();
}
@Override
public void glGetInteger(int param1, IntBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().glGetIntegerv(param1, param2);
}
@Override
public void glGetProgram(int param1, int param2, IntBuffer param3) {
checkLimit(param3);
GLContext.getCurrentGL().getGL2ES2().glGetProgramiv(param1, param2, param3);
}
@Override
public void glGetShader(int param1, int param2, IntBuffer param3) {
checkLimit(param3);
GLContext.getCurrentGL().getGL2ES2().glGetShaderiv(param1, param2, param3);
}
@Override
public String glGetString(int param1) {
return GLContext.getCurrentGL().glGetString(param1);
}
@Override
public String glGetString(int param1, int param2) {
return GLContext.getCurrentGL().getGL2ES3().glGetStringi(param1, param2);
}
@Override
public boolean glIsEnabled(int param1) {
return GLContext.getCurrentGL().glIsEnabled(param1);
}
@Override
public void glLineWidth(float param1) {
GLContext.getCurrentGL().glLineWidth(param1);
}
@Override
public void glLinkProgram(int param1) {
GLContext.getCurrentGL().getGL2ES2().glLinkProgram(param1);
}
@Override
public void glPixelStorei(int param1, int param2) {
GLContext.getCurrentGL().glPixelStorei(param1, param2);
}
@Override
public void glPointSize(float param1) {
GLContext.getCurrentGL().getGL2ES1().glPointSize(param1);
}
@Override
public void glPolygonMode(int param1, int param2) {
GLContext.getCurrentGL().getGL2().glPolygonMode(param1, param2);
}
@Override
public void glPolygonOffset(float param1, float param2) {
GLContext.getCurrentGL().glPolygonOffset(param1, param2);
}
@Override
public void glReadBuffer(int param1) {
GLContext.getCurrentGL().getGL2ES3().glReadBuffer(param1);
}
@Override
public void glReadPixels(int param1, int param2, int param3, int param4, int param5, int param6, ByteBuffer param7) {
checkLimit(param7);
GLContext.getCurrentGL().glReadPixels(param1, param2, param3, param4, param5, param6, param7);
}
@Override
public void glReadPixels(int param1, int param2, int param3, int param4, int param5, int param6, long param7) {
GLContext.getCurrentGL().glReadPixels(param1, param2, param3, param4, param5, param6, param7);
}
@Override
public void glScissor(int param1, int param2, int param3, int param4) {
GLContext.getCurrentGL().glScissor(param1, param2, param3, param4);
}
@Override
public void glStencilFuncSeparate(int param1, int param2, int param3, int param4) {
GLContext.getCurrentGL().getGL2ES2().glStencilFuncSeparate(param1, param2, param3, param4);
}
@Override
public void glStencilOpSeparate(int param1, int param2, int param3, int param4) {
GLContext.getCurrentGL().getGL2ES2().glStencilOpSeparate(param1, param2, param3, param4);
}
@Override
public void glTexImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, ByteBuffer param9) {
checkLimit(param9);
GLContext.getCurrentGL().glTexImage2D(param1, param2, param3, param4, param5, param6, param7, param8, param9);
}
@Override
public void glTexImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, ByteBuffer param10) {
checkLimit(param10);
GLContext.getCurrentGL().getGL2ES2().glTexImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
}
@Override
public void glTexParameterf(int param1, int param2, float param3) {
GLContext.getCurrentGL().glTexParameterf(param1, param2, param3);
}
@Override
public void glTexParameteri(int param1, int param2, int param3) {
GLContext.getCurrentGL().glTexParameteri(param1, param2, param3);
}
@Override
public void glTexSubImage2D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, ByteBuffer param9) {
checkLimit(param9);
GLContext.getCurrentGL().glTexSubImage2D(param1, param2, param3, param4, param5, param6, param7, param8, param9);
}
@Override
public void glTexSubImage3D(int param1, int param2, int param3, int param4, int param5, int param6, int param7, int param8, int param9, int param10, ByteBuffer param11) {
checkLimit(param11);
GLContext.getCurrentGL().getGL2ES2().glTexSubImage3D(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11);
}
@Override
public void glUniform1(int param1, FloatBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().getGL2ES2().glUniform1fv(param1, getLimitCount(param2, 1), param2);
}
@Override
public void glUniform1(int param1, IntBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().getGL2ES2().glUniform1iv(param1, getLimitCount(param2, 1), param2);
}
@Override
public void glUniform1f(int param1, float param2) {
GLContext.getCurrentGL().getGL2ES2().glUniform1f(param1, param2);
}
@Override
public void glUniform1i(int param1, int param2) {
GLContext.getCurrentGL().getGL2ES2().glUniform1i(param1, param2);
}
@Override
public void glUniform2(int param1, IntBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().getGL2ES2().glUniform2iv(param1, getLimitCount(param2, 2), param2);
}
@Override
public void glUniform2(int param1, FloatBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().getGL2ES2().glUniform2fv(param1, getLimitCount(param2, 2), param2);
}
@Override
public void glUniform2f(int param1, float param2, float param3) {
GLContext.getCurrentGL().getGL2ES2().glUniform2f(param1, param2, param3);
}
@Override
public void glUniform3(int param1, IntBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().getGL2ES2().glUniform3iv(param1, getLimitCount(param2, 3), param2);
}
@Override
public void glUniform3(int param1, FloatBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().getGL2ES2().glUniform3fv(param1, getLimitCount(param2, 3), param2);
}
@Override
public void glUniform3f(int param1, float param2, float param3, float param4) {
GLContext.getCurrentGL().getGL2ES2().glUniform3f(param1, param2, param3, param4);
}
@Override
public void glUniform4(int param1, FloatBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().getGL2ES2().glUniform4fv(param1, getLimitCount(param2, 4), param2);
}
@Override
public void glUniform4(int param1, IntBuffer param2) {
checkLimit(param2);
GLContext.getCurrentGL().getGL2ES2().glUniform4iv(param1, getLimitCount(param2, 4), param2);
}
@Override
public void glUniform4f(int param1, float param2, float param3, float param4, float param5) {
GLContext.getCurrentGL().getGL2ES2().glUniform4f(param1, param2, param3, param4, param5);
}
@Override
public void glUniformMatrix3(int param1, boolean param2, FloatBuffer param3) {
checkLimit(param3);
GLContext.getCurrentGL().getGL2ES2().glUniformMatrix3fv(param1, getLimitCount(param3, 3 * 3), param2, param3);
}
@Override
public void glUniformMatrix4(int param1, boolean param2, FloatBuffer param3) {
checkLimit(param3);
GLContext.getCurrentGL().getGL2ES2().glUniformMatrix4fv(param1, getLimitCount(param3, 4 * 4), param2, param3);
}
@Override
public void glUseProgram(int param1) {
GLContext.getCurrentGL().getGL2ES2().glUseProgram(param1);
}
@Override
public void glVertexAttribPointer(int param1, int param2, int param3, boolean param4, int param5, long param6) {
GLContext.getCurrentGL().getGL2ES2().glVertexAttribPointer(param1, param2, param3, param4, param5, param6);
}
@Override
public void glViewport(int param1, int param2, int param3, int param4) {
GLContext.getCurrentGL().glViewport(param1, param2, param3, param4);
}
@Override
public int glGetAttribLocation(int param1, String param2) {
// JOGL 2.0 doesn't need a null-terminated string
return GLContext.getCurrentGL().getGL2ES2().glGetAttribLocation(param1, param2);
}
@Override
public int glGetUniformLocation(int param1, String param2) {
// JOGL 2.0 doesn't need a null-terminated string
return GLContext.getCurrentGL().getGL2ES2().glGetUniformLocation(param1, param2);
}
@Override
public void glShaderSource(int param1, String[] param2, IntBuffer param3) {
checkLimit(param3);
int param3pos = param3.position();
try {
for (final String param2string : param2) {
param3.put(Math.max(param2string.length(), param2string.getBytes().length));
}
} finally {
param3.position(param3pos);
}
GLContext.getCurrentGL().getGL2ES2().glShaderSource(param1, param2.length, param2, param3);
}
@Override
public String glGetProgramInfoLog(int program, int maxSize) {
ByteBuffer buffer = ByteBuffer.allocateDirect(maxSize);
buffer.order(ByteOrder.nativeOrder());
ByteBuffer tmp = ByteBuffer.allocateDirect(4);
tmp.order(ByteOrder.nativeOrder());
IntBuffer intBuffer = tmp.asIntBuffer();
GLContext.getCurrentGL().getGL2ES2().glGetProgramInfoLog(program, maxSize, intBuffer, buffer);
int numBytes = intBuffer.get(0);
byte[] bytes = new byte[numBytes];
buffer.get(bytes);
return new String(bytes);
}
@Override
public String glGetShaderInfoLog(int shader, int maxSize) {
ByteBuffer buffer = ByteBuffer.allocateDirect(maxSize);
buffer.order(ByteOrder.nativeOrder());
ByteBuffer tmp = ByteBuffer.allocateDirect(4);
tmp.order(ByteOrder.nativeOrder());
IntBuffer intBuffer = tmp.asIntBuffer();
GLContext.getCurrentGL().getGL2ES2().glGetShaderInfoLog(shader, maxSize, intBuffer, buffer);
int numBytes = intBuffer.get(0);
byte[] bytes = new byte[numBytes];
buffer.get(bytes);
return new String(bytes);
}
@Override
public void glBindFragDataLocation(int param1, int param2, String param3) {
GLContext.getCurrentGL().getGL2GL3().glBindFragDataLocation(param1, param2, param3);
}
@Override
public void glBindVertexArray(int param1) {
GLContext.getCurrentGL().getGL2ES3().glBindVertexArray(param1);
}
@Override
public void glGenVertexArrays(IntBuffer param1) {
checkLimit(param1);
GLContext.getCurrentGL().getGL2ES3().glGenVertexArrays(param1.limit(), param1);
}
@Override
public void glPatchParameter(int count) {
GLContext.getCurrentGL().getGL3().glPatchParameteri(com.jogamp.opengl.GL3.GL_PATCH_VERTICES, count);
}
@Override
public void glDeleteVertexArrays(IntBuffer arrays) {
checkLimit(arrays);
GLContext.getCurrentGL().getGL2ES3().glDeleteVertexArrays(arrays.limit(), arrays);
}
}

@ -0,0 +1,88 @@
package com.jme3.renderer.jogl;
import com.jme3.renderer.RendererException;
import com.jme3.renderer.opengl.GLExt;
import com.jogamp.opengl.GLContext;
import java.nio.Buffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
public class JoglGLExt implements GLExt {
private static int getLimitBytes(IntBuffer buffer) {
checkLimit(buffer);
return buffer.limit() * 4;
}
private static void checkLimit(Buffer buffer) {
if (buffer == null) {
return;
}
if (buffer.limit() == 0) {
throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
}
if (buffer.remaining() == 0) {
throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
}
}
@Override
public void glBufferData(int target, IntBuffer data, int usage) {
checkLimit(data);
GLContext.getCurrentGL().glBufferData(target, getLimitBytes(data), data, usage);
}
@Override
public void glBufferSubData(int target, long offset, IntBuffer data) {
checkLimit(data);
GLContext.getCurrentGL().glBufferSubData(target, getLimitBytes(data), offset, data);
}
@Override
public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) {
GLContext.getCurrentGL().getGL2ES3().glDrawArraysInstanced(mode, first, count, primcount);
}
@Override
public void glDrawBuffers(IntBuffer bufs) {
checkLimit(bufs);
GLContext.getCurrentGL().getGL2ES2().glDrawBuffers(bufs.limit(), bufs);
}
@Override
public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) {
GLContext.getCurrentGL().getGL2ES3().glDrawElementsInstanced(mode, indices_count, type, indices_buffer_offset, primcount);
}
@Override
public void glGetMultisample(int pname, int index, FloatBuffer val) {
checkLimit(val);
GLContext.getCurrentGL().getGL2ES2().glGetMultisamplefv(pname, index, val);
}
@Override
public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) {
GLContext.getCurrentGL().getGL2ES2().glTexImage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations);
}
@Override
public void glVertexAttribDivisorARB(int index, int divisor) {
GLContext.getCurrentGL().getGL2ES3().glVertexAttribDivisor(index, divisor);
}
@Override
public Object glFenceSync(int condition, int flags) {
return GLContext.getCurrentGL().getGL3ES3().glFenceSync(condition, flags);
}
@Override
public int glClientWaitSync(Object sync, int flags, long timeout) {
return GLContext.getCurrentGL().getGL3ES3().glClientWaitSync(((Long) sync).longValue(), flags, timeout);
}
@Override
public void glDeleteSync(Object sync) {
GLContext.getCurrentGL().getGL3ES3().glDeleteSync(((Long) sync).longValue());
}
}

@ -0,0 +1,97 @@
package com.jme3.renderer.jogl;
import com.jme3.renderer.RendererException;
import com.jme3.renderer.opengl.GLFbo;
import com.jogamp.opengl.GLContext;
import java.nio.Buffer;
import java.nio.IntBuffer;
/**
* Implements GLFbo
*
* @author Kirill Vainer
*/
public class JoglGLFbo implements GLFbo {
private static void checkLimit(Buffer buffer) {
if (buffer == null) {
return;
}
if (buffer.limit() == 0) {
throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
}
if (buffer.remaining() == 0) {
throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
}
}
@Override
public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
GLContext.getCurrentGL().getGL2ES3().glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
@Override
public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
GLContext.getCurrentGL().glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
}
@Override
public void glBindFramebufferEXT(int param1, int param2) {
GLContext.getCurrentGL().glBindFramebuffer(param1, param2);
}
@Override
public void glBindRenderbufferEXT(int param1, int param2) {
GLContext.getCurrentGL().glBindRenderbuffer(param1, param2);
}
@Override
public int glCheckFramebufferStatusEXT(int param1) {
return GLContext.getCurrentGL().glCheckFramebufferStatus(param1);
}
@Override
public void glDeleteFramebuffersEXT(IntBuffer param1) {
checkLimit(param1);
GLContext.getCurrentGL().glDeleteFramebuffers(param1.limit(), param1);
}
@Override
public void glDeleteRenderbuffersEXT(IntBuffer param1) {
checkLimit(param1);
GLContext.getCurrentGL().glDeleteRenderbuffers(param1.limit(), param1);
}
@Override
public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
GLContext.getCurrentGL().glFramebufferRenderbuffer(param1, param2, param3, param4);
}
@Override
public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
GLContext.getCurrentGL().glFramebufferTexture2D(param1, param2, param3, param4, param5);
}
@Override
public void glGenFramebuffersEXT(IntBuffer param1) {
checkLimit(param1);
GLContext.getCurrentGL().glGenFramebuffers(param1.limit(), param1);
}
@Override
public void glGenRenderbuffersEXT(IntBuffer param1) {
checkLimit(param1);
GLContext.getCurrentGL().glGenRenderbuffers(param1.limit(), param1);
}
@Override
public void glGenerateMipmapEXT(int param1) {
GLContext.getCurrentGL().glGenerateMipmap(param1);
}
@Override
public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
GLContext.getCurrentGL().glRenderbufferStorage(param1, param2, param3, param4);
}
}

@ -37,7 +37,7 @@ import com.jme3.input.MouseInput;
import com.jme3.input.TouchInput; import com.jme3.input.TouchInput;
import com.jme3.input.awt.AwtKeyInput; import com.jme3.input.awt.AwtKeyInput;
import com.jme3.input.awt.AwtMouseInput; import com.jme3.input.awt.AwtMouseInput;
import com.jme3.renderer.jogl.JoglRenderer; import com.jme3.system.AppSettings;
import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.AnimatorBase; import com.jogamp.opengl.util.AnimatorBase;
import com.jogamp.opengl.util.FPSAnimator; import com.jogamp.opengl.util.FPSAnimator;
@ -47,15 +47,6 @@ import java.awt.GraphicsEnvironment;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jogamp.opengl.DebugGL2;
import com.jogamp.opengl.DebugGL3;
import com.jogamp.opengl.DebugGL3bc;
import com.jogamp.opengl.DebugGL4;
import com.jogamp.opengl.DebugGL4bc;
import com.jogamp.opengl.DebugGLES1;
import com.jogamp.opengl.DebugGLES2;
import com.jogamp.opengl.DebugGLES3;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLEventListener;
@ -90,9 +81,13 @@ public abstract class JoglAbstractDisplay extends JoglContext implements GLEvent
device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
//FIXME use the settings to know whether to use the max programmable profile GLCapabilities caps;
//then call GLProfile.getMaxProgrammable(true); if (settings.getRenderer().equals(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE)) {
GLCapabilities caps = new GLCapabilities(GLProfile.getMaxFixedFunc(true)); caps = new GLCapabilities(GLProfile.getMaxProgrammable(true));
} else {
caps = new GLCapabilities(GLProfile.getMaxFixedFunc(true));
}
caps.setHardwareAccelerated(true); caps.setHardwareAccelerated(true);
caps.setDoubleBuffered(true); caps.setDoubleBuffered(true);
caps.setStencilBits(settings.getStencilBits()); caps.setStencilBits(settings.getStencilBits());
@ -128,56 +123,8 @@ public abstract class JoglAbstractDisplay extends JoglContext implements GLEvent
canvas.setIgnoreRepaint(true); canvas.setIgnoreRepaint(true);
canvas.addGLEventListener(this); canvas.addGLEventListener(this);
if (settings.getBoolean("GraphicsDebug")) { //FIXME not sure it is the best place to do that
canvas.invoke(false, new GLRunnable() { renderable.set(true);
public boolean run(GLAutoDrawable glad) {
GL gl = glad.getGL();
if (gl.isGLES()) {
if (gl.isGLES1()) {
glad.setGL(new DebugGLES1(gl.getGLES1()));
} else {
if (gl.isGLES2()) {
glad.setGL(new DebugGLES2(gl.getGLES2()));
} else {
if (gl.isGLES3()) {
glad.setGL(new DebugGLES3(gl.getGLES3()));
}
}
}
} else {
if (gl.isGL4bc()) {
glad.setGL(new DebugGL4bc(gl.getGL4bc()));
} else {
if (gl.isGL4()) {
glad.setGL(new DebugGL4(gl.getGL4()));
} else {
if (gl.isGL3bc()) {
glad.setGL(new DebugGL3bc(gl.getGL3bc()));
} else {
if (gl.isGL3()) {
glad.setGL(new DebugGL3(gl.getGL3()));
} else {
if (gl.isGL2()) {
glad.setGL(new DebugGL2(gl.getGL2()));
}
}
}
}
}
}
return true;
}
});
}
renderer = new JoglRenderer();
canvas.invoke(false, new GLRunnable() {
public boolean run(GLAutoDrawable glad) {
renderer.setMainFrameBufferSrgb(settings.getGammaCorrection());
return true;
}
});
} }
protected void startGLCanvas() { protected void startGLCanvas() {
@ -192,9 +139,6 @@ public abstract class JoglAbstractDisplay extends JoglContext implements GLEvent
animator.start(); animator.start();
wasAnimating = true; wasAnimating = true;
//FIXME not sure it is the best place to do that
renderable.set(true);
} }
protected void onCanvasAdded() { protected void onCanvasAdded() {

@ -41,28 +41,34 @@ public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext
private static final Logger logger = Logger.getLogger(JoglCanvas.class.getName()); private static final Logger logger = Logger.getLogger(JoglCanvas.class.getName());
private int width, height; private int width, height;
private boolean runningFirstTime = true;
public JoglCanvas(){ public JoglCanvas(){
super(); super();
initGLCanvas(); initGLCanvas();
} }
public Type getType() { @Override
public Type getType() {
return Type.Canvas; return Type.Canvas;
} }
public void setTitle(String title) { @Override
public void setTitle(String title) {
} }
public void restart() { @Override
public void restart() {
} }
public void create(boolean waitFor){ @Override
public void create(boolean waitFor){
if (waitFor) if (waitFor)
waitFor(true); waitFor(true);
} }
public void destroy(boolean waitFor){ @Override
public void destroy(boolean waitFor){
if (waitFor) if (waitFor)
waitFor(false); waitFor(false);
if (animator.isAnimating()) if (animator.isAnimating())
@ -81,13 +87,20 @@ public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext
startGLCanvas(); startGLCanvas();
} }
public void init(GLAutoDrawable drawable) { @Override
public void init(GLAutoDrawable drawable) {
canvas.requestFocus(); canvas.requestFocus();
super.internalCreate(); super.internalCreate();
logger.fine("Display created."); logger.fine("Display created.");
renderer.initialize(); // At this point, the OpenGL context is active.
if (runningFirstTime){
// THIS is the part that creates the renderer.
// It must always be called, now that we have the pbuffer workaround.
initContextFirstTime();
runningFirstTime = false;
}
listener.initialize(); listener.initialize();
} }
@ -97,7 +110,8 @@ public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext
super.startGLCanvas(); super.startGLCanvas();
} }
public void display(GLAutoDrawable glad) { @Override
public void display(GLAutoDrawable glad) {
if (!created.get() && renderer != null){ if (!created.get() && renderer != null){
listener.destroy(); listener.destroy();
logger.fine("Canvas destroyed."); logger.fine("Canvas destroyed.");
@ -129,7 +143,8 @@ public class JoglCanvas extends JoglAbstractDisplay implements JmeCanvasContext
} }
public Canvas getCanvas() { @Override
public Canvas getCanvas() {
return canvas; return canvas;
} }

@ -36,17 +36,32 @@ import com.jme3.input.JoyInput;
import com.jme3.input.KeyInput; import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput; import com.jme3.input.MouseInput;
import com.jme3.renderer.Renderer; import com.jme3.renderer.Renderer;
import com.jme3.renderer.jogl.JoglRenderer; import com.jme3.renderer.RendererException;
import com.jme3.renderer.jogl.JoglGL;
import com.jme3.renderer.jogl.JoglGLExt;
import com.jme3.renderer.jogl.JoglGLFbo;
import com.jme3.renderer.opengl.GL2;
import com.jme3.renderer.opengl.GL3;
import com.jme3.renderer.opengl.GL4;
import com.jme3.renderer.opengl.GLDebugDesktop;
import com.jme3.renderer.opengl.GLExt;
import com.jme3.renderer.opengl.GLFbo;
import com.jme3.renderer.opengl.GLRenderer;
import com.jme3.renderer.opengl.GLTiming;
import com.jme3.renderer.opengl.GLTimingState;
import com.jme3.renderer.opengl.GLTracer;
import com.jme3.system.AppSettings; import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext; import com.jme3.system.JmeContext;
import com.jme3.system.NanoTimer; import com.jme3.system.NanoTimer;
import com.jme3.system.NativeLibraryLoader; import com.jme3.system.NativeLibraryLoader;
import com.jme3.system.SystemListener; import com.jme3.system.SystemListener;
import com.jme3.system.Timer; import com.jme3.system.Timer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jogamp.opengl.GL; import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2GL3; import com.jogamp.opengl.GL2GL3;
import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLContext;
@ -62,7 +77,7 @@ public abstract class JoglContext implements JmeContext {
protected final Object createdLock = new Object(); protected final Object createdLock = new Object();
protected AppSettings settings = new AppSettings(true); protected AppSettings settings = new AppSettings(true);
protected JoglRenderer renderer; protected Renderer renderer;
protected Timer timer; protected Timer timer;
protected SystemListener listener; protected SystemListener listener;
@ -77,43 +92,53 @@ public abstract class JoglContext implements JmeContext {
} }
} }
public void setSystemListener(SystemListener listener){ @Override
public void setSystemListener(SystemListener listener){
this.listener = listener; this.listener = listener;
} }
public void setSettings(AppSettings settings) { @Override
public void setSettings(AppSettings settings) {
this.settings.copyFrom(settings); this.settings.copyFrom(settings);
} }
public boolean isRenderable(){ @Override
public boolean isRenderable(){
return renderable.get(); return renderable.get();
} }
public AppSettings getSettings() { @Override
public AppSettings getSettings() {
return settings; return settings;
} }
public Renderer getRenderer() { @Override
public Renderer getRenderer() {
return renderer; return renderer;
} }
public MouseInput getMouseInput() { @Override
public MouseInput getMouseInput() {
return mouseInput; return mouseInput;
} }
public KeyInput getKeyInput() { @Override
public KeyInput getKeyInput() {
return keyInput; return keyInput;
} }
public JoyInput getJoyInput() { @Override
public JoyInput getJoyInput() {
return joyInput; return joyInput;
} }
public Timer getTimer() { @Override
public Timer getTimer() {
return timer; return timer;
} }
public boolean isCreated() { @Override
public boolean isCreated() {
return created.get(); return created.get();
} }
@ -136,12 +161,75 @@ public abstract class JoglContext implements JmeContext {
} }
} }
public void internalCreate() { protected void initContextFirstTime(){
if (GLContext.getCurrent().getGLVersionNumber().getMajor() < 2) {
throw new RendererException("OpenGL 2.0 or higher is " +
"required for jMonkeyEngine");
}
if (settings.getRenderer().startsWith("JOGL")) {
com.jme3.renderer.opengl.GL gl = new JoglGL();
GLExt glext = new JoglGLExt();
GLFbo glfbo = new JoglGLFbo();
if (settings.getBoolean("GraphicsDebug")) {
gl = new GLDebugDesktop(gl, glext, glfbo);
glext = (GLExt) gl;
glfbo = (GLFbo) gl;
}
if (settings.getBoolean("GraphicsTiming")) {
GLTimingState timingState = new GLTimingState();
gl = (com.jme3.renderer.opengl.GL) GLTiming.createGLTiming(gl, timingState, GL.class, GL2.class, GL3.class, GL4.class);
glext = (GLExt) GLTiming.createGLTiming(glext, timingState, GLExt.class);
glfbo = (GLFbo) GLTiming.createGLTiming(glfbo, timingState, GLFbo.class);
}
if (settings.getBoolean("GraphicsTrace")) {
gl = (com.jme3.renderer.opengl.GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class);
glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class);
glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class);
}
renderer = new GLRenderer(gl, glext, glfbo);
renderer.initialize();
} else {
throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer());
}
if (GLContext.getCurrentGL().isExtensionAvailable("GL_ARB_debug_output") && settings.getBoolean("GraphicsDebug")) {
GLContext.getCurrent().enableGLDebugMessage(true);
GLContext.getCurrent().addGLDebugListener(new JoglGLDebugOutputHandler());
}
renderer.setMainFrameBufferSrgb(settings.getGammaCorrection());
renderer.setLinearizeSrgbImages(settings.getGammaCorrection());
// Init input
if (keyInput != null) {
keyInput.initialize();
}
if (mouseInput != null) {
mouseInput.initialize();
}
if (joyInput != null) {
joyInput.initialize();
}
}
public void internalCreate() {
timer = new NanoTimer(); timer = new NanoTimer();
synchronized (createdLock){ synchronized (createdLock){
created.set(true); created.set(true);
createdLock.notifyAll(); createdLock.notifyAll();
} }
if (renderable.get()){
initContextFirstTime();
} else {
assert getType() == Type.Canvas;
}
} }
protected void internalDestroy() { protected void internalDestroy() {

@ -0,0 +1,80 @@
/*
* Copyright (c) 2009-2015 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.system.jogl;
import java.util.HashMap;
import com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GLDebugListener;
import com.jogamp.opengl.GLDebugMessage;
class JoglGLDebugOutputHandler implements GLDebugListener {
private static final HashMap<Integer, String> constMap = new HashMap<Integer, String>();
private static final String MESSAGE_FORMAT =
"[JME3] OpenGL debug message\r\n" +
" ID: %d\r\n" +
" Source: %s\r\n" +
" Type: %s\r\n" +
" Severity: %s\r\n" +
" Message: %s";
static {
constMap.put(GL2ES2.GL_DEBUG_SOURCE_API, "API");
constMap.put(GL2ES2.GL_DEBUG_SOURCE_APPLICATION, "APPLICATION");
constMap.put(GL2ES2.GL_DEBUG_SOURCE_OTHER, "OTHER");
constMap.put(GL2ES2.GL_DEBUG_SOURCE_SHADER_COMPILER, "SHADER_COMPILER");
constMap.put(GL2ES2.GL_DEBUG_SOURCE_THIRD_PARTY, "THIRD_PARTY");
constMap.put(GL2ES2.GL_DEBUG_SOURCE_WINDOW_SYSTEM, "WINDOW_SYSTEM");
constMap.put(GL2ES2.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, "DEPRECATED_BEHAVIOR");
constMap.put(GL2ES2.GL_DEBUG_TYPE_ERROR, "ERROR");
constMap.put(GL2ES2.GL_DEBUG_TYPE_OTHER, "OTHER");
constMap.put(GL2ES2.GL_DEBUG_TYPE_PERFORMANCE, "PERFORMANCE");
constMap.put(GL2ES2.GL_DEBUG_TYPE_PORTABILITY, "PORTABILITY");
constMap.put(GL2ES2.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, "UNDEFINED_BEHAVIOR");
constMap.put(GL2ES2.GL_DEBUG_SEVERITY_HIGH, "HIGH");
constMap.put(GL2ES2.GL_DEBUG_SEVERITY_MEDIUM, "MEDIUM");
constMap.put(GL2ES2.GL_DEBUG_SEVERITY_LOW, "LOW");
}
@Override
public void messageSent(GLDebugMessage event) {
String sourceStr = constMap.get(event.getDbgSource());
String typeStr = constMap.get(event.getDbgType());
String severityStr = constMap.get(event.getDbgSeverity());
System.err.println(String.format(MESSAGE_FORMAT, event.getDbgId(), sourceStr, typeStr, severityStr, event.getDbgMsg()));
}
}

@ -37,7 +37,7 @@ import com.jme3.input.MouseInput;
import com.jme3.input.TouchInput; import com.jme3.input.TouchInput;
import com.jme3.input.jogl.NewtKeyInput; import com.jme3.input.jogl.NewtKeyInput;
import com.jme3.input.jogl.NewtMouseInput; import com.jme3.input.jogl.NewtMouseInput;
import com.jme3.renderer.jogl.JoglRenderer; import com.jme3.system.AppSettings;
import com.jogamp.newt.opengl.GLWindow; import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.AnimatorBase; import com.jogamp.opengl.util.AnimatorBase;
@ -46,15 +46,6 @@ import com.jogamp.opengl.util.FPSAnimator;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jogamp.opengl.DebugGL2;
import com.jogamp.opengl.DebugGL3;
import com.jogamp.opengl.DebugGL3bc;
import com.jogamp.opengl.DebugGL4;
import com.jogamp.opengl.DebugGL4bc;
import com.jogamp.opengl.DebugGLES1;
import com.jogamp.opengl.DebugGLES2;
import com.jogamp.opengl.DebugGLES3;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLEventListener;
@ -83,10 +74,12 @@ public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLE
protected void initGLCanvas() { protected void initGLCanvas() {
loadNatives(); loadNatives();
//FIXME use the settings to know whether to use the max programmable profile GLCapabilities caps;
//then call GLProfile.getMaxProgrammable(true); if (settings.getRenderer().equals(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE)) {
//FIXME use the default profile only on embedded devices caps = new GLCapabilities(GLProfile.getMaxProgrammable(true));
GLCapabilities caps = new GLCapabilities(GLProfile.getDefault()); } else {
caps = new GLCapabilities(GLProfile.getMaxFixedFunc(true));
}
caps.setHardwareAccelerated(true); caps.setHardwareAccelerated(true);
caps.setDoubleBuffered(true); caps.setDoubleBuffered(true);
caps.setStencilBits(settings.getStencilBits()); caps.setStencilBits(settings.getStencilBits());
@ -107,56 +100,8 @@ public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLE
canvas.setSize(settings.getWidth(), settings.getHeight()); canvas.setSize(settings.getWidth(), settings.getHeight());
canvas.addGLEventListener(this); canvas.addGLEventListener(this);
if (settings.getBoolean("GraphicsDebug")) { //FIXME not sure it is the best place to do that
canvas.invoke(false, new GLRunnable() { renderable.set(true);
public boolean run(GLAutoDrawable glad) {
GL gl = glad.getGL();
if (gl.isGLES()) {
if (gl.isGLES1()) {
glad.setGL(new DebugGLES1(gl.getGLES1()));
} else {
if (gl.isGLES2()) {
glad.setGL(new DebugGLES2(gl.getGLES2()));
} else {
if (gl.isGLES3()) {
glad.setGL(new DebugGLES3(gl.getGLES3()));
}
}
}
} else {
if (gl.isGL4bc()) {
glad.setGL(new DebugGL4bc(gl.getGL4bc()));
} else {
if (gl.isGL4()) {
glad.setGL(new DebugGL4(gl.getGL4()));
} else {
if (gl.isGL3bc()) {
glad.setGL(new DebugGL3bc(gl.getGL3bc()));
} else {
if (gl.isGL3()) {
glad.setGL(new DebugGL3(gl.getGL3()));
} else {
if (gl.isGL2()) {
glad.setGL(new DebugGL2(gl.getGL2()));
}
}
}
}
}
}
return true;
}
});
}
renderer = new JoglRenderer();
canvas.invoke(false, new GLRunnable() {
public boolean run(GLAutoDrawable glad) {
renderer.setMainFrameBufferSrgb(settings.getGammaCorrection());
return true;
}
});
} }
protected void startGLCanvas() { protected void startGLCanvas() {
@ -171,9 +116,6 @@ public abstract class JoglNewtAbstractDisplay extends JoglContext implements GLE
animator.start(); animator.start();
wasAnimating = true; wasAnimating = true;
//FIXME not sure it is the best place to do that
renderable.set(true);
} }
protected void onCanvasAdded() { protected void onCanvasAdded() {

@ -41,6 +41,7 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas
private static final Logger logger = Logger.getLogger(JoglNewtCanvas.class.getName()); private static final Logger logger = Logger.getLogger(JoglNewtCanvas.class.getName());
private int width, height; private int width, height;
private boolean runningFirstTime = true;
private NewtCanvasAWT newtAwtCanvas; private NewtCanvasAWT newtAwtCanvas;
@ -53,7 +54,9 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas
protected final void initGLCanvas() { protected final void initGLCanvas() {
super.initGLCanvas(); super.initGLCanvas();
newtAwtCanvas = new NewtCanvasAWT(canvas) { newtAwtCanvas = new NewtCanvasAWT(canvas) {
@Override private static final long serialVersionUID = 1L;
@Override
public void addNotify() { public void addNotify() {
super.addNotify(); super.addNotify();
onCanvasAdded(); onCanvasAdded();
@ -67,22 +70,27 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas
}; };
} }
public Type getType() { @Override
public Type getType() {
return Type.Canvas; return Type.Canvas;
} }
public void setTitle(String title) { @Override
public void setTitle(String title) {
} }
public void restart() { @Override
public void restart() {
} }
public void create(boolean waitFor){ @Override
public void create(boolean waitFor){
if (waitFor) if (waitFor)
waitFor(true); waitFor(true);
} }
public void destroy(boolean waitFor){ @Override
public void destroy(boolean waitFor){
if (waitFor) if (waitFor)
waitFor(false); waitFor(false);
if (animator.isAnimating()) if (animator.isAnimating())
@ -101,13 +109,20 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas
startGLCanvas(); startGLCanvas();
} }
public void init(GLAutoDrawable drawable) { @Override
public void init(GLAutoDrawable drawable) {
canvas.requestFocus(); canvas.requestFocus();
super.internalCreate(); super.internalCreate();
logger.fine("Display created."); logger.fine("Display created.");
renderer.initialize(); // At this point, the OpenGL context is active.
if (runningFirstTime){
// THIS is the part that creates the renderer.
// It must always be called, now that we have the pbuffer workaround.
initContextFirstTime();
runningFirstTime = false;
}
listener.initialize(); listener.initialize();
} }
@ -117,7 +132,8 @@ public class JoglNewtCanvas extends JoglNewtAbstractDisplay implements JmeCanvas
super.startGLCanvas(); super.startGLCanvas();
} }
public void display(GLAutoDrawable glad) { @Override
public void display(GLAutoDrawable glad) {
if (!created.get() && renderer != null){ if (!created.get() && renderer != null){
listener.destroy(); listener.destroy();
logger.fine("Canvas destroyed."); logger.fine("Canvas destroyed.");

@ -173,6 +173,8 @@ public class JoglNewtDisplay extends JoglNewtAbstractDisplay {
if (waitFor){ if (waitFor){
waitFor(false); waitFor(false);
} }
if (animator.isAnimating())
animator.stop();
} }
public void restart() { public void restart() {

@ -37,16 +37,18 @@ import com.jme3.input.MouseInput;
import com.jme3.input.TouchInput; import com.jme3.input.TouchInput;
import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyKeyInput;
import com.jme3.input.dummy.DummyMouseInput; import com.jme3.input.dummy.DummyMouseInput;
import com.jogamp.newt.NewtVersion;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jogamp.opengl.GL; import com.jogamp.opengl.GL;
import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLContext;
import com.jogamp.opengl.GLDrawableFactory; import com.jogamp.opengl.GLDrawableFactory;
import com.jogamp.opengl.GLOffscreenAutoDrawable; import com.jogamp.opengl.GLOffscreenAutoDrawable;
import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.JoglVersion;
public class JoglOffscreenBuffer extends JoglContext implements Runnable { public class JoglOffscreenBuffer extends JoglContext implements Runnable {
@ -123,7 +125,7 @@ public class JoglOffscreenBuffer extends JoglContext implements Runnable {
@Override @Override
public void run(){ public void run(){
loadNatives(); loadNatives();
logger.log(Level.FINE, "Using JOGL {0}", NewtVersion.getInstance().getImplementationVersion()); logger.log(Level.FINE, "Using JOGL {0}", JoglVersion.getInstance().getImplementationVersion());
initInThread(); initInThread();
while (!needClose.get()){ while (!needClose.get()){
runLoop(); runLoop();

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

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

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

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

@ -40,7 +40,6 @@ import com.jme3.input.lwjgl.JInputJoyInput;
import com.jme3.input.lwjgl.LwjglKeyInput; import com.jme3.input.lwjgl.LwjglKeyInput;
import com.jme3.input.lwjgl.LwjglMouseInput; import com.jme3.input.lwjgl.LwjglMouseInput;
import com.jme3.system.AppSettings; import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext.Type;
import com.jme3.system.JmeSystem; import com.jme3.system.JmeSystem;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;

@ -52,12 +52,8 @@ import com.jme3.renderer.opengl.GLRenderer;
import com.jme3.renderer.opengl.GLTiming; import com.jme3.renderer.opengl.GLTiming;
import com.jme3.renderer.opengl.GLTimingState; import com.jme3.renderer.opengl.GLTimingState;
import com.jme3.renderer.opengl.GLTracer; import com.jme3.renderer.opengl.GLTracer;
import com.jme3.system.AppSettings; import com.jme3.system.*;
import com.jme3.system.JmeContext;
import com.jme3.system.JmeSystem;
import com.jme3.system.NativeLibraryLoader;
import com.jme3.system.SystemListener;
import com.jme3.system.Timer;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -166,7 +162,6 @@ public abstract class LwjglContext implements JmeContext {
} }
} }
} }
protected void loadNatives() { protected void loadNatives() {
if (JmeSystem.isLowPermissions()) { if (JmeSystem.isLowPermissions()) {
return; return;

@ -73,6 +73,7 @@ class LwjglGLDebugOutputHandler implements ARBDebugOutputCallback.Handler {
String severityStr = constMap.get(severity); String severityStr = constMap.get(severity);
System.err.println(String.format(MESSAGE_FORMAT, id, sourceStr, typeStr, severityStr, message)); System.err.println(String.format(MESSAGE_FORMAT, id, sourceStr, typeStr, severityStr, message));
Thread.dumpStack();
} }
} }

@ -0,0 +1,15 @@
if (!hasProperty('mainClass')) {
ext.mainClass = ''
}
repositories {
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
}
dependencies {
compile project(':jme3-core')
compile project(':jme3-desktop')
compile files('lib/lwjgl-3.0.0b-35.jar', 'lib/lwjgl-3.0.0b-35-natives.jar')
}

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

Loading…
Cancel
Save