Merge remote-tracking branch 'origin/master'

cleanup_build_scripts
iwgeric 9 years ago
commit e837336116
  1. 142
      .gitignore
  2. 6
      common.gradle
  3. 136
      jme3-android/src/main/java/com/jme3/asset/AndroidImageInfo.java
  4. 533
      jme3-android/src/main/java/com/jme3/audio/android/AndroidMediaPlayerAudioRenderer.java
  5. 5
      jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
  6. 591
      jme3-android/src/main/java/com/jme3/renderer/android/TextureUtil.java
  7. 23
      jme3-android/src/main/java/com/jme3/texture/plugins/AndroidImageLoader.java
  8. 2
      jme3-android/src/main/java/jme3test/android/SimpleTexturedTest.java
  9. 21
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/materials/MaterialContext.java
  10. 37
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/textures/CombinedTexture.java
  11. 34
      jme3-bullet-native/build.gradle
  12. 23
      jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java
  13. 42
      jme3-core/src/main/java/com/jme3/animation/SpatialAnimation.java
  14. 3
      jme3-core/src/main/java/com/jme3/app/SimpleApplication.java
  15. 9
      jme3-core/src/main/java/com/jme3/asset/AssetManager.java
  16. 33
      jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java
  17. 36
      jme3-core/src/main/java/com/jme3/asset/TextureKey.java
  18. 104
      jme3-core/src/main/java/com/jme3/audio/AudioListenerState.java
  19. 44
      jme3-core/src/main/java/com/jme3/audio/AudioNode.java
  20. 6
      jme3-core/src/main/java/com/jme3/audio/Listener.java
  21. 9
      jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java
  22. 3
      jme3-core/src/main/java/com/jme3/cinematic/MotionPath.java
  23. 5
      jme3-core/src/main/java/com/jme3/effect/ParticleMesh.java
  24. 6
      jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java
  25. 4
      jme3-core/src/main/java/com/jme3/material/MatParam.java
  26. 151
      jme3-core/src/main/java/com/jme3/material/MatParamOverride.java
  27. 28
      jme3-core/src/main/java/com/jme3/material/MatParamTexture.java
  28. 623
      jme3-core/src/main/java/com/jme3/material/Material.java
  29. 47
      jme3-core/src/main/java/com/jme3/material/MaterialDef.java
  30. 447
      jme3-core/src/main/java/com/jme3/material/RenderState.java
  31. 253
      jme3-core/src/main/java/com/jme3/material/Technique.java
  32. 305
      jme3-core/src/main/java/com/jme3/material/TechniqueDef.java
  33. 97
      jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java
  34. 178
      jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java
  35. 218
      jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java
  36. 182
      jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java
  37. 97
      jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java
  38. 12
      jme3-core/src/main/java/com/jme3/renderer/Camera.java
  39. 33
      jme3-core/src/main/java/com/jme3/renderer/Limits.java
  40. 26
      jme3-core/src/main/java/com/jme3/renderer/RenderContext.java
  41. 127
      jme3-core/src/main/java/com/jme3/renderer/RenderManager.java
  42. 24
      jme3-core/src/main/java/com/jme3/renderer/Renderer.java
  43. 280
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java
  44. 5
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java
  45. 5
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java
  46. 113
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  47. 10
      jme3-core/src/main/java/com/jme3/renderer/queue/GeometryList.java
  48. 5
      jme3-core/src/main/java/com/jme3/renderer/queue/OpaqueComparator.java
  49. 25
      jme3-core/src/main/java/com/jme3/scene/Mesh.java
  50. 44
      jme3-core/src/main/java/com/jme3/scene/Node.java
  51. 160
      jme3-core/src/main/java/com/jme3/scene/Spatial.java
  52. 4
      jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java
  53. 8
      jme3-core/src/main/java/com/jme3/scene/debug/WireBox.java
  54. 465
      jme3-core/src/main/java/com/jme3/shader/DefineList.java
  55. 79
      jme3-core/src/main/java/com/jme3/shader/Shader.java
  56. 47
      jme3-core/src/main/java/com/jme3/shader/ShaderGenerator.java
  57. 201
      jme3-core/src/main/java/com/jme3/shader/ShaderKey.java
  58. 115
      jme3-core/src/main/java/com/jme3/shader/Uniform.java
  59. 5
      jme3-core/src/main/java/com/jme3/shader/UniformBindingManager.java
  60. 9
      jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java
  61. 9
      jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java
  62. 7
      jme3-core/src/main/java/com/jme3/system/NullContext.java
  63. 20
      jme3-core/src/main/java/com/jme3/system/NullRenderer.java
  64. 2
      jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java
  65. 4
      jme3-core/src/main/java/com/jme3/util/clone/Cloner.java
  66. 21
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md
  67. 4
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md
  68. 3
      jme3-core/src/main/resources/com/jme3/asset/General.cfg
  69. 108
      jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
  70. 9
      jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java
  71. 3
      jme3-core/src/plugins/java/com/jme3/scene/plugins/MTLLoader.java
  72. 28
      jme3-core/src/test/java/com/jme3/asset/LoadShaderSourceTest.java
  73. 600
      jme3-core/src/test/java/com/jme3/material/MaterialMatParamOverrideTest.java
  74. 171
      jme3-core/src/test/java/com/jme3/material/MaterialTest.java
  75. 30
      jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java
  76. 100
      jme3-core/src/test/java/com/jme3/material/plugins/LoadJ3mdTest.java
  77. 38
      jme3-core/src/test/java/com/jme3/math/FastMathTest.java
  78. 343
      jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java
  79. 173
      jme3-core/src/test/java/com/jme3/scene/MPOTestUtils.java
  80. 278
      jme3-core/src/test/java/com/jme3/scene/SceneMatParamOverrideTest.java
  81. 300
      jme3-core/src/test/java/com/jme3/shader/DefineListTest.java
  82. 72
      jme3-core/src/test/java/com/jme3/system/MockJmeSystemDelegate.java
  83. 42
      jme3-core/src/test/java/com/jme3/system/TestUtil.java
  84. 6
      jme3-core/src/test/resources/no-default-technique.j3md
  85. 8
      jme3-core/src/test/resources/no-shader-specified.j3md
  86. 10
      jme3-core/src/test/resources/same-name-technique.j3md
  87. 34
      jme3-core/src/test/resources/testMatDef.j3md
  88. 1
      jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java
  89. 24
      jme3-core/src/tools/java/jme3tools/shadercheck/ShaderCheck.java
  90. 42
      jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java
  91. 359
      jme3-desktop/src/main/java/com/jme3/system/Natives.java
  92. 4
      jme3-examples/build.gradle
  93. 3
      jme3-examples/src/main/java/jme3test/animation/TestCameraMotionPath.java
  94. 3
      jme3-examples/src/main/java/jme3test/animation/TestCinematic.java
  95. 3
      jme3-examples/src/main/java/jme3test/animation/TestMotionPath.java
  96. 2
      jme3-examples/src/main/java/jme3test/app/TestAppStateLifeCycle.java
  97. 2
      jme3-examples/src/main/java/jme3test/app/TestBareBonesApp.java
  98. 156
      jme3-examples/src/main/java/jme3test/app/TestNativeLoader.java
  99. 2
      jme3-examples/src/main/java/jme3test/app/TestReleaseDirectMemory.java
  100. 2
      jme3-examples/src/main/java/jme3test/audio/TestAmbient.java
  101. Some files were not shown because too many files have changed in this diff Show More

142
.gitignore vendored

@ -1,34 +1,22 @@
**/nbproject/private/
/.gradle/ /.gradle/
/.nb-gradle/private/ /.nb-gradle/
/.nb-gradle/profiles/private/
/.idea/ /.idea/
/dist/ /dist/
/build/ /build/
/bin/
/netbeans/ /netbeans/
/sdk/jdks/local/ /.classpath
/jme3-core/build/ /.project
/.settings
*.dll
*.so
*.jnilib
*.dylib
*.iml
.DS_Store
/jme3-core/src/main/resources/com/jme3/system/version.properties /jme3-core/src/main/resources/com/jme3/system/version.properties
/jme3-plugins/build/ /jme3-*/build/
/jme3-desktop/build/
/jme3-android-native/build/
/jme3-android/build/
/jme3-android-examples/build/
/jme3-blender/build/
/jme3-effects/build/
/jme3-bullet/build/
/jme3-terrain/build/
/jme3-bullet-native/build/
/jme3-bullet-native-android/build/
/jme3-jogg/build/
/jme3-jbullet/build/
/jme3-lwjgl/build/
/jme3-networking/build/
/jme3-niftygui/build/
/jme3-testdata/build/
/jme3-examples/build/
/jme3-jogl/build/
/jme3-ios/build/
/jme3-gl-autogen/build/
/jme3-bullet-native/bullet.zip /jme3-bullet-native/bullet.zip
/jme3-bullet-native/bullet-2.82-r2704/ /jme3-bullet-native/bullet-2.82-r2704/
/jme3-android-native/openal-soft/ /jme3-android-native/openal-soft/
@ -38,113 +26,9 @@
/jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.h /jme3-android-native/src/native/jme_decode/com_jme3_audio_plugins_NativeVorbisFile.h
/jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.h /jme3-android-native/src/native/jme_decode/com_jme3_texture_plugins_AndroidNativeImageLoader.h
/jme3-android-native/stb_image.h /jme3-android-native/stb_image.h
/sdk/jme3-tests-template/src/com/jme3/gde/templates/tests/JmeTestsProject.zip
/sdk/jme3-tests-template/src/com/jme3/gde/templates/tests/JME3TestsAndroidProject.zip
/sdk/jme3-project-testdata/release/
/sdk/JME3TestsTemplateAndroid/src/jme3test/
/sdk/JME3TestsTemplate/src/jme3test/
/sdk/build/
/sdk/jme3-core-baselibs/release/
/sdk/jme3-core-libraries/release/
/sdk/jme3-project-baselibs/release/
/sdk/jme3-project-libraries/release/
/sdk/jme3-codepalette/build/
/sdk/jme3-core-libraries/build/
/sdk/jme3-code-check/build/
/sdk/jme3-core-baselibs/build/
/sdk/jme3-documentation/build/
/sdk/jme3-core-updatecenters/build/
/sdk/jme3-project-testdata/build/
/sdk/jme3-project-libraries/build/
/sdk/jme3-project-baselibs/build/
/sdk/jme3-templates/build/
/sdk/jme3-texture-editor/build/
/sdk/jme3-tests-template/build/
/sdk/jme3-upgrader/build/
/sdk/jme3-core/build/
/sdk/jme3-obfuscate/build/
/sdk/jme3-gui/build/
/sdk/jme3-cinematics/build/
/sdk/jme3-terrain-editor/build/
/sdk/jme3-lwjgl-applet/build/
/sdk/jme3-blender/build/
/sdk/jme3-navmesh-gen/build/
/sdk/jme3-angelfont/build/
/sdk/jme3-materialeditor/build/
/sdk/jme3-android/build/
/sdk/jme3-desktop-executables/build/
/sdk/jme3-ogrexml/build/
/sdk/jme3-ogretools/build/
/sdk/jme3-scenecomposer/build/
/sdk/jme3-assetpack-support/build/
/sdk/jme3-model-importer/build/
/sdk/jme3-wavefront/build/
/sdk/jme3-vehicle-creator/build/
/sdk/jme3-welcome-screen/build/
/sdk/jme3-glsl-support/build/
/sdk/jme3-dark-laf/build/
/sdk/nbproject/private/
/sdk/jme3-scenecomposer/nbproject/private/
/sdk/jme3-core/nbproject/private/
/sdk/jme3-core-baselibs/nbproject/private/
/sdk/jme3-welcome-screen/nbproject/private/
/sdk/jme3-lwjgl-applet/nbproject/private/
/sdk/jme3-ogrexml/nbproject/private/
/sdk/jme3-upgrader/nbproject/private/
/sdk/jme3-obfuscate/nbproject/private/
/sdk/jme3-navmesh-gen/nbproject/private/
/sdk/jme3-wavefront/nbproject/private/
/sdk/jme3-project-libraries/nbproject/private/
/sdk/jme3-ogretools/nbproject/private/
/sdk/jme3-assetpack-support/nbproject/private/
/sdk/jme3-cinematics/nbproject/private/
/sdk/jme3-model-importer/nbproject/private/
/sdk/jme3-desktop-executables/nbproject/private/
/sdk/jme3-glsl-support/nbproject/private/
/sdk/jme3-android/nbproject/private/
/sdk/jme3-angelfont/nbproject/private/
/sdk/jme3-codepalette/nbproject/private/
/sdk/jme3-documentation/nbproject/private/
/sdk/jme3-vehicle-creator/nbproject/private/
/sdk/jme3-code-check/nbproject/private/
/sdk/jme3-blender/nbproject/private/
/sdk/jme3-core-libraries/nbproject/private/
/sdk/jme3-core-updatecenters/nbproject/private/
/sdk/jme3-gui/nbproject/private/
/sdk/jme3-materialeditor/nbproject/private/
/sdk/jme3-project-baselibs/nbproject/private/
/sdk/jme3-project-testdata/nbproject/private/
/sdk/jme3-templates/nbproject/private/
/sdk/jme3-terrain-editor/nbproject/private/
/sdk/jme3-tests-template/nbproject/private/
/sdk/jme3-texture-editor/nbproject/private/
/sdk/JME3TestsTemplate/nbproject/private/
/sdk/JME3TestsTemplateAndroid/nbproject/private/
/bin
/.classpath
/.project
/.settings
*.dll
*.so
*.jnilib
*.dylib
*.iml
.DS_Store
/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
!/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib !/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib
!/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib !/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib
!/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so !/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so
!/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so !/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so
/.nb-gradle/
/sdk/ant-jme/nbproject/private/
/sdk/nbi/stub/ext/engine/nbproject/private/
/sdk/nbi/stub/ext/components/products/jdk/nbproject/private/
/sdk/nbi/stub/ext/components/products/blender/nbproject/private/
/sdk/nbi/stub/ext/components/products/helloworld/nbproject/private/
/sdk/BasicGameTemplate/nbproject/private/
/sdk/nbi/stub/ext/components/products/jdk/build/
/sdk/nbi/stub/ext/components/products/jdk/dist/
/sdk/jme3-dark-laf/nbproject/private/
jme3-lwjgl3/build/

@ -51,6 +51,12 @@ javadoc {
} }
} }
test {
testLogging {
exceptionFormat = 'full'
}
}
task sourcesJar(type: Jar, dependsOn: classes, description: 'Creates a jar from the source files.') { task sourcesJar(type: Jar, dependsOn: classes, description: 'Creates a jar from the source files.') {
classifier = 'sources' classifier = 'sources'
from sourceSets*.allSource from sourceSets*.allSource

@ -1,136 +0,0 @@
package com.jme3.asset;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import com.jme3.math.ColorRGBA;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.texture.image.ImageRaster;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <code>AndroidImageInfo</code> is set in a jME3 image via the {@link Image#setEfficentData(java.lang.Object) }
* method to retrieve a {@link Bitmap} when it is needed by the renderer.
* User code may extend <code>AndroidImageInfo</code> and provide their own implementation of the
* {@link AndroidImageInfo#loadBitmap()} method to acquire a bitmap by their own means.
*
* @author Kirill Vainer
*/
@Deprecated
public class AndroidImageInfo extends ImageRaster {
private static final Logger logger = Logger.getLogger(AndroidImageInfo.class.getName());
protected AssetInfo assetInfo;
protected Bitmap bitmap;
protected Format format;
public AndroidImageInfo(AssetInfo assetInfo) {
this.assetInfo = assetInfo;
}
public Bitmap getBitmap(){
if (bitmap == null || bitmap.isRecycled()){
try {
loadBitmap();
} catch (IOException ex) {
// If called first inside AssetManager, the error will propagate
// correctly. Assuming that if the first calls succeeds
// then subsequent calls will as well.
throw new AssetLoadException("Failed to load image " + assetInfo.getKey(), ex);
}
}
return bitmap;
}
public void notifyBitmapUploaded() {
// Default function is to recycle the bitmap.
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
logger.log(Level.FINE, "Bitmap was deleted. ");
}
}
public Format getFormat(){
return format;
}
@Override
public int getWidth() {
return getBitmap().getWidth();
}
@Override
public int getHeight() {
return getBitmap().getHeight();
}
@Override
public void setPixel(int x, int y, ColorRGBA color) {
getBitmap().setPixel(x, y, color.asIntARGB());
}
@Override
public ColorRGBA getPixel(int x, int y, ColorRGBA store) {
if (store == null) {
store = new ColorRGBA();
}
store.fromIntARGB(getBitmap().getPixel(x, y));
return store;
}
/**
* Loads the bitmap directly from the asset info, possibly updating
* or creating the image object.
*/
protected void loadBitmap() throws IOException{
InputStream in = null;
try {
in = assetInfo.openStream();
bitmap = BitmapFactory.decodeStream(in);
if (bitmap == null) {
throw new IOException("Failed to load image: " + assetInfo.getKey().getName());
}
} finally {
if (in != null) {
in.close();
}
}
switch (bitmap.getConfig()) {
case ALPHA_8:
format = Image.Format.Alpha8;
break;
case ARGB_8888:
format = Image.Format.RGBA8;
break;
case RGB_565:
format = Image.Format.RGB565;
break;
default:
// This should still work as long
// as renderer doesn't check format
// but just loads bitmap directly.
format = null;
}
TextureKey texKey = (TextureKey) assetInfo.getKey();
if (texKey.isFlipY()) {
// Flip the image, then delete the old one.
Matrix flipMat = new Matrix();
flipMat.preScale(1.0f, -1.0f);
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), flipMat, false);
bitmap.recycle();
bitmap = newBitmap;
if (bitmap == null) {
throw new IOException("Failed to flip image: " + texKey);
}
}
}
}

@ -1,533 +0,0 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.audio.android;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import com.jme3.asset.AssetKey;
import com.jme3.audio.*;
import com.jme3.audio.AudioSource.Status;
import com.jme3.audio.openal.ALAudioRenderer;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import java.io.IOException;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class is the android implementation for {@link AudioRenderer}
*
* @author larynx
* @author plan_rich
*
* @deprecated No longer supported due to too many limitations.
* Please use the generic {@link ALAudioRenderer} instead.
*/
@Deprecated
public class AndroidMediaPlayerAudioRenderer implements AudioRenderer,
SoundPool.OnLoadCompleteListener, MediaPlayer.OnCompletionListener {
private static final Logger logger = Logger.getLogger(AndroidMediaPlayerAudioRenderer.class.getName());
private final static int MAX_NUM_CHANNELS = 16;
private final HashMap<AudioSource, MediaPlayer> musicPlaying = new HashMap<AudioSource, MediaPlayer>();
private SoundPool soundPool = null;
private final Vector3f listenerPosition = new Vector3f();
// For temp use
private final Vector3f distanceVector = new Vector3f();
private final AssetManager assetManager;
private HashMap<Integer, AudioSource> soundpoolStillLoading = new HashMap<Integer, AudioSource>();
private Listener listener;
private boolean audioDisabled = false;
private final AudioManager manager;
public AndroidMediaPlayerAudioRenderer(Activity context) {
manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
context.setVolumeControlStream(AudioManager.STREAM_MUSIC);
assetManager = context.getAssets();
}
@Override
public void initialize() {
soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC,
0);
soundPool.setOnLoadCompleteListener(this);
}
@Override
public void updateSourceParam(AudioSource src, AudioParam param) {
if (audioDisabled) {
return;
}
if (src.getChannel() < 0) {
return;
}
switch (param) {
case Position:
if (!src.isPositional()) {
return;
}
Vector3f pos = src.getPosition();
break;
case Velocity:
if (!src.isPositional()) {
return;
}
Vector3f vel = src.getVelocity();
break;
case MaxDistance:
if (!src.isPositional()) {
return;
}
break;
case RefDistance:
if (!src.isPositional()) {
return;
}
break;
case ReverbFilter:
if (!src.isPositional() || !src.isReverbEnabled()) {
return;
}
break;
case ReverbEnabled:
if (!src.isPositional()) {
return;
}
if (src.isReverbEnabled()) {
updateSourceParam(src, AudioParam.ReverbFilter);
}
break;
case IsPositional:
break;
case Direction:
if (!src.isDirectional()) {
return;
}
Vector3f dir = src.getDirection();
break;
case InnerAngle:
if (!src.isDirectional()) {
return;
}
break;
case OuterAngle:
if (!src.isDirectional()) {
return;
}
break;
case IsDirectional:
if (src.isDirectional()) {
updateSourceParam(src, AudioParam.Direction);
updateSourceParam(src, AudioParam.InnerAngle);
updateSourceParam(src, AudioParam.OuterAngle);
} else {
}
break;
case DryFilter:
if (src.getDryFilter() != null) {
Filter f = src.getDryFilter();
if (f.isUpdateNeeded()) {
// updateFilter(f);
}
}
break;
case Looping:
if (src.isLooping()) {
}
break;
case Volume:
MediaPlayer mp = musicPlaying.get(src);
if (mp != null) {
mp.setVolume(src.getVolume(), src.getVolume());
} else {
soundPool.setVolume(src.getChannel(), src.getVolume(),
src.getVolume());
}
break;
case Pitch:
break;
}
}
@Override
public void updateListenerParam(Listener listener, ListenerParam param) {
if (audioDisabled) {
return;
}
switch (param) {
case Position:
listenerPosition.set(listener.getLocation());
break;
case Rotation:
Vector3f dir = listener.getDirection();
Vector3f up = listener.getUp();
break;
case Velocity:
Vector3f vel = listener.getVelocity();
break;
case Volume:
// alListenerf(AL_GAIN, listener.getVolume());
break;
}
}
@Override
public void update(float tpf) {
float distance;
float volume;
// Loop over all mediaplayers
for (AudioSource src : musicPlaying.keySet()) {
MediaPlayer mp = musicPlaying.get(src);
// Calc the distance to the listener
distanceVector.set(listenerPosition);
distanceVector.subtractLocal(src.getPosition());
distance = FastMath.abs(distanceVector.length());
if (distance < src.getRefDistance()) {
distance = src.getRefDistance();
}
if (distance > src.getMaxDistance()) {
distance = src.getMaxDistance();
}
volume = src.getRefDistance() / distance;
AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
if (FastMath.abs(audioData.getCurrentVolume() - volume) > FastMath.FLT_EPSILON) {
// Left / Right channel get the same volume by now, only
// positional
mp.setVolume(volume, volume);
audioData.setCurrentVolume(volume);
}
}
}
public void setListener(Listener listener) {
if (audioDisabled) {
return;
}
if (this.listener != null) {
// previous listener no longer associated with current
// renderer
this.listener.setRenderer(null);
}
this.listener = listener;
this.listener.setRenderer(this);
}
@Override
public void cleanup() {
// Cleanup sound pool
if (soundPool != null) {
soundPool.release();
soundPool = null;
}
// Cleanup media player
for (AudioSource src : musicPlaying.keySet()) {
MediaPlayer mp = musicPlaying.get(src);
{
mp.stop();
mp.release();
src.setStatus(Status.Stopped);
}
}
musicPlaying.clear();
}
@Override
public void onCompletion(MediaPlayer mp) {
if (mp.isPlaying()) {
mp.seekTo(0);
mp.stop();
}
// XXX: This has bad performance -> maybe change overall structure of
// mediaplayer in this audiorenderer?
for (AudioSource src : musicPlaying.keySet()) {
if (musicPlaying.get(src) == mp) {
src.setStatus(Status.Stopped);
break;
}
}
}
/**
* Plays using the {@link SoundPool} of Android. Due to hard limitation of
* the SoundPool: After playing more instances of the sound you only have
* the channel of the last played instance.
*
* It is not possible to get information about the state of the soundpool of
* a specific streamid, so removing is not possilbe -&gt; noone knows when
* sound finished.
*/
public void playSourceInstance(AudioSource src) {
if (audioDisabled) {
return;
}
AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
if (!(audioData.getAssetKey() instanceof AudioKey)) {
throw new IllegalArgumentException("Asset is not a AudioKey");
}
AudioKey assetKey = (AudioKey) audioData.getAssetKey();
try {
if (audioData.getId() < 0) { // found something to load
int soundId = soundPool.load(
assetManager.openFd(assetKey.getName()), 1);
audioData.setId(soundId);
}
int channel = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
if (channel == 0) {
soundpoolStillLoading.put(audioData.getId(), src);
} else {
if (src.getStatus() != Status.Stopped) {
soundPool.stop(channel);
src.setStatus(Status.Stopped);
}
src.setChannel(channel); // receive a channel at the last
setSourceParams(src);
// playing at least
}
} catch (IOException e) {
logger.log(Level.SEVERE,
"Failed to load sound " + assetKey.getName(), e);
audioData.setId(-1);
}
}
@Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
AudioSource src = soundpoolStillLoading.remove(sampleId);
if (src == null) {
logger.warning("Something went terribly wrong! onLoadComplete"
+ " had sampleId which was not in the HashMap of loading items");
return;
}
AudioData audioData = src.getAudioData();
// load was successfull
if (status == 0) {
int channelIndex;
channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
src.setChannel(channelIndex);
setSourceParams(src);
}
}
public void playSource(AudioSource src) {
if (audioDisabled) {
return;
}
AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
MediaPlayer mp = musicPlaying.get(src);
if (mp == null) {
mp = new MediaPlayer();
mp.setOnCompletionListener(this);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
try {
if (src.getStatus() == Status.Stopped) {
mp.reset();
AssetKey<?> key = audioData.getAssetKey();
AssetFileDescriptor afd = assetManager.openFd(key.getName()); // assetKey.getName()
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
afd.getLength());
mp.prepare();
setSourceParams(src, mp);
src.setChannel(0);
src.setStatus(Status.Playing);
musicPlaying.put(src, mp);
mp.start();
} else {
mp.start();
}
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private void setSourceParams(AudioSource src, MediaPlayer mp) {
mp.setLooping(src.isLooping());
mp.setVolume(src.getVolume(), src.getVolume());
//src.getDryFilter();
}
private void setSourceParams(AudioSource src) {
soundPool.setLoop(src.getChannel(), src.isLooping() ? -1 : 0);
soundPool.setVolume(src.getChannel(), src.getVolume(), src.getVolume());
}
/**
* Pause the current playing sounds. Both from the {@link SoundPool} and the
* active {@link MediaPlayer}s
*/
public void pauseAll() {
if (soundPool != null) {
soundPool.autoPause();
for (MediaPlayer mp : musicPlaying.values()) {
if(mp.isPlaying()){
mp.pause();
}
}
}
}
/**
* Resume all paused sounds.
*/
public void resumeAll() {
if (soundPool != null) {
soundPool.autoResume();
for (MediaPlayer mp : musicPlaying.values()) {
mp.start(); //no resume -> api says call start to resume
}
}
}
public void pauseSource(AudioSource src) {
if (audioDisabled) {
return;
}
MediaPlayer mp = musicPlaying.get(src);
if (mp != null) {
mp.pause();
src.setStatus(Status.Paused);
} else {
int channel = src.getChannel();
if (channel != -1) {
soundPool.pause(channel); // is not very likley to make
} // something useful :)
}
}
public void stopSource(AudioSource src) {
if (audioDisabled) {
return;
}
// can be stream or buffer -> so try to get mediaplayer
// if there is non try to stop soundpool
MediaPlayer mp = musicPlaying.get(src);
if (mp != null) {
mp.stop();
mp.reset();
src.setStatus(Status.Stopped);
} else {
int channel = src.getChannel();
if (channel != -1) {
soundPool.pause(channel); // is not very likley to make
// something useful :)
}
}
}
@Override
public void deleteAudioData(AudioData ad) {
for (AudioSource src : musicPlaying.keySet()) {
if (src.getAudioData() == ad) {
MediaPlayer mp = musicPlaying.remove(src);
mp.stop();
mp.release();
src.setStatus(Status.Stopped);
src.setChannel(-1);
ad.setId(-1);
break;
}
}
if (ad.getId() > 0) {
soundPool.unload(ad.getId());
ad.setId(-1);
}
}
@Override
public void setEnvironment(Environment env) {
// not yet supported
}
@Override
public void deleteFilter(Filter filter) {
}
@Override
public float getSourcePlaybackTime(AudioSource src) {
throw new UnsupportedOperationException("Not supported yet.");
}
}

@ -523,4 +523,9 @@ public class AndroidGL implements GL, GLExt, GLFbo {
public Object glFenceSync(int condition, int flags) { public Object glFenceSync(int condition, int flags) {
throw new UnsupportedOperationException("OpenGL ES 2 does not support sync fences"); throw new UnsupportedOperationException("OpenGL ES 2 does not support sync fences");
} }
@Override
public void glBlendEquationSeparate(int colorMode, int alphaMode) {
GLES20.glBlendEquationSeparate(colorMode, alphaMode);
}
} }

@ -1,591 +0,0 @@
/*
* 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.renderer.android;
import android.graphics.Bitmap;
import android.opengl.ETC1;
import android.opengl.ETC1Util.ETC1Texture;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import com.jme3.asset.AndroidImageInfo;
import com.jme3.renderer.RendererException;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.util.BufferUtils;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @deprecated Should not be used anymore. Use {@link GLRenderer} instead.
*/
@Deprecated
public class TextureUtil {
private static final Logger logger = Logger.getLogger(TextureUtil.class.getName());
//TODO Make this configurable through appSettings
public static boolean ENABLE_COMPRESSION = true;
private static boolean NPOT = false;
private static boolean ETC1support = false;
private static boolean DXT1 = false;
private static boolean DEPTH24_STENCIL8 = false;
private static boolean DEPTH_TEXTURE = false;
private static boolean RGBA8 = false;
// Same constant used by both GL_ARM_rgba8 and GL_OES_rgb8_rgba8.
private static final int GL_RGBA8 = 0x8058;
private static final int GL_DXT1 = 0x83F0;
private static final int GL_DXT1A = 0x83F1;
private static final int GL_DEPTH_STENCIL_OES = 0x84F9;
private static final int GL_UNSIGNED_INT_24_8_OES = 0x84FA;
private static final int GL_DEPTH24_STENCIL8_OES = 0x88F0;
public static void loadTextureFeatures(String extensionString) {
ETC1support = extensionString.contains("GL_OES_compressed_ETC1_RGB8_texture");
DEPTH24_STENCIL8 = extensionString.contains("GL_OES_packed_depth_stencil");
NPOT = extensionString.contains("GL_IMG_texture_npot")
|| extensionString.contains("GL_OES_texture_npot")
|| extensionString.contains("GL_NV_texture_npot_2D_mipmap");
DXT1 = extensionString.contains("GL_EXT_texture_compression_dxt1");
DEPTH_TEXTURE = extensionString.contains("GL_OES_depth_texture");
RGBA8 = extensionString.contains("GL_ARM_rgba8") ||
extensionString.contains("GL_OES_rgb8_rgba8");
logger.log(Level.FINE, "Supports ETC1? {0}", ETC1support);
logger.log(Level.FINE, "Supports DEPTH24_STENCIL8? {0}", DEPTH24_STENCIL8);
logger.log(Level.FINE, "Supports NPOT? {0}", NPOT);
logger.log(Level.FINE, "Supports DXT1? {0}", DXT1);
logger.log(Level.FINE, "Supports DEPTH_TEXTURE? {0}", DEPTH_TEXTURE);
logger.log(Level.FINE, "Supports RGBA8? {0}", RGBA8);
}
private static void buildMipmap(Bitmap bitmap, boolean compress) {
int level = 0;
int height = bitmap.getHeight();
int width = bitmap.getWidth();
logger.log(Level.FINEST, " - Generating mipmaps for bitmap using SOFTWARE");
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
while (height >= 1 || width >= 1) {
//First of all, generate the texture from our bitmap and set it to the according level
if (compress) {
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) with compression.", new Object[]{level, width, height});
uploadBitmapAsCompressed(GLES20.GL_TEXTURE_2D, level, bitmap, false, 0, 0);
} else {
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) directly.", new Object[]{level, width, height});
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, level, bitmap, 0);
}
if (height == 1 || width == 1) {
break;
}
//Increase the mipmap level
height /= 2;
width /= 2;
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
// Recycle any bitmaps created as a result of scaling the bitmap.
// Do not recycle the original image (mipmap level 0)
if (level != 0) {
bitmap.recycle();
}
bitmap = bitmap2;
level++;
}
}
private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap, boolean subTexture, int x, int y) {
if (bitmap.hasAlpha()) {
logger.log(Level.FINEST, " - Uploading bitmap directly. Cannot compress as alpha present.");
if (subTexture) {
GLUtils.texSubImage2D(target, level, x, y, bitmap);
RendererUtil.checkGLError();
} else {
GLUtils.texImage2D(target, level, bitmap, 0);
RendererUtil.checkGLError();
}
} else {
// Convert to RGB565
int bytesPerPixel = 2;
Bitmap rgb565 = bitmap.copy(Bitmap.Config.RGB_565, true);
// Put texture data into ByteBuffer
ByteBuffer inputImage = BufferUtils.createByteBuffer(bitmap.getRowBytes() * bitmap.getHeight());
rgb565.copyPixelsToBuffer(inputImage);
inputImage.position(0);
// Delete the copied RGB565 image
rgb565.recycle();
// Encode the image into the output bytebuffer
int encodedImageSize = ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight());
ByteBuffer compressedImage = BufferUtils.createByteBuffer(encodedImageSize);
ETC1.encodeImage(inputImage, bitmap.getWidth(),
bitmap.getHeight(),
bytesPerPixel,
bytesPerPixel * bitmap.getWidth(),
compressedImage);
// Delete the input image buffer
BufferUtils.destroyDirectBuffer(inputImage);
// Create an ETC1Texture from the compressed image data
ETC1Texture etc1tex = new ETC1Texture(bitmap.getWidth(), bitmap.getHeight(), compressedImage);
// Upload the ETC1Texture
if (bytesPerPixel == 2) {
int oldSize = (bitmap.getRowBytes() * bitmap.getHeight());
int newSize = compressedImage.capacity();
logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float) oldSize / newSize});
if (subTexture) {
GLES20.glCompressedTexSubImage2D(target,
level,
x, y,
bitmap.getWidth(),
bitmap.getHeight(),
ETC1.ETC1_RGB8_OES,
etc1tex.getData().capacity(),
etc1tex.getData());
RendererUtil.checkGLError();
} else {
GLES20.glCompressedTexImage2D(target,
level,
ETC1.ETC1_RGB8_OES,
bitmap.getWidth(),
bitmap.getHeight(),
0,
etc1tex.getData().capacity(),
etc1tex.getData());
RendererUtil.checkGLError();
}
// ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB,
// GLES20.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);
// } else if (bytesPerPixel == 3) {
// ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB,
// GLES20.GL_UNSIGNED_BYTE, etc1Texture);
}
BufferUtils.destroyDirectBuffer(compressedImage);
}
}
/**
* <code>uploadTextureBitmap</code> uploads a native android bitmap
*/
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips) {
uploadTextureBitmap(target, bitmap, needMips, false, 0, 0);
}
/**
* <code>uploadTextureBitmap</code> uploads a native android bitmap
*/
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips, boolean subTexture, int x, int y) {
boolean recycleBitmap = false;
//TODO, maybe this should raise an exception when NPOT is not supported
boolean willCompress = ENABLE_COMPRESSION && ETC1support && !bitmap.hasAlpha();
if (needMips && willCompress) {
// Image is compressed and mipmaps are desired, generate them
// using software.
buildMipmap(bitmap, willCompress);
} else {
if (willCompress) {
// Image is compressed but mipmaps are not desired, upload directly.
logger.log(Level.FINEST, " - Uploading compressed bitmap. Mipmaps are not generated.");
uploadBitmapAsCompressed(target, 0, bitmap, subTexture, x, y);
} else {
// Image is not compressed, mipmaps may or may not be desired.
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
(needMips
? " Mipmaps will be generated in HARDWARE"
: " Mipmaps are not generated."));
if (subTexture) {
System.err.println("x : " + x + " y :" + y + " , " + bitmap.getWidth() + "/" + bitmap.getHeight());
GLUtils.texSubImage2D(target, 0, x, y, bitmap);
RendererUtil.checkGLError();
} else {
GLUtils.texImage2D(target, 0, bitmap, 0);
RendererUtil.checkGLError();
}
if (needMips) {
// No pregenerated mips available,
// generate from base level if required
GLES20.glGenerateMipmap(target);
RendererUtil.checkGLError();
}
}
}
if (recycleBitmap) {
bitmap.recycle();
}
}
public static void uploadTextureAny(Image img, int target, int index, boolean needMips) {
if (img.getEfficentData() instanceof AndroidImageInfo) {
logger.log(Level.FINEST, " === Uploading image {0}. Using BITMAP PATH === ", img);
// If image was loaded from asset manager, use fast path
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
uploadTextureBitmap(target, imageInfo.getBitmap(), needMips);
} else {
logger.log(Level.FINEST, " === Uploading image {0}. Using BUFFER PATH === ", img);
boolean wantGeneratedMips = needMips && !img.hasMipmaps();
if (wantGeneratedMips && img.getFormat().isCompressed()) {
logger.log(Level.WARNING, "Generating mipmaps is only"
+ " supported for Bitmap based or non-compressed images!");
}
// Upload using slower path
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
(wantGeneratedMips
? " Mipmaps will be generated in HARDWARE"
: " Mipmaps are not generated."));
uploadTexture(img, target, index);
// Image was uploaded using slower path, since it is not compressed,
// then compress it
if (wantGeneratedMips) {
// No pregenerated mips available,
// generate from base level if required
GLES20.glGenerateMipmap(target);
}
}
}
private static void unsupportedFormat(Format fmt) {
throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware.");
}
public static AndroidGLImageFormat getImageFormat(Format fmt, boolean forRenderBuffer)
throws UnsupportedOperationException {
AndroidGLImageFormat imageFormat = new AndroidGLImageFormat();
switch (fmt) {
case Depth32:
case Depth32F:
throw new UnsupportedOperationException("The image format '"
+ fmt + "' is not supported by OpenGL ES 2.0 specification.");
case Alpha8:
imageFormat.format = GLES20.GL_ALPHA;
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
if (RGBA8) {
imageFormat.renderBufferStorageFormat = GL_RGBA8;
} else {
// Highest precision alpha supported by vanilla OGLES2
imageFormat.renderBufferStorageFormat = GLES20.GL_RGBA4;
}
break;
case Luminance8:
imageFormat.format = GLES20.GL_LUMINANCE;
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
if (RGBA8) {
imageFormat.renderBufferStorageFormat = GL_RGBA8;
} else {
// Highest precision luminance supported by vanilla OGLES2
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB565;
}
break;
case Luminance8Alpha8:
imageFormat.format = GLES20.GL_LUMINANCE_ALPHA;
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
if (RGBA8) {
imageFormat.renderBufferStorageFormat = GL_RGBA8;
} else {
imageFormat.renderBufferStorageFormat = GLES20.GL_RGBA4;
}
break;
case RGB565:
imageFormat.format = GLES20.GL_RGB;
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5;
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB565;
break;
case RGB5A1:
imageFormat.format = GLES20.GL_RGBA;
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1;
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB5_A1;
break;
case RGB8:
imageFormat.format = GLES20.GL_RGB;
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
if (RGBA8) {
imageFormat.renderBufferStorageFormat = GL_RGBA8;
} else {
// Fallback: Use RGB565 if RGBA8 is not available.
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB565;
}
break;
case BGR8:
imageFormat.format = GLES20.GL_RGB;
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
if (RGBA8) {
imageFormat.renderBufferStorageFormat = GL_RGBA8;
} else {
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB565;
}
break;
case RGBA8:
imageFormat.format = GLES20.GL_RGBA;
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
if (RGBA8) {
imageFormat.renderBufferStorageFormat = GL_RGBA8;
} else {
imageFormat.renderBufferStorageFormat = GLES20.GL_RGBA4;
}
break;
case Depth:
case Depth16:
if (!DEPTH_TEXTURE && !forRenderBuffer) {
unsupportedFormat(fmt);
}
imageFormat.format = GLES20.GL_DEPTH_COMPONENT;
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT;
imageFormat.renderBufferStorageFormat = GLES20.GL_DEPTH_COMPONENT16;
break;
case Depth24:
case Depth24Stencil8:
if (!DEPTH_TEXTURE) {
unsupportedFormat(fmt);
}
if (DEPTH24_STENCIL8) {
// NEW: True Depth24 + Stencil8 format.
imageFormat.format = GL_DEPTH_STENCIL_OES;
imageFormat.dataType = GL_UNSIGNED_INT_24_8_OES;
imageFormat.renderBufferStorageFormat = GL_DEPTH24_STENCIL8_OES;
} else {
// Vanilla OGLES2, only Depth16 available.
imageFormat.format = GLES20.GL_DEPTH_COMPONENT;
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT;
imageFormat.renderBufferStorageFormat = GLES20.GL_DEPTH_COMPONENT16;
}
break;
case DXT1:
if (!DXT1) {
unsupportedFormat(fmt);
}
imageFormat.format = GL_DXT1;
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
imageFormat.compress = true;
break;
case DXT1A:
if (!DXT1) {
unsupportedFormat(fmt);
}
imageFormat.format = GL_DXT1A;
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
imageFormat.compress = true;
break;
default:
throw new UnsupportedOperationException("Unrecognized format: " + fmt);
}
return imageFormat;
}
public static class AndroidGLImageFormat {
boolean compress = false;
int format = -1;
int renderBufferStorageFormat = -1;
int dataType = -1;
}
private static void uploadTexture(Image img,
int target,
int index) {
if (img.getEfficentData() instanceof AndroidImageInfo) {
throw new RendererException("This image uses efficient data. "
+ "Use uploadTextureBitmap instead.");
}
// Otherwise upload image directly.
// Prefer to only use power of 2 textures here to avoid errors.
Image.Format fmt = img.getFormat();
ByteBuffer data;
if (index >= 0 || img.getData() != null && img.getData().size() > 0) {
data = img.getData(index);
} else {
data = null;
}
int width = img.getWidth();
int height = img.getHeight();
if (!NPOT && img.isNPOT()) {
// Check if texture is POT
throw new RendererException("Non-power-of-2 textures "
+ "are not supported by the video hardware "
+ "and no scaling path available for image: " + img);
}
AndroidGLImageFormat imageFormat = getImageFormat(fmt, false);
if (data != null) {
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
}
int[] mipSizes = img.getMipMapSizes();
int pos = 0;
if (mipSizes == null) {
if (data != null) {
mipSizes = new int[]{data.capacity()};
} else {
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8};
}
}
for (int i = 0; i < mipSizes.length; i++) {
int mipWidth = Math.max(1, width >> i);
int mipHeight = Math.max(1, height >> i);
if (data != null) {
data.position(pos);
data.limit(pos + mipSizes[i]);
}
if (imageFormat.compress && data != null) {
GLES20.glCompressedTexImage2D(target,
i,
imageFormat.format,
mipWidth,
mipHeight,
0,
data.remaining(),
data);
} else {
GLES20.glTexImage2D(target,
i,
imageFormat.format,
mipWidth,
mipHeight,
0,
imageFormat.format,
imageFormat.dataType,
data);
}
pos += mipSizes[i];
}
}
/**
* Update the texture currently bound to target at with data from the given
* Image at position x and y. The parameter index is used as the zoffset in
* case a 3d texture or texture 2d array is being updated.
*
* @param image Image with the source data (this data will be put into the
* texture)
* @param target the target texture
* @param index the mipmap level to update
* @param x the x position where to put the image in the texture
* @param y the y position where to put the image in the texture
*/
public static void uploadSubTexture(
Image img,
int target,
int index,
int x,
int y) {
if (img.getEfficentData() instanceof AndroidImageInfo) {
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
uploadTextureBitmap(target, imageInfo.getBitmap(), true, true, x, y);
return;
}
// Otherwise upload image directly.
// Prefer to only use power of 2 textures here to avoid errors.
Image.Format fmt = img.getFormat();
ByteBuffer data;
if (index >= 0 || img.getData() != null && img.getData().size() > 0) {
data = img.getData(index);
} else {
data = null;
}
int width = img.getWidth();
int height = img.getHeight();
if (!NPOT && img.isNPOT()) {
// Check if texture is POT
throw new RendererException("Non-power-of-2 textures "
+ "are not supported by the video hardware "
+ "and no scaling path available for image: " + img);
}
AndroidGLImageFormat imageFormat = getImageFormat(fmt, false);
if (data != null) {
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
}
int[] mipSizes = img.getMipMapSizes();
int pos = 0;
if (mipSizes == null) {
if (data != null) {
mipSizes = new int[]{data.capacity()};
} else {
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8};
}
}
for (int i = 0; i < mipSizes.length; i++) {
int mipWidth = Math.max(1, width >> i);
int mipHeight = Math.max(1, height >> i);
if (data != null) {
data.position(pos);
data.limit(pos + mipSizes[i]);
}
if (imageFormat.compress && data != null) {
GLES20.glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, data.remaining(), data);
RendererUtil.checkGLError();
} else {
GLES20.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, imageFormat.dataType, data);
RendererUtil.checkGLError();
}
pos += mipSizes[i];
}
}
}

@ -1,23 +0,0 @@
package com.jme3.texture.plugins;
import android.graphics.Bitmap;
import com.jme3.asset.AndroidImageInfo;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoader;
import com.jme3.texture.Image;
import com.jme3.texture.image.ColorSpace;
import java.io.IOException;
@Deprecated
public class AndroidImageLoader implements AssetLoader {
public Object load(AssetInfo info) throws IOException {
AndroidImageInfo imageInfo = new AndroidImageInfo(info);
Bitmap bitmap = imageInfo.getBitmap();
Image image = new Image(imageInfo.getFormat(), bitmap.getWidth(), bitmap.getHeight(), null, ColorSpace.sRGB);
image.setEfficentData(imageInfo);
return image;
}
}

@ -46,7 +46,7 @@ public class SimpleTexturedTest extends SimpleApplication {
shapeSphere = new Sphere(16, 16, .5f); shapeSphere = new Sphere(16, 16, .5f);
shapeBox = new Box(Vector3f.ZERO, 0.3f, 0.3f, 0.3f); shapeBox = new Box(0.3f, 0.3f, 0.3f);
// ModelConverter.optimize(geom); // ModelConverter.optimize(geom);

@ -1,6 +1,7 @@
package com.jme3.scene.plugins.blender.materials; package com.jme3.scene.plugins.blender.materials;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -157,14 +158,14 @@ public final class MaterialContext implements Savable {
} }
// applying textures // applying textures
int textureIndex = 0;
if (loadedTextures != null && loadedTextures.size() > 0) { if (loadedTextures != null && loadedTextures.size() > 0) {
int textureIndex = 0;
if (loadedTextures.size() > TextureHelper.TEXCOORD_TYPES.length) { if (loadedTextures.size() > TextureHelper.TEXCOORD_TYPES.length) {
LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different textures. JME supports only {0} UV mappings.", TextureHelper.TEXCOORD_TYPES.length); LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different textures. JME supports only {0} UV mappings.", TextureHelper.TEXCOORD_TYPES.length);
} }
for (CombinedTexture combinedTexture : loadedTextures) { for (CombinedTexture combinedTexture : loadedTextures) {
if (textureIndex < TextureHelper.TEXCOORD_TYPES.length) { if (textureIndex < TextureHelper.TEXCOORD_TYPES.length) {
combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext); String usedUserUVSet = combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture()); this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture());
List<Vector2f> uvs = combinedTexture.getResultUVS(); List<Vector2f> uvs = combinedTexture.getResultUVS();
@ -173,13 +174,19 @@ public final class MaterialContext implements Savable {
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()]))); uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
geometry.getMesh().setBuffer(uvCoordsBuffer); 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) }//uvs might be null if the user assigned non existing UV coordinates group name to the mesh (this should be fixed in blender file)
if(usedUserUVSet != null) {
userDefinedUVCoordinates = new HashMap<>(userDefinedUVCoordinates);
userDefinedUVCoordinates.remove(usedUserUVSet);
}
} 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);
} }
} }
} else if (userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) { }
LOGGER.fine("No textures found for the mesh, but UV coordinates are applied.");
int textureIndex = 0; if (userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
LOGGER.fine("Storing unused, user defined UV coordinates sets.");
if (userDefinedUVCoordinates.size() > TextureHelper.TEXCOORD_TYPES.length) { if (userDefinedUVCoordinates.size() > TextureHelper.TEXCOORD_TYPES.length) {
LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different UV coordinates for the mesh. JME supports only {0} UV coordinates buffers.", TextureHelper.TEXCOORD_TYPES.length); LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different UV coordinates for the mesh. JME supports only {0} UV coordinates buffers.", TextureHelper.TEXCOORD_TYPES.length);
} }
@ -190,7 +197,9 @@ public final class MaterialContext implements Savable {
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()]))); uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
geometry.getMesh().setBuffer(uvCoordsBuffer); geometry.getMesh().setBuffer(uvCoordsBuffer);
} 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 user's UV set named: '{0}' could not be stored because JME only supports up to {1} different UV's.", new Object[] {
entry.getKey(), TextureHelper.TEXCOORD_TYPES.length
});
} }
} }
} }

@ -119,22 +119,24 @@ public class CombinedTexture {
} }
} }
/** /**
* This method flattens the texture and creates a single result of Texture2D * This method flattens the texture and creates a single result of Texture2D
* type. * type.
* *
* @param geometry * @param geometry
* the geometry the texture is created for * the geometry the texture is created for
* @param geometriesOMA * @param geometriesOMA
* the old memory address of the geometries list that the given * the old memory address of the geometries list that the given
* geometry belongs to (needed for bounding box creation) * geometry belongs to (needed for bounding box creation)
* @param userDefinedUVCoordinates * @param userDefinedUVCoordinates
* the UV's defined by user (null or zero length table if none * the UV's defined by user (null or zero length table if none
* were defined) * were defined)
* @param blenderContext * @param blenderContext
* the blender context * the blender context
*/ * @return the name of the user UV coordinates used (null if the UV's were
public void flatten(Geometry geometry, Long geometriesOMA, Map<String, List<Vector2f>> userDefinedUVCoordinates, BlenderContext blenderContext) { * generated)
*/
public String flatten(Geometry geometry, Long geometriesOMA, Map<String, List<Vector2f>> userDefinedUVCoordinates, BlenderContext blenderContext) {
Mesh mesh = geometry.getMesh(); Mesh mesh = geometry.getMesh();
Texture previousTexture = null; Texture previousTexture = null;
UVCoordinatesType masterUVCoordinatesType = null; UVCoordinatesType masterUVCoordinatesType = null;
@ -226,6 +228,7 @@ public class CombinedTexture {
} }
resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS(); resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS();
resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture(); resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture();
masterUserUVSetName = null;
} }
// setting additional data // setting additional data
@ -234,6 +237,8 @@ public class CombinedTexture {
// otherwise ugly lines appear between the mesh faces // otherwise ugly lines appear between the mesh faces
resultTexture.setMagFilter(MagFilter.Nearest); resultTexture.setMagFilter(MagFilter.Nearest);
resultTexture.setMinFilter(MinFilter.NearestNoMipMaps); resultTexture.setMinFilter(MinFilter.NearestNoMipMaps);
return masterUserUVSetName;
} }
/** /**

@ -34,7 +34,14 @@ libraries {
// linker.args "-static-libstdc++" // linker.args "-static-libstdc++"
} else if (targetPlatform.operatingSystem.name == "windows") { } else if (targetPlatform.operatingSystem.name == "windows") {
if (toolChain in Gcc) { if (toolChain in Gcc) {
cppCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32" if (toolChain.name.startsWith('mingw')) {
cppCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include/linux"
} else {
cppCompiler.args '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"
}
cppCompiler.args "-fpermissive"
cppCompiler.args "-static"
linker.args "-static"
} }
else if (toolChain in VisualCpp) { else if (toolChain in VisualCpp) {
cppCompiler.args "/I${org.gradle.internal.jvm.Jvm.current().javaHome}\\include\\win32" cppCompiler.args "/I${org.gradle.internal.jvm.Jvm.current().javaHome}\\include\\win32"
@ -76,6 +83,31 @@ sourceSets {
// Set of target platforms, will be available based on build system // Set of target platforms, will be available based on build system
model { model {
toolChains {
gcc(Gcc)
mingw_x86(Gcc) {
eachPlatform() {
cCompiler.executable "i686-w64-mingw32-gcc"
cppCompiler.executable "i686-w64-mingw32-g++"
linker.executable "i686-w64-mingw32-g++"
assembler.executable "i686-w64-mingw32-g++"
staticLibArchiver.executable "i686-w64-mingw32-gcc-ar"
}
target("windows_x86")
}
mingw_x86_64(Gcc) {
eachPlatform() {
cCompiler.executable "x86_64-w64-mingw32-gcc"
cppCompiler.executable "x86_64-w64-mingw32-g++"
linker.executable "x86_64-w64-mingw32-g++"
assembler.executable "x86_64-w64-mingw32-g++"
staticLibArchiver.executable "x86_64-w64-mingw32-gcc-ar"
}
target("windows_x86_64")
}
}
platforms{ platforms{
// osx_universal { // TODO: universal binary doesn't work? // osx_universal { // TODO: universal binary doesn't work?
// architecture 'x86_64' // architecture 'x86_64'

@ -111,7 +111,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
* Material references used for hardware skinning * Material references used for hardware skinning
*/ */
private Set<Material> materials = new HashSet<Material>(); private Set<Material> materials = new HashSet<Material>();
/** /**
* Serialization only. Do not use. * Serialization only. Do not use.
*/ */
@ -204,6 +204,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
* @param skeleton the skeleton * @param skeleton the skeleton
*/ */
public SkeletonControl(Skeleton skeleton) { public SkeletonControl(Skeleton skeleton) {
if (skeleton == null) {
throw new IllegalArgumentException("skeleton cannot be null");
}
this.skeleton = skeleton; this.skeleton = skeleton;
} }
@ -406,7 +409,23 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
// Not automatic set cloning yet // Not automatic set cloning yet
Set<Material> newMaterials = new HashSet<Material>(); Set<Material> newMaterials = new HashSet<Material>();
for( Material m : this.materials ) { for( Material m : this.materials ) {
newMaterials.add(cloner.clone(m)); Material mClone = cloner.clone(m);
newMaterials.add(mClone);
if( mClone != m ) {
// Material was really cloned so clear the bone matrices in case
// this is hardware skinned. This allows a local version to be
// used and will be reset on the material. Really this just avoids
// the 'safety' check in controlRenderHardware(). Right now material
// doesn't clone itself with the cloner (and doesn't clone its parameters)
// else this would be unnecessary.
MatParam boneMatrices = mClone.getParam("BoneMatrices");
// ...because for some strange reason you can't clear a non-existant
// parameter.
if( boneMatrices != null ) {
mClone.clearParam("BoneMatrices");
}
}
} }
this.materials = newMaterials; this.materials = newMaterials;
} }

@ -1,42 +0,0 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.animation;
/**
* @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack)
*/
@Deprecated
public class SpatialAnimation extends Animation {
public SpatialAnimation(String name, float length) {
super(name, length);
}
}

@ -32,6 +32,7 @@
package com.jme3.app; package com.jme3.app;
import com.jme3.app.state.AppState; import com.jme3.app.state.AppState;
import com.jme3.audio.AudioListenerState;
import com.jme3.font.BitmapFont; import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText; import com.jme3.font.BitmapText;
import com.jme3.input.FlyByCamera; import com.jme3.input.FlyByCamera;
@ -96,7 +97,7 @@ public abstract class SimpleApplication extends LegacyApplication {
} }
public SimpleApplication() { public SimpleApplication() {
this( new StatsAppState(), new FlyCamAppState(), new DebugKeysAppState() ); this(new StatsAppState(), new FlyCamAppState(), new AudioListenerState(), new DebugKeysAppState());
} }
public SimpleApplication( AppState... initialStates ) { public SimpleApplication( AppState... initialStates ) {

@ -41,9 +41,7 @@ import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.Caps; import com.jme3.renderer.Caps;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.OBJLoader; import com.jme3.scene.plugins.OBJLoader;
import com.jme3.shader.Shader;
import com.jme3.shader.ShaderGenerator; import com.jme3.shader.ShaderGenerator;
import com.jme3.shader.ShaderKey;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.plugins.TGALoader; import com.jme3.texture.plugins.TGALoader;
import java.io.IOException; import java.io.IOException;
@ -320,13 +318,6 @@ public interface AssetManager {
*/ */
public Material loadMaterial(String name); public Material loadMaterial(String name);
/**
* Loads shader file(s), shouldn't be used by end-user in most cases.
*
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
*/
public Shader loadShader(ShaderKey key);
/** /**
* Load a font file. Font files are in AngelCode text format, * Load a font file. Font files are in AngelCode text format,
* and are with the extension "fnt". * and are with the extension "fnt".

@ -32,7 +32,6 @@
package com.jme3.asset; package com.jme3.asset;
import com.jme3.asset.cache.AssetCache; import com.jme3.asset.cache.AssetCache;
import com.jme3.asset.cache.SimpleAssetCache;
import com.jme3.audio.AudioData; import com.jme3.audio.AudioData;
import com.jme3.audio.AudioKey; import com.jme3.audio.AudioKey;
import com.jme3.font.BitmapFont; import com.jme3.font.BitmapFont;
@ -42,9 +41,7 @@ import com.jme3.renderer.Caps;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.shader.Glsl100ShaderGenerator; import com.jme3.shader.Glsl100ShaderGenerator;
import com.jme3.shader.Glsl150ShaderGenerator; import com.jme3.shader.Glsl150ShaderGenerator;
import com.jme3.shader.Shader;
import com.jme3.shader.ShaderGenerator; import com.jme3.shader.ShaderGenerator;
import com.jme3.shader.ShaderKey;
import com.jme3.system.JmeSystem; import com.jme3.system.JmeSystem;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import java.io.IOException; import java.io.IOException;
@ -431,36 +428,6 @@ public class DesktopAssetManager implements AssetManager {
return loadFilter(new FilterKey(name)); return loadFilter(new FilterKey(name));
} }
/**
* Load a vertex/fragment shader combo.
*
* @param key
* @return the loaded {@link Shader}
*/
public Shader loadShader(ShaderKey key){
// cache abuse in method
// that doesn't use loaders/locators
AssetCache cache = handler.getCache(SimpleAssetCache.class);
Shader shader = (Shader) cache.getFromCache(key);
if (shader == null){
if (key.isUsesShaderNodes()) {
if(shaderGenerator == null){
throw new UnsupportedOperationException("ShaderGenerator was not initialized, make sure assetManager.getGenerator(caps) has been called");
}
shader = shaderGenerator.generateShader();
} else {
shader = new Shader();
shader.initialize();
for (Shader.ShaderType shaderType : key.getUsedShaderPrograms()) {
shader.addSource(shaderType,key.getShaderProgramName(shaderType),(String) loadAsset(new AssetKey(key.getShaderProgramName(shaderType))),key.getDefines().getCompiled(),key.getShaderProgramLanguage(shaderType));
}
}
cache.addToCache(key, shader);
}
return shader;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

@ -123,24 +123,6 @@ public class TextureKey extends AssetKey<Texture> {
this.anisotropy = anisotropy; this.anisotropy = anisotropy;
} }
/**
* @deprecated Use {@link #setTextureTypeHint(com.jme3.texture.Texture.Type) }
* instead.
*/
@Deprecated
public boolean isAsCube() {
return textureTypeHint == Type.CubeMap;
}
/**
* @deprecated Use {@link #setTextureTypeHint(com.jme3.texture.Texture.Type) }
* instead.
*/
@Deprecated
public void setAsCube(boolean asCube) {
textureTypeHint = asCube ? Type.CubeMap : Type.TwoDimensional;
}
public boolean isGenerateMips() { public boolean isGenerateMips() {
return generateMips; return generateMips;
} }
@ -149,24 +131,6 @@ public class TextureKey extends AssetKey<Texture> {
this.generateMips = generateMips; this.generateMips = generateMips;
} }
/**
* @deprecated Use {@link #setTextureTypeHint(com.jme3.texture.Texture.Type) }
* instead.
*/
@Deprecated
public boolean isAsTexture3D() {
return textureTypeHint == Type.ThreeDimensional;
}
/**
* @deprecated Use {@link #setTextureTypeHint(com.jme3.texture.Texture.Type) }
* instead.
*/
@Deprecated
public void setAsTexture3D(boolean asTexture3D) {
textureTypeHint = asTexture3D ? Type.ThreeDimensional : Type.TwoDimensional;
}
/** /**
* The type of texture expected to be returned. * The type of texture expected to be returned.
* *

@ -0,0 +1,104 @@
/*
* Copyright (c) 2009-2016 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.audio;
import com.jme3.app.Application;
import com.jme3.app.state.BaseAppState;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
/**
* <code>AudioListenerState</code> updates the audio listener's position,
* orientation, and velocity from a {@link Camera}.
*
* @author Kirill Vainer
*/
public class AudioListenerState extends BaseAppState {
private Listener listener;
private Camera camera;
private float lastTpf;
public AudioListenerState() {
}
@Override
protected void initialize(Application app) {
this.camera = app.getCamera();
this.listener = app.getListener();
}
@Override
protected void cleanup(Application app) {
}
@Override
public void update(float tpf) {
lastTpf = tpf;
}
@Override
public void render(RenderManager rm) {
if (!isEnabled()) {
return;
}
Vector3f lastLocation = listener.getLocation();
Vector3f currentLocation = camera.getLocation();
Vector3f velocity = listener.getVelocity();
if (!lastLocation.equals(currentLocation)) {
velocity.set(currentLocation).subtractLocal(lastLocation);
velocity.multLocal(1f / lastTpf);
listener.setLocation(currentLocation);
listener.setVelocity(velocity);
} else if (!velocity.equals(Vector3f.ZERO)) {
listener.setVelocity(Vector3f.ZERO);
}
Quaternion lastRotation = listener.getRotation();
Quaternion currentRotation = camera.getRotation();
if (!lastRotation.equals(currentRotation)) {
listener.setRotation(currentRotation);
}
}
@Override
protected void onEnable() {
}
@Override
protected void onDisable() {
}
}

@ -78,6 +78,7 @@ public class AudioNode extends Node implements AudioSource {
protected transient AudioData data = null; protected transient AudioData data = null;
protected transient volatile AudioSource.Status status = AudioSource.Status.Stopped; protected transient volatile AudioSource.Status status = AudioSource.Status.Stopped;
protected transient volatile int channel = -1; protected transient volatile int channel = -1;
protected Vector3f previousWorldTranslation = Vector3f.NAN;
protected Vector3f velocity = new Vector3f(); protected Vector3f velocity = new Vector3f();
protected boolean reverbEnabled = false; protected boolean reverbEnabled = false;
protected float maxDistance = 200; // 200 meters protected float maxDistance = 200; // 200 meters
@ -88,6 +89,8 @@ public class AudioNode extends Node implements AudioSource {
protected float innerAngle = 360; protected float innerAngle = 360;
protected float outerAngle = 360; protected float outerAngle = 360;
protected boolean positional = true; protected boolean positional = true;
protected boolean velocityFromTranslation = false;
protected float lastTpf;
/** /**
* <code>Status</code> indicates the current status of the audio node. * <code>Status</code> indicates the current status of the audio node.
@ -702,17 +705,44 @@ public class AudioNode extends Node implements AudioSource {
} }
} }
public boolean isVelocityFromTranslation() {
return velocityFromTranslation;
}
public void setVelocityFromTranslation(boolean velocityFromTranslation) {
this.velocityFromTranslation = velocityFromTranslation;
}
@Override @Override
public void updateGeometricState(){ public void updateLogicalState(float tpf) {
boolean updatePos = false; super.updateLogicalState(tpf);
if ((refreshFlags & RF_TRANSFORM) != 0){ lastTpf = tpf;
updatePos = true; }
}
@Override
public void updateGeometricState() {
super.updateGeometricState(); super.updateGeometricState();
if (updatePos && channel >= 0) if (channel < 0) {
return;
}
Vector3f currentWorldTranslation = worldTransform.getTranslation();
if (Float.isNaN(previousWorldTranslation.x)
|| !previousWorldTranslation.equals(currentWorldTranslation)) {
getRenderer().updateSourceParam(this, AudioParam.Position); getRenderer().updateSourceParam(this, AudioParam.Position);
if (velocityFromTranslation) {
velocity.set(currentWorldTranslation).subtractLocal(previousWorldTranslation);
velocity.multLocal(1f / lastTpf);
getRenderer().updateSourceParam(this, AudioParam.Velocity);
}
previousWorldTranslation.set(currentWorldTranslation);
}
} }
@Override @Override
@ -772,6 +802,7 @@ public class AudioNode extends Node implements AudioSource {
oc.write(outerAngle, "outer_angle", 360); oc.write(outerAngle, "outer_angle", 360);
oc.write(positional, "positional", false); oc.write(positional, "positional", false);
oc.write(velocityFromTranslation, "velocity_from_translation", false);
} }
@Override @Override
@ -806,6 +837,7 @@ public class AudioNode extends Node implements AudioSource {
outerAngle = ic.readFloat("outer_angle", 360); outerAngle = ic.readFloat("outer_angle", 360);
positional = ic.readBoolean("positional", false); positional = ic.readBoolean("positional", false);
velocityFromTranslation = ic.readBoolean("velocity_from_translation", false);
if (audioKey != null) { if (audioKey != null) {
try { try {

@ -36,9 +36,9 @@ import com.jme3.math.Vector3f;
public class Listener { public class Listener {
private Vector3f location; private final Vector3f location;
private Vector3f velocity; private final Vector3f velocity;
private Quaternion rotation; private final Quaternion rotation;
private float volume = 1; private float volume = 1;
private AudioRenderer renderer; private AudioRenderer renderer;

@ -904,11 +904,12 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
} else { } else {
// Buffer finished playing. // Buffer finished playing.
if (src.isLooping()) { if (src.isLooping()) {
throw new AssertionError("Unexpected state: " + // When a device is disconnected, all sources
"A looping sound has stopped playing"); // will enter the "stopped" state.
} else { logger.warning("A looping sound has stopped playing");
reclaimChannel = true;
} }
reclaimChannel = true;
} }
if (reclaimChannel) { if (reclaimChannel) {

@ -121,7 +121,8 @@ public class MotionPath implements Savable {
Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m"); Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) { for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
Vector3f cp = it.next(); Vector3f cp = it.next();
Geometry geo = new Geometry("box", new Box(cp, 0.3f, 0.3f, 0.3f)); Geometry geo = new Geometry("box", new Box(0.3f, 0.3f, 0.3f));
geo.setLocalTranslation(cp);
geo.setMaterial(m); geo.setMaterial(m);
debugNode.attachChild(geo); debugNode.attachChild(geo);

@ -50,8 +50,9 @@ public abstract class ParticleMesh extends Mesh {
public enum Type { public enum Type {
/** /**
* The particle mesh is composed of points. Each particle is a point. * The particle mesh is composed of points. Each particle is a point.
* This can be used in conjuction with {@link RenderState#setPointSprite(boolean) point sprites} * Note that point based particles do not support certain features such
* to render particles the usual way. * as {@link ParticleEmitter#setRotateSpeed(float) rotation}, and
* {@link ParticleEmitter#setFacingVelocity(boolean) velocity following}.
*/ */
Point, Point,

@ -54,10 +54,10 @@ import java.util.logging.Logger;
*/ */
public class SavableClassUtil { public class SavableClassUtil {
private final static HashMap<String, String> classRemappings = new HashMap<String, String>(); private final static HashMap<String, String> CLASS_REMAPPINGS = new HashMap<>();
private static void addRemapping(String oldClass, Class<? extends Savable> newClass){ private static void addRemapping(String oldClass, Class<? extends Savable> newClass){
classRemappings.put(oldClass, newClass.getName()); CLASS_REMAPPINGS.put(oldClass, newClass.getName());
} }
static { static {
@ -74,7 +74,7 @@ public class SavableClassUtil {
} }
private static String remapClass(String className) throws ClassNotFoundException { private static String remapClass(String className) throws ClassNotFoundException {
String result = classRemappings.get(className); String result = CLASS_REMAPPINGS.get(className);
if (result == null) { if (result == null) {
return className; return className;
} else { } else {

@ -34,7 +34,6 @@ package com.jme3.material;
import com.jme3.asset.TextureKey; import com.jme3.asset.TextureKey;
import com.jme3.export.*; import com.jme3.export.*;
import com.jme3.math.*; import com.jme3.math.*;
import com.jme3.renderer.Renderer;
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.Texture.WrapMode;
@ -129,9 +128,6 @@ public class MatParam implements Savable, Cloneable {
this.value = value; this.value = value;
} }
void apply(Renderer r, Technique technique) {
technique.updateUniformParam(getPrefixedName(), getVarType(), getValue());
}
/** /**
* Returns the material parameter value as it would appear in a J3M * Returns the material parameter value as it would appear in a J3M

@ -0,0 +1,151 @@
/*
* Copyright (c) 2009-2016 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.material;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.scene.Spatial;
import com.jme3.shader.VarType;
import java.io.IOException;
/**
* <code>MatParamOverride</code> is a mechanism by which
* {@link MatParam material parameters} can be overridden on the scene graph.
* <p>
* A scene branch which has a <code>MatParamOverride</code> applied to it will
* cause all material parameters with the same name and type to have their value
* replaced with the value set on the <code>MatParamOverride</code>. If those
* parameters are mapped to a define, then the define will be overridden as well
* using the same rules as the ones used for regular material parameters.
* <p>
* <code>MatParamOverrides</code> are applied to a {@link Spatial} via the
* {@link Spatial#addMatParamOverride(com.jme3.material.MatParamOverride)}
* method. They are propagated to child <code>Spatials</code> via
* {@link Spatial#updateGeometricState()} similar to how lights are propagated.
* <p>
* Example:<br>
* <pre>
* {@code
*
* Geometry box = new Geometry("Box", new Box(1,1,1));
* Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
* mat.setColor("Color", ColorRGBA.Blue);
* box.setMaterial(mat);
* rootNode.attachChild(box);
*
* // ... later ...
* MatParamOverride override = new MatParamOverride(Type.Vector4, "Color", ColorRGBA.Red);
* rootNode.addMatParamOverride(override);
*
* // After adding the override to the root node, the box becomes red.
* }
* </pre>
*
* @author Kirill Vainer
* @see Spatial#addMatParamOverride(com.jme3.material.MatParamOverride)
* @see Spatial#getWorldMatParamOverrides()
*/
public final class MatParamOverride extends MatParam {
private boolean enabled = true;
/**
* Serialization only. Do not use.
*/
public MatParamOverride() {
super();
}
/**
* Create a new <code>MatParamOverride</code>.
*
* Overrides are created enabled by default.
*
* @param type The type of parameter.
* @param name The name of the parameter.
* @param value The value to set the material parameter to.
*/
public MatParamOverride(VarType type, String name, Object value) {
super(type, name, value);
}
@Override
public boolean equals(Object obj) {
return super.equals(obj) && this.enabled == ((MatParamOverride) obj).enabled;
}
@Override
public int hashCode() {
int hash = super.hashCode();
hash = 59 * hash + (enabled ? 1 : 0);
return hash;
}
/**
* Determine if the <code>MatParamOverride</code> is enabled or disabled.
*
* @return true if enabled, false if disabled.
* @see #setEnabled(boolean)
*/
public boolean isEnabled() {
return enabled;
}
/**
* Enable or disable this <code>MatParamOverride</code>.
*
* When disabled, the override will continue to propagate through the scene
* graph like before, but it will have no effect on materials. Overrides are
* enabled by default.
*
* @param enabled Whether to enable or disable this override.
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public void write(JmeExporter ex) throws IOException {
super.write(ex);
OutputCapsule oc = ex.getCapsule(this);
oc.write(enabled, "enabled", true);
}
@Override
public void read(JmeImporter im) throws IOException {
super.read(im);
InputCapsule ic = im.getCapsule(this);
enabled = ic.readBoolean("enabled", true);
}
}

@ -35,7 +35,6 @@ 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.renderer.Renderer;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ColorSpace;
@ -44,13 +43,11 @@ import java.io.IOException;
public class MatParamTexture extends MatParam { public class MatParamTexture extends MatParam {
private Texture texture; private Texture texture;
private int unit;
private ColorSpace colorSpace; private ColorSpace colorSpace;
public MatParamTexture(VarType type, String name, Texture texture, int unit, ColorSpace colorSpace) { public MatParamTexture(VarType type, String name, Texture texture, ColorSpace colorSpace) {
super(type, name, texture); super(type, name, texture);
this.texture = texture; this.texture = texture;
this.unit = unit;
this.colorSpace = colorSpace; this.colorSpace = colorSpace;
} }
@ -92,37 +89,18 @@ public class MatParamTexture extends MatParam {
this.colorSpace = colorSpace; this.colorSpace = colorSpace;
} }
public void setUnit(int unit) {
this.unit = unit;
}
public int getUnit() {
return unit;
}
@Override
public void apply(Renderer r, Technique technique) {
TechniqueDef techDef = technique.getDef();
r.setTexture(getUnit(), getTextureValue());
technique.updateUniformParam(getPrefixedName(), getVarType(), getUnit());
}
@Override @Override
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
super.write(ex); super.write(ex);
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);
oc.write(unit, "texture_unit", -1); oc.write(0, "texture_unit", -1);
oc.write(texture, "texture", null); // For backwards compatibility
// For backwards compat
oc.write(texture, "texture", null);
} }
@Override @Override
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
super.read(im); super.read(im);
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
unit = ic.readInt("texture_unit", -1);
texture = (Texture) value; texture = (Texture) value;
//texture = (Texture) ic.readSavable("texture", null);
} }
} }

@ -44,18 +44,16 @@ import com.jme3.math.*;
import com.jme3.renderer.Caps; import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer; import com.jme3.renderer.Renderer;
import com.jme3.renderer.RendererException;
import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.instancing.InstancedGeometry;
import com.jme3.shader.Shader; import com.jme3.shader.Shader;
import com.jme3.shader.Uniform; import com.jme3.shader.Uniform;
import com.jme3.shader.UniformBindingManager;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.texture.Image;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ColorSpace;
import com.jme3.util.ListMap; import com.jme3.util.ListMap;
import com.jme3.util.TempVars;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
@ -77,32 +75,18 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
// Version #2: Fixed issue with RenderState.apply*** flags not getting exported // Version #2: Fixed issue with RenderState.apply*** flags not getting exported
public static final int SAVABLE_VERSION = 2; public static final int SAVABLE_VERSION = 2;
private static final Logger logger = Logger.getLogger(Material.class.getName()); private static final Logger logger = Logger.getLogger(Material.class.getName());
private static final RenderState additiveLight = new RenderState();
private static final RenderState depthOnly = new RenderState();
private static final Quaternion nullDirLight = new Quaternion(0, -1, 0, -1);
static {
depthOnly.setDepthTest(true);
depthOnly.setDepthWrite(true);
depthOnly.setFaceCullMode(RenderState.FaceCullMode.Back);
depthOnly.setColorWrite(false);
additiveLight.setBlendMode(RenderState.BlendMode.AlphaAdditive);
additiveLight.setDepthWrite(false);
}
private AssetKey key; private AssetKey key;
private String name; private String name;
private MaterialDef def; private MaterialDef def;
private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>(); private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
private Technique technique; private Technique technique;
private HashMap<String, Technique> techniques = new HashMap<String, Technique>(); private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
private int nextTexUnit = 0;
private RenderState additionalState = null; private RenderState additionalState = null;
private RenderState mergedRenderState = new RenderState(); private RenderState mergedRenderState = new RenderState();
private boolean transparent = false; private boolean transparent = false;
private boolean receivesShadows = false; private boolean receivesShadows = false;
private int sortingId = -1; private int sortingId = -1;
private transient ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
public Material(MaterialDef def) { public Material(MaterialDef def) {
if (def == null) { if (def == null) {
@ -175,22 +159,29 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
* @return The sorting ID used for sorting geometries for rendering. * @return The sorting ID used for sorting geometries for rendering.
*/ */
public int getSortId() { public int getSortId() {
Technique t = getActiveTechnique(); if (sortingId == -1 && technique != null) {
if (sortingId == -1 && t != null && t.getShader() != null) { sortingId = technique.getSortId() << 16;
int texId = -1; int texturesSortId = 17;
for (int i = 0; i < paramValues.size(); i++) { for (int i = 0; i < paramValues.size(); i++) {
MatParam param = paramValues.getValue(i); MatParam param = paramValues.getValue(i);
if (param instanceof MatParamTexture) { if (!param.getVarType().isTextureType()) {
MatParamTexture tex = (MatParamTexture) param; continue;
if (tex.getTextureValue() != null && tex.getTextureValue().getImage() != null) { }
if (texId == -1) { Texture texture = (Texture) param.getValue();
texId = 0; if (texture == null) {
} continue;
texId += tex.getTextureValue().getImage().getId() % 0xff; }
} Image image = texture.getImage();
if (image == null) {
continue;
}
int textureId = image.getId();
if (textureId == -1) {
textureId = 0;
} }
texturesSortId = texturesSortId * 23 + textureId;
} }
sortingId = texId + t.getShader().getId() * 1000; sortingId |= texturesSortId & 0xFFFF;
} }
return sortingId; return sortingId;
} }
@ -215,6 +206,8 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
mat.paramValues.put(entry.getKey(), entry.getValue().clone()); mat.paramValues.put(entry.getKey(), entry.getValue().clone());
} }
mat.sortingId = -1;
return mat; return mat;
} catch (CloneNotSupportedException ex) { } catch (CloneNotSupportedException ex) {
throw new AssertionError(ex); throw new AssertionError(ex);
@ -258,8 +251,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
// E.g. if user chose custom technique for one material but // E.g. if user chose custom technique for one material but
// uses default technique for other material, the materials // uses default technique for other material, the materials
// are not equal. // are not equal.
String thisDefName = this.technique != null ? this.technique.getDef().getName() : "Default"; String thisDefName = this.technique != null
String otherDefName = other.technique != null ? other.technique.getDef().getName() : "Default"; ? this.technique.getDef().getName()
: TechniqueDef.DEFAULT_TECHNIQUE_NAME;
String otherDefName = other.technique != null
? other.technique.getDef().getName()
: TechniqueDef.DEFAULT_TECHNIQUE_NAME;
if (!thisDefName.equals(otherDefName)) { if (!thisDefName.equals(otherDefName)) {
return false; return false;
} }
@ -444,7 +443,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
* *
* @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object) * @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
*/ */
public ListMap getParamsMap() { public ListMap<String, MatParam> getParamsMap() {
return paramValues; return paramValues;
} }
@ -504,16 +503,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
paramValues.remove(name); paramValues.remove(name);
if (matParam instanceof MatParamTexture) { if (matParam instanceof MatParamTexture) {
int texUnit = ((MatParamTexture) matParam).getUnit();
nextTexUnit--;
for (MatParam param : paramValues.values()) {
if (param instanceof MatParamTexture) {
MatParamTexture texParam = (MatParamTexture) param;
if (texParam.getUnit() > texUnit) {
texParam.setUnit(texParam.getUnit() - 1);
}
}
}
sortingId = -1; sortingId = -1;
} }
if (technique != null) { if (technique != null) {
@ -556,13 +545,13 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
+ "Linear using texture.getImage.setColorSpace().", + "Linear using texture.getImage.setColorSpace().",
new Object[]{value.getName(), value.getImage().getColorSpace().name(), name}); new Object[]{value.getName(), value.getImage().getColorSpace().name(), name});
} }
paramValues.put(name, new MatParamTexture(type, name, value, nextTexUnit++, null)); paramValues.put(name, new MatParamTexture(type, name, value, null));
} else { } else {
val.setTextureValue(value); val.setTextureValue(value);
} }
if (technique != null) { if (technique != null) {
technique.notifyParamChanged(name, type, nextTexUnit - 1); technique.notifyParamChanged(name, type, value);
} }
// need to recompute sort ID // need to recompute sort ID
@ -695,277 +684,21 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
setParam(name, VarType.Vector4, value); setParam(name, VarType.Vector4, value);
} }
private ColorRGBA getAmbientColor(LightList lightList, boolean removeLights) {
ambientLightColor.set(0, 0, 0, 1);
for (int j = 0; j < lightList.size(); j++) {
Light l = lightList.get(j);
if (l instanceof AmbientLight) {
ambientLightColor.addLocal(l.getColor());
if(removeLights){
lightList.remove(l);
}
}
}
ambientLightColor.a = 1.0f;
return ambientLightColor;
}
private static void renderMeshFromGeometry(Renderer renderer, Geometry geom) {
Mesh mesh = geom.getMesh();
int lodLevel = geom.getLodLevel();
if (geom instanceof InstancedGeometry) {
InstancedGeometry instGeom = (InstancedGeometry) geom;
int numInstances = instGeom.getActualNumInstances();
if (numInstances == 0) {
return;
}
if (renderer.getCaps().contains(Caps.MeshInstancing)) {
renderer.renderMesh(mesh, lodLevel, numInstances, instGeom.getAllInstanceData());
} else {
throw new RendererException("Mesh instancing is not supported by the video hardware");
}
} else {
renderer.renderMesh(mesh, lodLevel, 1, null);
}
}
/**
* Uploads the lights in the light list as two uniform arrays.<br/><br/> *
* <p>
* <code>uniform vec4 g_LightColor[numLights];</code><br/> //
* g_LightColor.rgb is the diffuse/specular color of the light.<br/> //
* g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/> //
* 2 = Spot. <br/> <br/>
* <code>uniform vec4 g_LightPosition[numLights];</code><br/> //
* g_LightPosition.xyz is the position of the light (for point lights)<br/>
* // or the direction of the light (for directional lights).<br/> //
* g_LightPosition.w is the inverse radius (1/r) of the light (for
* attenuation) <br/> </p>
*/
protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex) {
if (numLights == 0) { // this shader does not do lighting, ignore.
return 0;
}
Uniform lightData = shader.getUniform("g_LightData");
lightData.setVector4Length(numLights * 3);//8 lights * max 3
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
if (startIndex != 0) {
// apply additive blending for 2nd and future passes
rm.getRenderer().applyRenderState(additiveLight);
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
}else{
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList,true));
}
int lightDataIndex = 0;
TempVars vars = TempVars.get();
Vector4f tmpVec = vars.vect4f1;
int curIndex;
int endIndex = numLights + startIndex;
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
Light l = lightList.get(curIndex);
if(l.getType() == Light.Type.Ambient){
endIndex++;
continue;
}
ColorRGBA color = l.getColor();
//Color
lightData.setVector4InArray(color.getRed(),
color.getGreen(),
color.getBlue(),
l.getType().getId(),
lightDataIndex);
lightDataIndex++;
switch (l.getType()) {
case Directional:
DirectionalLight dl = (DirectionalLight) l;
Vector3f dir = dl.getDirection();
//Data directly sent in view space to avoid a matrix mult for each pixel
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
// tmpVec.divideLocal(tmpVec.w);
// tmpVec.normalizeLocal();
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
lightDataIndex++;
//PADDING
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
lightDataIndex++;
break;
case Point:
PointLight pl = (PointLight) l;
Vector3f pos = pl.getPosition();
float invRadius = pl.getInvRadius();
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
//tmpVec.divideLocal(tmpVec.w);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
lightDataIndex++;
//PADDING
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
lightDataIndex++;
break;
case Spot:
SpotLight sl = (SpotLight) l;
Vector3f pos2 = sl.getPosition();
Vector3f dir2 = sl.getDirection();
float invRange = sl.getInvSpotRange();
float spotAngleCos = sl.getPackedAngleCos();
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
// tmpVec.divideLocal(tmpVec.w);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
lightDataIndex++;
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
//one vec4 less and a vec4 that becomes a vec3
//the downside is that spotAngleCos decoding happens now in the frag shader.
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
tmpVec.normalizeLocal();
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
lightDataIndex++;
break;
default:
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
}
}
vars.release();
//Padding of unsued buffer space
while(lightDataIndex < numLights * 3) {
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
lightDataIndex++;
}
return curIndex;
}
protected void renderMultipassLighting(Shader shader, Geometry g, LightList lightList, RenderManager rm) {
Renderer r = rm.getRenderer();
Uniform lightDir = shader.getUniform("g_LightDirection");
Uniform lightColor = shader.getUniform("g_LightColor");
Uniform lightPos = shader.getUniform("g_LightPosition");
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
boolean isFirstLight = true;
boolean isSecondLight = false;
for (int i = 0; i < lightList.size(); i++) {
Light l = lightList.get(i);
if (l instanceof AmbientLight) {
continue;
}
if (isFirstLight) {
// set ambient color for first light only
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, false));
isFirstLight = false;
isSecondLight = true;
} else if (isSecondLight) {
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
// apply additive blending for 2nd and future lights
r.applyRenderState(additiveLight);
isSecondLight = false;
}
TempVars vars = TempVars.get();
Quaternion tmpLightDirection = vars.quat1;
Quaternion tmpLightPosition = vars.quat2;
ColorRGBA tmpLightColor = vars.color;
Vector4f tmpVec = vars.vect4f1;
ColorRGBA color = l.getColor();
tmpLightColor.set(color);
tmpLightColor.a = l.getType().getId();
lightColor.setValue(VarType.Vector4, tmpLightColor);
switch (l.getType()) {
case Directional:
DirectionalLight dl = (DirectionalLight) l;
Vector3f dir = dl.getDirection();
//FIXME : there is an inconstency here due to backward
//compatibility of the lighting shader.
//The directional light direction is passed in the
//LightPosition uniform. The lighting shader needs to be
//reworked though in order to fix this.
tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
lightPos.setValue(VarType.Vector4, tmpLightPosition);
tmpLightDirection.set(0, 0, 0, 0);
lightDir.setValue(VarType.Vector4, tmpLightDirection);
break;
case Point:
PointLight pl = (PointLight) l;
Vector3f pos = pl.getPosition();
float invRadius = pl.getInvRadius();
tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
lightPos.setValue(VarType.Vector4, tmpLightPosition);
tmpLightDirection.set(0, 0, 0, 0);
lightDir.setValue(VarType.Vector4, tmpLightDirection);
break;
case Spot:
SpotLight sl = (SpotLight) l;
Vector3f pos2 = sl.getPosition();
Vector3f dir2 = sl.getDirection();
float invRange = sl.getInvSpotRange();
float spotAngleCos = sl.getPackedAngleCos();
tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
lightPos.setValue(VarType.Vector4, tmpLightPosition);
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
//one vec4 less and a vec4 that becomes a vec3
//the downside is that spotAngleCos decoding happens now in the frag shader.
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
lightDir.setValue(VarType.Vector4, tmpLightDirection);
break;
default:
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
}
vars.release();
r.setShader(shader);
renderMeshFromGeometry(r, g);
}
if (isFirstLight) {
// Either there are no lights at all, or only ambient lights.
// Render a dummy "normal light" so we can see the ambient color.
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, false));
lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
lightPos.setValue(VarType.Vector4, nullDirLight);
r.setShader(shader);
renderMeshFromGeometry(r, g);
}
}
/** /**
* Select the technique to use for rendering this material. * Select the technique to use for rendering this material.
* <p> * <p>
* If <code>name</code> is "Default", then one of the
* {@link MaterialDef#getDefaultTechniques() default techniques}
* on the material will be selected. Otherwise, the named technique
* will be found in the material definition.
* <p>
* Any candidate technique for selection (either default or named) * Any candidate technique for selection (either default or named)
* must be verified to be compatible with the system, for that, the * must be verified to be compatible with the system, for that, the
* <code>renderManager</code> is queried for capabilities. * <code>renderManager</code> is queried for capabilities.
* *
* @param name The name of the technique to select, pass "Default" to * @param name The name of the technique to select, pass
* select one of the default techniques. * {@link TechniqueDef#DEFAULT_TECHNIQUE_NAME} to select one of the default
* techniques.
* @param renderManager The {@link RenderManager render manager} * @param renderManager The {@link RenderManager render manager}
* to query for capabilities. * to query for capabilities.
* *
* @throws IllegalArgumentException If "Default" is passed and no default * @throws IllegalArgumentException If no technique exists with the given
* techniques are available on the material definition, or if a name * name.
* is passed but there's no technique by that name.
* @throws UnsupportedOperationException If no candidate technique supports * @throws UnsupportedOperationException If no candidate technique supports
* the system capabilities. * the system capabilities.
*/ */
@ -974,49 +707,34 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
Technique tech = techniques.get(name); Technique tech = techniques.get(name);
// When choosing technique, we choose one that // When choosing technique, we choose one that
// supports all the caps. // supports all the caps.
EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
if (tech == null) { if (tech == null) {
EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
List<TechniqueDef> techDefs = def.getTechniqueDefs(name);
if (name.equals("Default")) { if (techDefs == null || techDefs.isEmpty()) {
List<TechniqueDef> techDefs = def.getDefaultTechniques(); throw new IllegalArgumentException(
if (techDefs == null || techDefs.isEmpty()) { String.format("The requested technique %s is not available on material %s", name, def.getName()));
throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'"); }
}
TechniqueDef lastTech = null; TechniqueDef lastTech = null;
for (TechniqueDef techDef : techDefs) { for (TechniqueDef techDef : techDefs) {
if (rendererCaps.containsAll(techDef.getRequiredCaps())) { if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
// use the first one that supports all the caps // use the first one that supports all the caps
tech = new Technique(this, techDef); tech = new Technique(this, techDef);
techniques.put(name, tech); techniques.put(name, tech);
if(tech.getDef().getLightMode() == renderManager.getPreferredLightMode() || if (tech.getDef().getLightMode() == renderManager.getPreferredLightMode()
tech.getDef().getLightMode() == LightMode.Disable){ || tech.getDef().getLightMode() == LightMode.Disable) {
break; break;
}
} }
lastTech = techDef;
}
if (tech == null) {
throw new UnsupportedOperationException("No default technique on material '" + def.getName() + "'\n"
+ " is supported by the video hardware. The caps "
+ lastTech.getRequiredCaps() + " are required.");
}
} else {
// create "special" technique instance
TechniqueDef techDef = def.getTechniqueDef(name);
if (techDef == null) {
throw new IllegalArgumentException("For material " + def.getName() + ", technique not found: " + name);
}
if (!rendererCaps.containsAll(techDef.getRequiredCaps())) {
throw new UnsupportedOperationException("The explicitly chosen technique '" + name + "' on material '" + def.getName() + "'\n"
+ "requires caps " + techDef.getRequiredCaps() + " which are not "
+ "supported by the video renderer");
} }
lastTech = techDef;
tech = new Technique(this, techDef); }
techniques.put(name, tech); if (tech == null) {
throw new UnsupportedOperationException(
String.format("No technique '%s' on material "
+ "'%s' is supported by the video hardware. "
+ "The capabilities %s are required.",
name, def.getName(), lastTech.getRequiredCaps()));
} }
} else if (technique == tech) { } else if (technique == tech) {
// attempting to switch to an already // attempting to switch to an already
@ -1025,20 +743,82 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
} }
technique = tech; technique = tech;
tech.makeCurrent(def.getAssetManager(), true, rendererCaps, renderManager); tech.notifyTechniqueSwitched();
// shader was changed // shader was changed
sortingId = -1; sortingId = -1;
} }
private void autoSelectTechnique(RenderManager rm) { private int applyOverrides(Renderer renderer, Shader shader, List<MatParamOverride> overrides, int unit) {
if (technique == null) { for (MatParamOverride override : overrides) {
selectTechnique("Default", rm); VarType type = override.getVarType();
} else {
technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps(), rm); MatParam paramDef = def.getMaterialParam(override.getName());
if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) {
continue;
}
Uniform uniform = shader.getUniform(override.getPrefixedName());
if (override.getValue() != null) {
if (type.isTextureType()) {
renderer.setTexture(unit, (Texture) override.getValue());
uniform.setValue(VarType.Int, unit);
unit++;
} else {
uniform.setValue(type, override.getValue());
}
} else {
uniform.clearValue();
}
} }
return unit;
} }
private void updateShaderMaterialParameters(Renderer renderer, Shader shader,
List<MatParamOverride> worldOverrides, List<MatParamOverride> forcedOverrides) {
int unit = 0;
if (worldOverrides != null) {
unit = applyOverrides(renderer, shader, worldOverrides, unit);
}
if (forcedOverrides != null) {
unit = applyOverrides(renderer, shader, forcedOverrides, unit);
}
for (int i = 0; i < paramValues.size(); i++) {
MatParam param = paramValues.getValue(i);
VarType type = param.getVarType();
Uniform uniform = shader.getUniform(param.getPrefixedName());
if (uniform.isSetByCurrentMaterial()) {
continue;
}
if (type.isTextureType()) {
renderer.setTexture(unit, (Texture) param.getValue());
uniform.setValue(VarType.Int, unit);
unit++;
} else {
uniform.setValue(type, param.getValue());
}
}
}
private void updateRenderState(RenderManager renderManager, Renderer renderer, TechniqueDef techniqueDef) {
if (renderManager.getForcedRenderState() != null) {
renderer.applyRenderState(renderManager.getForcedRenderState());
} else {
if (techniqueDef.getRenderState() != null) {
renderer.applyRenderState(techniqueDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
} else {
renderer.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
}
}
}
/** /**
* Preloads this material for the given render manager. * Preloads this material for the given render manager.
* <p> * <p>
@ -1046,20 +826,23 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
* used for rendering, there won't be any delay since the material has * used for rendering, there won't be any delay since the material has
* been already been setup for rendering. * been already been setup for rendering.
* *
* @param rm The render manager to preload for * @param renderManager The render manager to preload for
*/ */
public void preload(RenderManager rm) { public void preload(RenderManager renderManager) {
autoSelectTechnique(rm); if (technique == null) {
selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
Renderer r = rm.getRenderer(); }
TechniqueDef techDef = technique.getDef(); TechniqueDef techniqueDef = technique.getDef();
Renderer renderer = renderManager.getRenderer();
EnumSet<Caps> rendererCaps = renderer.getCaps();
Collection<MatParam> params = paramValues.values(); if (techniqueDef.isNoRender()) {
for (MatParam param : params) { return;
param.apply(r, technique);
} }
r.setShader(technique.getShader()); Shader shader = technique.makeCurrent(renderManager, null, null, null, rendererCaps);
updateShaderMaterialParameters(renderer, shader, null, null);
renderManager.getRenderer().setShader(shader);
} }
private void clearUniformsSetByCurrent(Shader shader) { private void clearUniformsSetByCurrent(Shader shader) {
@ -1141,80 +924,46 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
* </ul> * </ul>
* </ul> * </ul>
* *
* @param geom The geometry to render * @param geometry The geometry to render
* @param lights Presorted and filtered light list to use for rendering * @param lights Presorted and filtered light list to use for rendering
* @param rm The render manager requesting the rendering * @param renderManager The render manager requesting the rendering
*/ */
public void render(Geometry geom, LightList lights, RenderManager rm) { public void render(Geometry geometry, LightList lights, RenderManager renderManager) {
autoSelectTechnique(rm); if (technique == null) {
TechniqueDef techDef = technique.getDef(); selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
if (techDef.isNoRender()) return;
Renderer r = rm.getRenderer();
if (rm.getForcedRenderState() != null) {
r.applyRenderState(rm.getForcedRenderState());
} else {
if (techDef.getRenderState() != null) {
r.applyRenderState(techDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
} else {
r.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
}
}
// update camera and world matrices
// NOTE: setWorldTransform should have been called already
// reset unchanged uniform flag
clearUniformsSetByCurrent(technique.getShader());
rm.updateUniformBindings(technique.getWorldBindUniforms());
// setup textures and uniforms
for (int i = 0; i < paramValues.size(); i++) {
MatParam param = paramValues.getValue(i);
param.apply(r, technique);
} }
Shader shader = technique.getShader(); TechniqueDef techniqueDef = technique.getDef();
Renderer renderer = renderManager.getRenderer();
// send lighting information, if needed EnumSet<Caps> rendererCaps = renderer.getCaps();
switch (techDef.getLightMode()) {
case Disable: if (techniqueDef.isNoRender()) {
break; return;
case SinglePass:
int nbRenderedLights = 0;
resetUniformsNotSetByCurrent(shader);
if (lights.size() == 0) {
nbRenderedLights = updateLightListUniforms(shader, geom, lights, rm.getSinglePassLightBatchSize(), rm, 0);
r.setShader(shader);
renderMeshFromGeometry(r, geom);
} else {
while (nbRenderedLights < lights.size()) {
nbRenderedLights = updateLightListUniforms(shader, geom, lights, rm.getSinglePassLightBatchSize(), rm, nbRenderedLights);
r.setShader(shader);
renderMeshFromGeometry(r, geom);
}
}
return;
case FixedPipeline:
throw new IllegalArgumentException("OpenGL1 is not supported");
case MultiPass:
// NOTE: Special case!
resetUniformsNotSetByCurrent(shader);
renderMultipassLighting(shader, geom, lights, rm);
// very important, notice the return statement!
return;
} }
// upload and bind shader // Apply render state
// any unset uniforms will be set to 0 updateRenderState(renderManager, renderer, techniqueDef);
// Get world overrides
List<MatParamOverride> overrides = geometry.getWorldMatParamOverrides();
// Select shader to use
Shader shader = technique.makeCurrent(renderManager, overrides, renderManager.getForcedMatParams(), lights, rendererCaps);
// Begin tracking which uniforms were changed by material.
clearUniformsSetByCurrent(shader);
// Set uniform bindings
renderManager.updateUniformBindings(shader);
// Set material parameters
updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams());
// Clear any uniforms not changed by material.
resetUniformsNotSetByCurrent(shader); resetUniformsNotSetByCurrent(shader);
r.setShader(shader);
// Delegate rendering to the technique
renderMeshFromGeometry(r, geom); technique.render(renderManager, shader, geometry, lights);
} }
/** /**
@ -1239,6 +988,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
oc.write(name, "name", null); oc.write(name, "name", null);
oc.writeStringSavableMap(paramValues, "parameters", null); oc.writeStringSavableMap(paramValues, "parameters", null);
} }
@Override
public String toString() {
return "Material[name=" + name +
", def=" + def.getName() +
", tech=" + technique.getDef().getName() +
"]";
}
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
@ -1296,11 +1053,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
MatParam param = entry.getValue(); MatParam param = entry.getValue();
if (param instanceof MatParamTexture) { if (param instanceof MatParamTexture) {
MatParamTexture texVal = (MatParamTexture) param; MatParamTexture texVal = (MatParamTexture) param;
if (nextTexUnit < texVal.getUnit() + 1) {
nextTexUnit = texVal.getUnit() + 1;
}
// the texture failed to load for this param // the texture failed to load for this param
// do not add to param values // do not add to param values
if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) { if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) {
@ -1335,14 +1087,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
// Try to guess values of "apply" render state based on defaults // Try to guess values of "apply" render state based on defaults
// if value != default then set apply to true // if value != default then set apply to true
additionalState.applyPolyOffset = additionalState.offsetEnabled; additionalState.applyPolyOffset = additionalState.offsetEnabled;
additionalState.applyAlphaFallOff = additionalState.alphaTest;
additionalState.applyAlphaTest = additionalState.alphaTest;
additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off; additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off;
additionalState.applyColorWrite = !additionalState.colorWrite; additionalState.applyColorWrite = !additionalState.colorWrite;
additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back; additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back;
additionalState.applyDepthTest = !additionalState.depthTest; additionalState.applyDepthTest = !additionalState.depthTest;
additionalState.applyDepthWrite = !additionalState.depthWrite; additionalState.applyDepthWrite = !additionalState.depthWrite;
additionalState.applyPointSprite = additionalState.pointSprite;
additionalState.applyStencilTest = additionalState.stencilTest; additionalState.applyStencilTest = additionalState.stencilTest;
additionalState.applyWireFrame = additionalState.wireframe; additionalState.applyWireFrame = additionalState.wireframe;
} }

@ -32,6 +32,7 @@
package com.jme3.material; package com.jme3.material;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.renderer.Caps;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ColorSpace;
import java.util.*; import java.util.*;
@ -51,8 +52,7 @@ public class MaterialDef {
private String assetName; private String assetName;
private AssetManager assetManager; private AssetManager assetManager;
private List<TechniqueDef> defaultTechs; private Map<String, List<TechniqueDef>> techniques;
private Map<String, TechniqueDef> techniques;
private Map<String, MatParam> matParams; private Map<String, MatParam> matParams;
/** /**
@ -70,9 +70,8 @@ public class MaterialDef {
public MaterialDef(AssetManager assetManager, String name){ public MaterialDef(AssetManager assetManager, String name){
this.assetManager = assetManager; this.assetManager = assetManager;
this.name = name; this.name = name;
techniques = new HashMap<String, TechniqueDef>(); techniques = new HashMap<String, List<TechniqueDef>>();
matParams = new HashMap<String, MatParam>(); matParams = new HashMap<String, MatParam>();
defaultTechs = new ArrayList<TechniqueDef>();
logger.log(Level.FINE, "Loaded material definition: {0}", name); logger.log(Level.FINE, "Loaded material definition: {0}", name);
} }
@ -135,7 +134,7 @@ public class MaterialDef {
* @see ColorSpace * @see ColorSpace
*/ */
public void addMaterialParamTexture(VarType type, String name, ColorSpace colorSpace) { public void addMaterialParamTexture(VarType type, String name, ColorSpace colorSpace) {
matParams.put(name, new MatParamTexture(type, name, null , 0, colorSpace)); matParams.put(name, new MatParamTexture(type, name, null, colorSpace));
} }
/** /**
@ -164,40 +163,26 @@ public class MaterialDef {
/** /**
* Adds a new technique definition to this material definition. * Adds a new technique definition to this material definition.
* <p> *
* If the technique name is "Default", it will be added
* to the list of {@link MaterialDef#getDefaultTechniques() default techniques}.
*
* @param technique The technique definition to add. * @param technique The technique definition to add.
*/ */
public void addTechniqueDef(TechniqueDef technique) { public void addTechniqueDef(TechniqueDef technique) {
if (technique.getName().equals("Default")) { List<TechniqueDef> list = techniques.get(technique.getName());
defaultTechs.add(technique); if (list == null) {
} else { list = new ArrayList<>();
techniques.put(technique.getName(), technique); techniques.put(technique.getName(), list);
} }
list.add(technique);
} }
/** /**
* Returns a list of all default techniques. * Returns technique definitions with the given name.
* *
* @return a list of all default techniques. * @param name The name of the technique definitions to find
*/ *
public List<TechniqueDef> getDefaultTechniques(){ * @return The technique definitions, or null if cannot be found.
return defaultTechs;
}
/**
* Returns a technique definition with the given name.
* This does not include default techniques which can be
* retrieved via {@link MaterialDef#getDefaultTechniques() }.
*
* @param name The name of the technique definition to find
*
* @return The technique definition, or null if cannot be found.
*/ */
public TechniqueDef getTechniqueDef(String name) { public List<TechniqueDef> getTechniqueDefs(String name) {
return techniques.get(name); return techniques.get(name);
} }
} }

@ -75,12 +75,11 @@ public class RenderState implements Cloneable, Savable {
/** /**
* <code>TestFunction</code> specifies the testing function for stencil test * <code>TestFunction</code> specifies the testing function for stencil test
* function and alpha test function. * function.
* *
* <p>The functions work similarly as described except that for stencil * <p>
* test function, the reference value given in the stencil command is * The reference value given in the stencil command is the input value while
* the input value while the reference is the value already in the stencil * the reference is the value already in the stencil buffer.
* buffer.
*/ */
public enum TestFunction { public enum TestFunction {
@ -118,7 +117,94 @@ public class RenderState implements Cloneable, Savable {
/** /**
* The test always passes * The test always passes
*/ */
Always,} Always
}
/**
* <code>BlendEquation</code> specifies the blending equation to combine
* pixels.
*/
public enum BlendEquation {
/**
* Sets the blend equation so that the source and destination data are
* added. (Default) Clamps to [0,1] Useful for things like antialiasing
* and transparency.
*/
Add,
/**
* Sets the blend equation so that the source and destination data are
* subtracted (Src - Dest). Clamps to [0,1] Falls back to Add if
* supportsSubtract is false.
*/
Subtract,
/**
* Same as Subtract, but the order is reversed (Dst - Src). Clamps to
* [0,1] Falls back to Add if supportsSubtract is false.
*/
ReverseSubtract,
/**
* Sets the blend equation so that each component of the result color is
* the minimum of the corresponding components of the source and
* destination colors. This and Max are useful for applications that
* analyze image data (image thresholding against a constant color, for
* example). Falls back to Add if supportsMinMax is false.
*/
Min,
/**
* Sets the blend equation so that each component of the result color is
* the maximum of the corresponding components of the source and
* destination colors. This and Min are useful for applications that
* analyze image data (image thresholding against a constant color, for
* example). Falls back to Add if supportsMinMax is false.
*/
Max
}
/**
* <code>BlendEquationAlpha</code> specifies the blending equation to
* combine pixels for the alpha component.
*/
public enum BlendEquationAlpha {
/**
* Sets the blend equation to be the same as the one defined by
* {@link #blendEquation}.
*
*/
InheritColor,
/**
* Sets the blend equation so that the source and destination data are
* added. (Default) Clamps to [0,1] Useful for things like antialiasing
* and transparency.
*/
Add,
/**
* Sets the blend equation so that the source and destination data are
* subtracted (Src - Dest). Clamps to [0,1] Falls back to Add if
* supportsSubtract is false.
*/
Subtract,
/**
* Same as Subtract, but the order is reversed (Dst - Src). Clamps to
* [0,1] Falls back to Add if supportsSubtract is false.
*/
ReverseSubtract,
/**
* Sets the blend equation so that the result alpha is the minimum of
* the source alpha and destination alpha. This and Max are useful for
* applications that analyze image data (image thresholding against a
* constant color, for example). Falls back to Add if supportsMinMax is
* false.
*/
Min,
/**
* sSets the blend equation so that the result alpha is the maximum of
* the source alpha and destination alpha. This and Min are useful for
* applications that analyze image data (image thresholding against a
* constant color, for example). Falls back to Add if supportsMinMax is
* false.
*/
Max
}
/** /**
* <code>BlendMode</code> specifies the blending operation to use. * <code>BlendMode</code> specifies the blending operation to use.
@ -276,19 +362,16 @@ public class RenderState implements Cloneable, Savable {
} }
static { static {
ADDITIONAL.applyPointSprite = false;
ADDITIONAL.applyWireFrame = false; ADDITIONAL.applyWireFrame = false;
ADDITIONAL.applyCullMode = false; ADDITIONAL.applyCullMode = false;
ADDITIONAL.applyDepthWrite = false; ADDITIONAL.applyDepthWrite = false;
ADDITIONAL.applyDepthTest = false; ADDITIONAL.applyDepthTest = false;
ADDITIONAL.applyColorWrite = false; ADDITIONAL.applyColorWrite = false;
ADDITIONAL.applyBlendEquation = false;
ADDITIONAL.applyBlendEquationAlpha = false;
ADDITIONAL.applyBlendMode = false; ADDITIONAL.applyBlendMode = false;
ADDITIONAL.applyAlphaTest = false;
ADDITIONAL.applyAlphaFallOff = false;
ADDITIONAL.applyPolyOffset = false; ADDITIONAL.applyPolyOffset = false;
} }
boolean pointSprite = false;
boolean applyPointSprite = true;
boolean wireframe = false; boolean wireframe = false;
boolean applyWireFrame = true; boolean applyWireFrame = true;
FaceCullMode cullMode = FaceCullMode.Back; FaceCullMode cullMode = FaceCullMode.Back;
@ -299,12 +382,12 @@ public class RenderState implements Cloneable, Savable {
boolean applyDepthTest = true; boolean applyDepthTest = true;
boolean colorWrite = true; boolean colorWrite = true;
boolean applyColorWrite = true; boolean applyColorWrite = true;
BlendEquation blendEquation = BlendEquation.Add;
boolean applyBlendEquation = true;
BlendEquationAlpha blendEquationAlpha = BlendEquationAlpha.InheritColor;
boolean applyBlendEquationAlpha = true;
BlendMode blendMode = BlendMode.Off; BlendMode blendMode = BlendMode.Off;
boolean applyBlendMode = true; boolean applyBlendMode = true;
boolean alphaTest = false;
boolean applyAlphaTest = true;
float alphaFallOff = 0;
boolean applyAlphaFallOff = true;
float offsetFactor = 0; float offsetFactor = 0;
float offsetUnits = 0; float offsetUnits = 0;
boolean offsetEnabled = false; boolean offsetEnabled = false;
@ -315,10 +398,7 @@ public class RenderState implements Cloneable, Savable {
boolean applyLineWidth = false; boolean applyLineWidth = false;
TestFunction depthFunc = TestFunction.LessOrEqual; TestFunction depthFunc = TestFunction.LessOrEqual;
//by default depth func will be applied anyway if depth test is applied //by default depth func will be applied anyway if depth test is applied
boolean applyDepthFunc = false; boolean applyDepthFunc = false;
//by default alpha func will be applied anyway if alpha test is applied
TestFunction alphaFunc = TestFunction.Greater;
boolean applyAlphaFunc = false;
StencilOperation frontStencilStencilFailOperation = StencilOperation.Keep; StencilOperation frontStencilStencilFailOperation = StencilOperation.Keep;
StencilOperation frontStencilDepthFailOperation = StencilOperation.Keep; StencilOperation frontStencilDepthFailOperation = StencilOperation.Keep;
StencilOperation frontStencilDepthPassOperation = StencilOperation.Keep; StencilOperation frontStencilDepthPassOperation = StencilOperation.Keep;
@ -331,15 +411,13 @@ public class RenderState implements Cloneable, Savable {
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);
oc.write(pointSprite, "pointSprite", false); oc.write(true, "pointSprite", false);
oc.write(wireframe, "wireframe", false); oc.write(wireframe, "wireframe", false);
oc.write(cullMode, "cullMode", FaceCullMode.Back); oc.write(cullMode, "cullMode", FaceCullMode.Back);
oc.write(depthWrite, "depthWrite", true); oc.write(depthWrite, "depthWrite", true);
oc.write(depthTest, "depthTest", true); oc.write(depthTest, "depthTest", true);
oc.write(colorWrite, "colorWrite", true); oc.write(colorWrite, "colorWrite", true);
oc.write(blendMode, "blendMode", BlendMode.Off); oc.write(blendMode, "blendMode", BlendMode.Off);
oc.write(alphaTest, "alphaTest", false);
oc.write(alphaFallOff, "alphaFallOff", 0);
oc.write(offsetEnabled, "offsetEnabled", false); oc.write(offsetEnabled, "offsetEnabled", false);
oc.write(offsetFactor, "offsetFactor", 0); oc.write(offsetFactor, "offsetFactor", 0);
oc.write(offsetUnits, "offsetUnits", 0); oc.write(offsetUnits, "offsetUnits", 0);
@ -352,38 +430,34 @@ public class RenderState implements Cloneable, Savable {
oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep); oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep);
oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always); oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always);
oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always); oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always);
oc.write(blendEquation, "blendEquation", BlendEquation.Add);
oc.write(blendEquationAlpha, "blendEquationAlpha", BlendEquationAlpha.InheritColor);
oc.write(depthFunc, "depthFunc", TestFunction.LessOrEqual); oc.write(depthFunc, "depthFunc", TestFunction.LessOrEqual);
oc.write(alphaFunc, "alphaFunc", TestFunction.Greater);
oc.write(lineWidth, "lineWidth", 1); oc.write(lineWidth, "lineWidth", 1);
// Only "additional render state" has them set to false by default // Only "additional render state" has them set to false by default
oc.write(applyPointSprite, "applyPointSprite", true);
oc.write(applyWireFrame, "applyWireFrame", true); oc.write(applyWireFrame, "applyWireFrame", true);
oc.write(applyCullMode, "applyCullMode", true); oc.write(applyCullMode, "applyCullMode", true);
oc.write(applyDepthWrite, "applyDepthWrite", true); oc.write(applyDepthWrite, "applyDepthWrite", true);
oc.write(applyDepthTest, "applyDepthTest", true); oc.write(applyDepthTest, "applyDepthTest", true);
oc.write(applyColorWrite, "applyColorWrite", true); oc.write(applyColorWrite, "applyColorWrite", true);
oc.write(applyBlendEquation, "applyBlendEquation", true);
oc.write(applyBlendEquationAlpha, "applyBlendEquationAlpha", true);
oc.write(applyBlendMode, "applyBlendMode", true); oc.write(applyBlendMode, "applyBlendMode", true);
oc.write(applyAlphaTest, "applyAlphaTest", true);
oc.write(applyAlphaFallOff, "applyAlphaFallOff", true);
oc.write(applyPolyOffset, "applyPolyOffset", true); oc.write(applyPolyOffset, "applyPolyOffset", true);
oc.write(applyDepthFunc, "applyDepthFunc", true); oc.write(applyDepthFunc, "applyDepthFunc", true);
oc.write(applyAlphaFunc, "applyAlphaFunc", false);
oc.write(applyLineWidth, "applyLineWidth", true); oc.write(applyLineWidth, "applyLineWidth", true);
} }
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
pointSprite = ic.readBoolean("pointSprite", false);
wireframe = ic.readBoolean("wireframe", false); wireframe = ic.readBoolean("wireframe", false);
cullMode = ic.readEnum("cullMode", FaceCullMode.class, FaceCullMode.Back); cullMode = ic.readEnum("cullMode", FaceCullMode.class, FaceCullMode.Back);
depthWrite = ic.readBoolean("depthWrite", true); depthWrite = ic.readBoolean("depthWrite", true);
depthTest = ic.readBoolean("depthTest", true); depthTest = ic.readBoolean("depthTest", true);
colorWrite = ic.readBoolean("colorWrite", true); colorWrite = ic.readBoolean("colorWrite", true);
blendMode = ic.readEnum("blendMode", BlendMode.class, BlendMode.Off); blendMode = ic.readEnum("blendMode", BlendMode.class, BlendMode.Off);
alphaTest = ic.readBoolean("alphaTest", false);
alphaFallOff = ic.readFloat("alphaFallOff", 0);
offsetEnabled = ic.readBoolean("offsetEnabled", false); offsetEnabled = ic.readBoolean("offsetEnabled", false);
offsetFactor = ic.readFloat("offsetFactor", 0); offsetFactor = ic.readFloat("offsetFactor", 0);
offsetUnits = ic.readFloat("offsetUnits", 0); offsetUnits = ic.readFloat("offsetUnits", 0);
@ -396,23 +470,22 @@ public class RenderState implements Cloneable, Savable {
backStencilDepthPassOperation = ic.readEnum("backStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep); backStencilDepthPassOperation = ic.readEnum("backStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep);
frontStencilFunction = ic.readEnum("frontStencilFunction", TestFunction.class, TestFunction.Always); frontStencilFunction = ic.readEnum("frontStencilFunction", TestFunction.class, TestFunction.Always);
backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always); backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always);
blendEquation = ic.readEnum("blendEquation", BlendEquation.class, BlendEquation.Add);
blendEquationAlpha = ic.readEnum("blendEquationAlpha", BlendEquationAlpha.class, BlendEquationAlpha.InheritColor);
depthFunc = ic.readEnum("depthFunc", TestFunction.class, TestFunction.LessOrEqual); depthFunc = ic.readEnum("depthFunc", TestFunction.class, TestFunction.LessOrEqual);
alphaFunc = ic.readEnum("alphaFunc", TestFunction.class, TestFunction.Greater);
lineWidth = ic.readFloat("lineWidth", 1); lineWidth = ic.readFloat("lineWidth", 1);
applyPointSprite = ic.readBoolean("applyPointSprite", true);
applyWireFrame = ic.readBoolean("applyWireFrame", true); applyWireFrame = ic.readBoolean("applyWireFrame", true);
applyCullMode = ic.readBoolean("applyCullMode", true); applyCullMode = ic.readBoolean("applyCullMode", true);
applyDepthWrite = ic.readBoolean("applyDepthWrite", true); applyDepthWrite = ic.readBoolean("applyDepthWrite", true);
applyDepthTest = ic.readBoolean("applyDepthTest", true); applyDepthTest = ic.readBoolean("applyDepthTest", true);
applyColorWrite = ic.readBoolean("applyColorWrite", true); applyColorWrite = ic.readBoolean("applyColorWrite", true);
applyBlendEquation = ic.readBoolean("applyBlendEquation", true);
applyBlendEquationAlpha = ic.readBoolean("applyBlendEquationAlpha", true);
applyBlendMode = ic.readBoolean("applyBlendMode", true); applyBlendMode = ic.readBoolean("applyBlendMode", true);
applyAlphaTest = ic.readBoolean("applyAlphaTest", true);
applyAlphaFallOff = ic.readBoolean("applyAlphaFallOff", true);
applyPolyOffset = ic.readBoolean("applyPolyOffset", true); applyPolyOffset = ic.readBoolean("applyPolyOffset", true);
applyDepthFunc = ic.readBoolean("applyDepthFunc", true); applyDepthFunc = ic.readBoolean("applyDepthFunc", true);
applyAlphaFunc = ic.readBoolean("applyAlphaFunc", false);
applyLineWidth = ic.readBoolean("applyLineWidth", true); applyLineWidth = ic.readBoolean("applyLineWidth", true);
@ -433,8 +506,8 @@ public class RenderState implements Cloneable, Savable {
} }
/** /**
* returns true if the given renderState is equall to this one * returns true if the given renderState is equal to this one
* @param o the renderState to compate to * @param o the renderState to compare to
* @return true if the renderStates are equal * @return true if the renderStates are equal
*/ */
@Override @Override
@ -446,9 +519,6 @@ public class RenderState implements Cloneable, Savable {
return false; return false;
} }
RenderState rs = (RenderState) o; RenderState rs = (RenderState) o;
if (pointSprite != rs.pointSprite) {
return false;
}
if (wireframe != rs.wireframe) { if (wireframe != rs.wireframe) {
return false; return false;
@ -475,23 +545,19 @@ public class RenderState implements Cloneable, Savable {
return false; return false;
} }
if (blendMode != rs.blendMode) { if (blendEquation != rs.blendEquation) {
return false; return false;
} }
if (alphaTest != rs.alphaTest) { if (blendEquationAlpha != rs.blendEquationAlpha) {
return false; return false;
} }
if (alphaTest) {
if (alphaFunc != rs.alphaFunc) {
return false;
}
}
if (alphaFallOff != rs.alphaFallOff) { if (blendMode != rs.blendMode) {
return false; return false;
} }
if (offsetEnabled != rs.offsetEnabled) { if (offsetEnabled != rs.offsetEnabled) {
return false; return false;
} }
@ -544,70 +610,30 @@ public class RenderState implements Cloneable, Savable {
} }
/** /**
* Enables point sprite mode. * @deprecated Does nothing. Point sprite is already enabled by default for
* * all supported platforms. jME3 does not support rendering conventional
* <p>When point sprite is enabled, any meshes * point clouds.
* with the type of {@link Mode#Points} will be rendered as 2D quads
* with texturing enabled. Fragment shaders can write to the
* <code>gl_PointCoord</code> variable to manipulate the texture coordinate
* for each pixel. The size of the 2D quad can be controlled by writing
* to the <code>gl_PointSize</code> variable in the vertex shader.
*
* @param pointSprite Enables Point Sprite mode.
*/ */
@Deprecated
public void setPointSprite(boolean pointSprite) { public void setPointSprite(boolean pointSprite) {
applyPointSprite = true;
this.pointSprite = pointSprite;
cachedHashCode = -1;
} }
/** /**
* Sets the alpha fall off value for alpha testing. * @deprecated Does nothing. To use alpha test, set the
* * <code>AlphaDiscardThreshold</code> material parameter.
* <p>If the pixel's alpha value is greater than the * @param alphaFallOff does nothing
* <code>alphaFallOff</code> then the pixel will be rendered, otherwise
* the pixel will be discarded.
*
* Note : Alpha test is deprecated since opengl 3.0 and does not exists in
* openglES 2.0.
* The prefered way is to use the alphaDiscardThreshold on the material
* Or have a shader that discards the pixel when its alpha value meets the
* discarding condition.
*
* @param alphaFallOff The alpha of all rendered pixels must be higher
* than this value to be rendered. This value should be between 0 and 1.
*
* @see RenderState#setAlphaTest(boolean)
*/ */
@Deprecated
public void setAlphaFallOff(float alphaFallOff) { public void setAlphaFallOff(float alphaFallOff) {
applyAlphaFallOff = true;
this.alphaFallOff = alphaFallOff;
cachedHashCode = -1;
} }
/** /**
* Enable alpha testing. * @deprecated Does nothing. To use alpha test, set the
* * <code>AlphaDiscardThreshold</code> material parameter.
* <p>When alpha testing is enabled, all input pixels' alpha are compared * @param alphaTest does nothing
* to the {@link RenderState#setAlphaFallOff(float) constant alpha falloff}.
* If the input alpha is greater than the falloff, the pixel will be rendered,
* otherwise it will be discarded.
*
* @param alphaTest Set to true to enable alpha testing.
*
* Note : Alpha test is deprecated since opengl 3.0 and does not exists in
* openglES 2.0.
* The prefered way is to use the alphaDiscardThreshold on the material
* Or have a shader that discards the pixel when its alpha value meets the
* discarding condition.
*
*
* @see RenderState#setAlphaFallOff(float)
*/ */
@Deprecated
public void setAlphaTest(boolean alphaTest) { public void setAlphaTest(boolean alphaTest) {
applyAlphaTest = true;
this.alphaTest = alphaTest;
cachedHashCode = -1;
} }
/** /**
@ -663,6 +689,61 @@ public class RenderState implements Cloneable, Savable {
cachedHashCode = -1; cachedHashCode = -1;
} }
/**
* Set the blending equation.
* <p>
* When blending is enabled, (<code>blendMode</code> is not
* {@link BlendMode#Off}) the input pixel will be blended with the pixel
* already in the color buffer. The blending equation is determined by the
* {@link BlendEquation}. For example, the mode {@link BlendMode#Additive}
* and {@link BlendEquation#Add} will add the input pixel's color to the
* color already in the color buffer:
* <br/>
* <code>Result = Source Color + Destination Color</code>
* <br/>
* However, the mode {@link BlendMode#Additive}
* and {@link BlendEquation#Subtract} will subtract the input pixel's color to the
* color already in the color buffer:
* <br/>
* <code>Result = Source Color - Destination Color</code>
*
* @param blendEquation The blend equation to use.
*/
public void setBlendEquation(BlendEquation blendEquation) {
applyBlendEquation = true;
this.blendEquation = blendEquation;
cachedHashCode = -1;
}
/**
* Set the blending equation for the alpha component.
* <p>
* When blending is enabled, (<code>blendMode</code> is not
* {@link BlendMode#Off}) the input pixel will be blended with the pixel
* already in the color buffer. The blending equation is determined by the
* {@link BlendEquation} and can be overrode for the alpha component using
* the {@link BlendEquationAlpha} . For example, the mode
* {@link BlendMode#Additive} and {@link BlendEquationAlpha#Add} will add
* the input pixel's alpha to the alpha component already in the color
* buffer:
* <br/>
* <code>Result = Source Alpha + Destination Alpha</code>
* <br/>
* However, the mode {@link BlendMode#Additive} and
* {@link BlendEquationAlpha#Subtract} will subtract the input pixel's alpha
* to the alpha component already in the color buffer:
* <br/>
* <code>Result = Source Alpha - Destination Alpha</code>
*
* @param blendEquationAlpha The blend equation to use for the alpha
* component.
*/
public void setBlendEquationAlpha(BlendEquationAlpha blendEquationAlpha) {
applyBlendEquationAlpha = true;
this.blendEquationAlpha = blendEquationAlpha;
cachedHashCode = -1;
}
/** /**
* Enable depth testing. * Enable depth testing.
* *
@ -796,24 +877,10 @@ public class RenderState implements Cloneable, Savable {
} }
/** /**
* Sets the alpha comparision function to the given TestFunction * @deprecated
* default is Greater (GL_GREATER)
*
* Note : Alpha test is deprecated since opengl 3.0 and does not exists in
* openglES 2.0.
* The prefered way is to use the alphaDiscardThreshold on the material
* Or have a shader taht discards the pixel when its alpha value meets the
* discarding condition.
*
* @see TestFunction
* @see RenderState#setAlphaTest(boolean)
* @see RenderState#setAlphaFallOff(float)
* @param alphaFunc the alpha comparision function
*/ */
public void setAlphaFunc(TestFunction alphaFunc) { @Deprecated
applyAlphaFunc = true; public void setAlphaFunc(TestFunction alphaFunc) {
this.alphaFunc = alphaFunc;
cachedHashCode = -1;
} }
/** /**
@ -822,6 +889,9 @@ public class RenderState implements Cloneable, Savable {
* @param lineWidth the line width. * @param lineWidth the line width.
*/ */
public void setLineWidth(float lineWidth) { public void setLineWidth(float lineWidth) {
if (lineWidth < 1f) {
throw new IllegalArgumentException("lineWidth must be greater than or equal to 1.0");
}
this.lineWidth = lineWidth; this.lineWidth = lineWidth;
this.applyLineWidth = true; this.applyLineWidth = true;
cachedHashCode = -1; cachedHashCode = -1;
@ -988,6 +1058,24 @@ public class RenderState implements Cloneable, Savable {
return backStencilFunction; return backStencilFunction;
} }
/**
* Retrieve the blend equation.
*
* @return the blend equation.
*/
public BlendEquation getBlendEquation() {
return blendEquation;
}
/**
* Retrieve the blend equation used for the alpha component.
*
* @return the blend equation for the alpha component.
*/
public BlendEquationAlpha getBlendEquationAlpha() {
return blendEquationAlpha;
}
/** /**
* Retrieve the blend mode. * Retrieve the blend mode.
* *
@ -998,25 +1086,22 @@ public class RenderState implements Cloneable, Savable {
} }
/** /**
* Check if point sprite mode is enabled * @return true
* * @deprecated Always returns true since point sprite is always enabled.
* @return True if point sprite mode is enabled. * @see #setPointSprite(boolean)
*
* @see RenderState#setPointSprite(boolean)
*/ */
@Deprecated
public boolean isPointSprite() { public boolean isPointSprite() {
return pointSprite; return true;
} }
/** /**
* Check if alpha test is enabled. * @deprecated To use alpha test, set the <code>AlphaDiscardThreshold</code>
* * material parameter.
* @return True if alpha test is enabled. * @return false
*
* @see RenderState#setAlphaTest(boolean)
*/ */
public boolean isAlphaTest() { public boolean isAlphaTest() {
return alphaTest; return false;
} }
/** /**
@ -1108,14 +1193,12 @@ public class RenderState implements Cloneable, Savable {
} }
/** /**
* Retrieve the alpha falloff value. * @return 0
* * @deprecated
* @return the alpha falloff value.
*
* @see RenderState#setAlphaFallOff(float)
*/ */
@Deprecated
public float getAlphaFallOff() { public float getAlphaFallOff() {
return alphaFallOff; return 0f;
} }
/** /**
@ -1130,14 +1213,12 @@ public class RenderState implements Cloneable, Savable {
} }
/** /**
* Retrieve the alpha comparison function * @return {@link TestFunction#Greater}.
* * @deprecated
* @return the alpha comparison function
*
* @see RenderState#setAlphaFunc(com.jme3.material.RenderState.TestFunction)
*/ */
@Deprecated
public TestFunction getAlphaFunc() { public TestFunction getAlphaFunc() {
return alphaFunc; return TestFunction.Greater;
} }
/** /**
@ -1150,16 +1231,17 @@ public class RenderState implements Cloneable, Savable {
} }
public boolean isApplyAlphaFallOff() {
return applyAlphaFallOff; public boolean isApplyBlendMode() {
return applyBlendMode;
} }
public boolean isApplyAlphaTest() { public boolean isApplyBlendEquation() {
return applyAlphaTest; return applyBlendEquation;
} }
public boolean isApplyBlendMode() { public boolean isApplyBlendEquationAlpha() {
return applyBlendMode; return applyBlendEquationAlpha;
} }
public boolean isApplyColorWrite() { public boolean isApplyColorWrite() {
@ -1178,9 +1260,6 @@ public class RenderState implements Cloneable, Savable {
return applyDepthWrite; return applyDepthWrite;
} }
public boolean isApplyPointSprite() {
return applyPointSprite;
}
public boolean isApplyPolyOffset() { public boolean isApplyPolyOffset() {
return applyPolyOffset; return applyPolyOffset;
@ -1194,9 +1273,6 @@ public class RenderState implements Cloneable, Savable {
return applyDepthFunc; return applyDepthFunc;
} }
public boolean isApplyAlphaFunc() {
return applyAlphaFunc;
}
public boolean isApplyLineWidth() { public boolean isApplyLineWidth() {
return applyLineWidth; return applyLineWidth;
@ -1208,7 +1284,6 @@ public class RenderState implements Cloneable, Savable {
public int contentHashCode() { public int contentHashCode() {
if (cachedHashCode == -1){ if (cachedHashCode == -1){
int hash = 7; int hash = 7;
hash = 79 * hash + (this.pointSprite ? 1 : 0);
hash = 79 * hash + (this.wireframe ? 1 : 0); hash = 79 * hash + (this.wireframe ? 1 : 0);
hash = 79 * hash + (this.cullMode != null ? this.cullMode.hashCode() : 0); hash = 79 * hash + (this.cullMode != null ? this.cullMode.hashCode() : 0);
hash = 79 * hash + (this.depthWrite ? 1 : 0); hash = 79 * hash + (this.depthWrite ? 1 : 0);
@ -1216,9 +1291,8 @@ public class RenderState implements Cloneable, Savable {
hash = 79 * hash + (this.depthFunc != null ? this.depthFunc.hashCode() : 0); hash = 79 * hash + (this.depthFunc != null ? this.depthFunc.hashCode() : 0);
hash = 79 * hash + (this.colorWrite ? 1 : 0); hash = 79 * hash + (this.colorWrite ? 1 : 0);
hash = 79 * hash + (this.blendMode != null ? this.blendMode.hashCode() : 0); hash = 79 * hash + (this.blendMode != null ? this.blendMode.hashCode() : 0);
hash = 79 * hash + (this.alphaTest ? 1 : 0); hash = 79 * hash + (this.blendEquation != null ? this.blendEquation.hashCode() : 0);
hash = 79 * hash + (this.alphaFunc != null ? this.alphaFunc.hashCode() : 0); hash = 79 * hash + (this.blendEquationAlpha != null ? this.blendEquationAlpha.hashCode() : 0);
hash = 79 * hash + Float.floatToIntBits(this.alphaFallOff);
hash = 79 * hash + Float.floatToIntBits(this.offsetFactor); hash = 79 * hash + Float.floatToIntBits(this.offsetFactor);
hash = 79 * hash + Float.floatToIntBits(this.offsetUnits); hash = 79 * hash + Float.floatToIntBits(this.offsetUnits);
hash = 79 * hash + (this.offsetEnabled ? 1 : 0); hash = 79 * hash + (this.offsetEnabled ? 1 : 0);
@ -1263,11 +1337,6 @@ public class RenderState implements Cloneable, Savable {
return this; return this;
} }
if (additionalState.applyPointSprite) {
state.pointSprite = additionalState.pointSprite;
} else {
state.pointSprite = pointSprite;
}
if (additionalState.applyWireFrame) { if (additionalState.applyWireFrame) {
state.wireframe = additionalState.wireframe; state.wireframe = additionalState.wireframe;
} else { } else {
@ -1299,27 +1368,22 @@ public class RenderState implements Cloneable, Savable {
} else { } else {
state.colorWrite = colorWrite; state.colorWrite = colorWrite;
} }
if (additionalState.applyBlendMode) { if (additionalState.applyBlendEquation) {
state.blendMode = additionalState.blendMode; state.blendEquation = additionalState.blendEquation;
} else { } else {
state.blendMode = blendMode; state.blendEquation = blendEquation;
} }
if (additionalState.applyAlphaTest) { if (additionalState.applyBlendEquationAlpha) {
state.alphaTest = additionalState.alphaTest; state.blendEquationAlpha = additionalState.blendEquationAlpha;
} else { } else {
state.alphaTest = alphaTest; state.blendEquationAlpha = blendEquationAlpha;
} }
if (additionalState.applyAlphaFunc) { if (additionalState.applyBlendMode) {
state.alphaFunc = additionalState.alphaFunc; state.blendMode = additionalState.blendMode;
} else { } else {
state.alphaFunc = alphaFunc; state.blendMode = blendMode;
} }
if (additionalState.applyAlphaFallOff) {
state.alphaFallOff = additionalState.alphaFallOff;
} else {
state.alphaFallOff = alphaFallOff;
}
if (additionalState.applyPolyOffset) { if (additionalState.applyPolyOffset) {
state.offsetEnabled = additionalState.offsetEnabled; state.offsetEnabled = additionalState.offsetEnabled;
state.offsetFactor = additionalState.offsetFactor; state.offsetFactor = additionalState.offsetFactor;
@ -1364,16 +1428,14 @@ public class RenderState implements Cloneable, Savable {
state.cachedHashCode = -1; state.cachedHashCode = -1;
return state; return state;
} }
public void set(RenderState state) {
pointSprite = state.pointSprite; public void set(RenderState state) {
wireframe = state.wireframe; wireframe = state.wireframe;
cullMode = state.cullMode; cullMode = state.cullMode;
depthWrite = state.depthWrite; depthWrite = state.depthWrite;
depthTest = state.depthTest; depthTest = state.depthTest;
colorWrite = state.colorWrite; colorWrite = state.colorWrite;
blendMode = state.blendMode; blendMode = state.blendMode;
alphaTest = state.alphaTest;
alphaFallOff = state.alphaFallOff;
offsetEnabled = state.offsetEnabled; offsetEnabled = state.offsetEnabled;
offsetFactor = state.offsetFactor; offsetFactor = state.offsetFactor;
offsetUnits = state.offsetUnits; offsetUnits = state.offsetUnits;
@ -1386,30 +1448,27 @@ public class RenderState implements Cloneable, Savable {
backStencilDepthPassOperation = state.backStencilDepthPassOperation; backStencilDepthPassOperation = state.backStencilDepthPassOperation;
frontStencilFunction = state.frontStencilFunction; frontStencilFunction = state.frontStencilFunction;
backStencilFunction = state.backStencilFunction; backStencilFunction = state.backStencilFunction;
blendEquationAlpha = state.blendEquationAlpha;
blendEquation = state.blendEquation;
depthFunc = state.depthFunc; depthFunc = state.depthFunc;
alphaFunc = state.alphaFunc;
lineWidth = state.lineWidth; lineWidth = state.lineWidth;
applyPointSprite = true;
applyWireFrame = true; applyWireFrame = true;
applyCullMode = true; applyCullMode = true;
applyDepthWrite = true; applyDepthWrite = true;
applyDepthTest = true; applyDepthTest = true;
applyColorWrite = true; applyColorWrite = true;
applyBlendMode = true; applyBlendEquation = true;
applyAlphaTest = true; applyBlendEquationAlpha = true;
applyAlphaFallOff = true; applyBlendMode = true;
applyPolyOffset = true; applyPolyOffset = true;
applyDepthFunc = true; applyDepthFunc = true;
applyAlphaFunc = false;
applyLineWidth = true; applyLineWidth = true;
} }
@Override @Override
public String toString() { public String toString() {
return "RenderState[\n" return "RenderState[\n"
+ "pointSprite=" + pointSprite
+ "\napplyPointSprite=" + applyPointSprite
+ "\nwireframe=" + wireframe + "\nwireframe=" + wireframe
+ "\napplyWireFrame=" + applyWireFrame + "\napplyWireFrame=" + applyWireFrame
+ "\ncullMode=" + cullMode + "\ncullMode=" + cullMode
@ -1421,13 +1480,11 @@ public class RenderState implements Cloneable, Savable {
+ "\napplyDepthTest=" + applyDepthTest + "\napplyDepthTest=" + applyDepthTest
+ "\ncolorWrite=" + colorWrite + "\ncolorWrite=" + colorWrite
+ "\napplyColorWrite=" + applyColorWrite + "\napplyColorWrite=" + applyColorWrite
+ "\nblendEquation=" + blendEquation
+ "\napplyBlendEquation=" + applyBlendEquation
+ "\napplyBlendEquationAlpha=" + applyBlendEquationAlpha
+ "\nblendMode=" + blendMode + "\nblendMode=" + blendMode
+ "\napplyBlendMode=" + applyBlendMode + "\napplyBlendMode=" + applyBlendMode
+ "\nalphaTest=" + alphaTest
+ "\nalphaFunc=" + alphaFunc
+ "\napplyAlphaTest=" + applyAlphaTest
+ "\nalphaFallOff=" + alphaFallOff
+ "\napplyAlphaFallOff=" + applyAlphaFallOff
+ "\noffsetEnabled=" + offsetEnabled + "\noffsetEnabled=" + offsetEnabled
+ "\napplyPolyOffset=" + applyPolyOffset + "\napplyPolyOffset=" + applyPolyOffset
+ "\noffsetFactor=" + offsetFactor + "\noffsetFactor=" + offsetFactor

@ -31,27 +31,30 @@
*/ */
package com.jme3.material; package com.jme3.material;
import com.jme3.material.logic.TechniqueDefLogic;
import com.jme3.asset.AssetManager; import com.jme3.asset.AssetManager;
import com.jme3.light.LightList;
import com.jme3.material.TechniqueDef.LightMode;
import com.jme3.renderer.Caps; import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.shader.*; import com.jme3.scene.Geometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import com.jme3.shader.VarType;
import com.jme3.util.ListMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.logging.Logger;
/** /**
* Represents a technique instance. * Represents a technique instance.
*/ */
public class Technique /* implements Savable */ { public final class Technique {
private static final Logger logger = Logger.getLogger(Technique.class.getName()); private final TechniqueDef def;
private TechniqueDef def; private final Material owner;
private Material owner; private final DefineList paramDefines;
private ArrayList<Uniform> worldBindUniforms; private final DefineList dynamicDefines;
private DefineList defines;
private Shader shader;
private boolean needReload = true;
/** /**
* Creates a new technique instance that implements the given * Creates a new technique instance that implements the given
@ -63,14 +66,8 @@ public class Technique /* implements Savable */ {
public Technique(Material owner, TechniqueDef def) { public Technique(Material owner, TechniqueDef def) {
this.owner = owner; this.owner = owner;
this.def = def; this.def = def;
this.worldBindUniforms = new ArrayList<Uniform>(); this.paramDefines = def.createDefineList();
this.defines = new DefineList(); this.dynamicDefines = def.createDefineList();
}
/**
* Serialization only. Do not use.
*/
public Technique() {
} }
/** /**
@ -85,157 +82,125 @@ public class Technique /* implements Savable */ {
} }
/** /**
* Returns the shader currently used by this technique instance. * Called by the material to tell the technique a parameter was modified.
* <p> * Specify <code>null</code> for value if the param is to be cleared.
* Shaders are typically loaded dynamically when the technique is first
* used, therefore, this variable will most likely be null most of the time.
*
* @return the shader currently used by this technique instance.
*/ */
public Shader getShader() { final void notifyParamChanged(String paramName, VarType type, Object value) {
return shader; Integer defineId = def.getShaderParamDefineId(paramName);
}
if (defineId == null) {
return;
}
paramDefines.set(defineId, type, value);
}
/** /**
* Returns a list of uniforms that implements the world parameters * Called by the material to tell the technique that it has been made
* that were requested by the material definition. * current.
* * The technique updates dynamic defines based on the
* @return a list of uniforms implementing the world parameters. * currently set material parameters.
*/ */
public List<Uniform> getWorldBindUniforms() { final void notifyTechniqueSwitched() {
return worldBindUniforms; ListMap<String, MatParam> paramMap = owner.getParamsMap();
paramDefines.clear();
for (int i = 0; i < paramMap.size(); i++) {
MatParam param = paramMap.getValue(i);
notifyParamChanged(param.getName(), param.getVarType(), param.getValue());
}
} }
/** private void applyOverrides(DefineList defineList, List<MatParamOverride> overrides) {
* Called by the material to tell the technique a parameter was modified. for (MatParamOverride override : overrides) {
* Specify <code>null</code> for value if the param is to be cleared. if (!override.isEnabled()) {
*/ continue;
void notifyParamChanged(String paramName, VarType type, Object value) { }
// Check if there's a define binding associated with this Integer defineId = def.getShaderParamDefineId(override.name);
// parameter. if (defineId != null) {
String defineName = def.getShaderParamDefine(paramName); if (def.getDefineIdType(defineId) == override.type) {
if (defineName != null) { defineList.set(defineId, override.type, override.value);
// There is a define. Change it on the define list. }
// The "needReload" variable will determine
// if the shader will be reloaded when the material
// is rendered.
if (value == null) {
// Clear the define.
needReload = defines.remove(defineName) || needReload;
} else {
// Set the define.
needReload = defines.set(defineName, type, value) || needReload;
} }
} }
} }
void updateUniformParam(String paramName, VarType type, Object value) { /**
if (paramName == null) { * Called by the material to determine which shader to use for rendering.
throw new IllegalArgumentException(); *
* The {@link TechniqueDefLogic} is used to determine the shader to use
* based on the {@link LightMode}.
*
* @param renderManager The render manager for which the shader is to be selected.
* @param rendererCaps The renderer capabilities which the shader should support.
* @return A compatible shader.
*/
Shader makeCurrent(RenderManager renderManager, List<MatParamOverride> worldOverrides,
List<MatParamOverride> forcedOverrides,
LightList lights, EnumSet<Caps> rendererCaps) {
TechniqueDefLogic logic = def.getLogic();
AssetManager assetManager = owner.getMaterialDef().getAssetManager();
dynamicDefines.clear();
dynamicDefines.setAll(paramDefines);
if (worldOverrides != null) {
applyOverrides(dynamicDefines, worldOverrides);
} }
if (forcedOverrides != null) {
Uniform u = shader.getUniform(paramName); applyOverrides(dynamicDefines, forcedOverrides);
switch (type) {
case TextureBuffer:
case Texture2D: // fall intentional
case Texture3D:
case TextureArray:
case TextureCubeMap:
case Int:
u.setValue(VarType.Int, value);
break;
default:
u.setValue(type, value);
break;
} }
}
return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines);
}
/** /**
* Returns true if the technique must be reloaded. * Render the technique according to its {@link TechniqueDefLogic}.
* <p>
* If a technique needs to reload, then the {@link Material} should
* call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this
* technique.
* *
* @return true if the technique must be reloaded. * @param renderManager The render manager to perform the rendering against.
* @param shader The shader that was selected in
* {@link #makeCurrent(com.jme3.renderer.RenderManager, java.util.EnumSet)}.
* @param geometry The geometry to render
* @param lights Lights which influence the geometry.
*/ */
public boolean isNeedReload() { void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
return needReload; TechniqueDefLogic logic = def.getLogic();
logic.render(renderManager, shader, geometry, lights);
} }
/** /**
* Prepares the technique for use by loading the shader and setting * Get the {@link DefineList} for dynamic defines.
* the proper defines based on material parameters. *
* Dynamic defines are used to implement material parameter -> define
* bindings as well as {@link TechniqueDefLogic} specific functionality.
* *
* @param assetManager The asset manager to use for loading shaders. * @return all dynamic defines.
*/ */
public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet<Caps> rendererCaps, RenderManager rm) { public DefineList getDynamicDefines() {
if (techniqueSwitched) { return dynamicDefines;
if (defines.update(owner.getParamsMap(), def)) {
needReload = true;
}
if (getDef().getLightMode() == TechniqueDef.LightMode.SinglePass) {
defines.set("SINGLE_PASS_LIGHTING", VarType.Boolean, true);
defines.set("NB_LIGHTS", VarType.Int, rm.getSinglePassLightBatchSize() * 3);
} else {
defines.set("SINGLE_PASS_LIGHTING", VarType.Boolean, null);
}
}
if (needReload) {
loadShader(assetManager,rendererCaps);
}
}
private void loadShader(AssetManager manager,EnumSet<Caps> rendererCaps) {
ShaderKey key = new ShaderKey(getAllDefines(),def.getShaderProgramLanguages(),def.getShaderProgramNames());
if (getDef().isUsingShaderNodes()) {
manager.getShaderGenerator(rendererCaps).initialize(this);
key.setUsesShaderNodes(true);
}
shader = manager.loadShader(key);
// register the world bound uniforms
worldBindUniforms.clear();
if (def.getWorldBindings() != null) {
for (UniformBinding binding : def.getWorldBindings()) {
Uniform uniform = shader.getUniform("g_" + binding.name());
uniform.setBinding(binding);
worldBindUniforms.add(uniform);
}
}
needReload = false;
} }
/** /**
* Computes the define list * @return nothing.
* @return the complete define list *
* @deprecated Preset defines are precompiled into
* {@link TechniqueDef#getShaderPrologue()}, whereas dynamic defines are
* available via {@link #getParamDefines()}.
*/ */
@Deprecated
public DefineList getAllDefines() { public DefineList getAllDefines() {
DefineList allDefines = new DefineList(); throw new UnsupportedOperationException();
allDefines.addFrom(def.getShaderPresetDefines());
allDefines.addFrom(defines);
return allDefines;
}
/*
public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this);
oc.write(def, "def", null);
oc.writeSavableArrayList(worldBindUniforms, "worldBindUniforms", null);
oc.write(defines, "defines", null);
oc.write(shader, "shader", null);
} }
public void read(JmeImporter im) throws IOException { /**
InputCapsule ic = im.getCapsule(this); * Compute the sort ID. Similar to {@link Object#hashCode()} but used
def = (TechniqueDef) ic.readSavable("def", null); * for sorting geometries for rendering.
worldBindUniforms = ic.readSavableArrayList("worldBindUniforms", null); *
defines = (DefineList) ic.readSavable("defines", null); * @return the sort ID for this technique instance.
shader = (Shader) ic.readSavable("shader", null); */
public int getSortId() {
int hash = 17;
hash = hash * 23 + def.getSortId();
hash = hash * 23 + paramDefines.hashCode();
return hash;
} }
*/
} }

@ -31,9 +31,12 @@
*/ */
package com.jme3.material; package com.jme3.material;
import com.jme3.material.logic.TechniqueDefLogic;
import com.jme3.asset.AssetManager;
import com.jme3.export.*; import com.jme3.export.*;
import com.jme3.renderer.Caps; import com.jme3.renderer.Caps;
import com.jme3.shader.*; import com.jme3.shader.*;
import com.jme3.shader.Shader.ShaderType;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
@ -50,6 +53,14 @@ public class TechniqueDef implements Savable {
*/ */
public static final int SAVABLE_VERSION = 1; public static final int SAVABLE_VERSION = 1;
/**
* The default technique name.
*
* The technique with this name is selected if no specific technique is
* requested by the user. Currently set to "Default".
*/
public static final String DEFAULT_TECHNIQUE_NAME = "Default";
/** /**
* Describes light rendering mode. * Describes light rendering mode.
*/ */
@ -91,13 +102,19 @@ public class TechniqueDef implements Savable {
PostPass, PostPass,
} }
private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class); private final EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
private String name; private String name;
private int sortId;
private EnumMap<Shader.ShaderType,String> shaderLanguages; private EnumMap<Shader.ShaderType,String> shaderLanguages;
private EnumMap<Shader.ShaderType,String> shaderNames; private EnumMap<Shader.ShaderType,String> shaderNames;
private DefineList presetDefines; private String shaderPrologue;
private ArrayList<String> defineNames;
private ArrayList<VarType> defineTypes;
private HashMap<String, Integer> paramToDefineId;
private final HashMap<DefineList, Shader> definesToShaderMap;
private boolean usesNodes = false; private boolean usesNodes = false;
private List<ShaderNode> shaderNodes; private List<ShaderNode> shaderNodes;
private ShaderGenerationInfo shaderGenerationInfo; private ShaderGenerationInfo shaderGenerationInfo;
@ -106,10 +123,10 @@ public class TechniqueDef implements Savable {
private RenderState renderState; private RenderState renderState;
private RenderState forcedRenderState; private RenderState forcedRenderState;
private LightMode lightMode = LightMode.Disable; private LightMode lightMode = LightMode.Disable;
private ShadowMode shadowMode = ShadowMode.Disable; private ShadowMode shadowMode = ShadowMode.Disable;
private TechniqueDefLogic logic;
private HashMap<String, String> defineParams;
private ArrayList<UniformBinding> worldBinds; private ArrayList<UniformBinding> worldBinds;
/** /**
@ -117,25 +134,38 @@ public class TechniqueDef implements Savable {
* <p> * <p>
* Used internally by the J3M/J3MD loader. * Used internally by the J3M/J3MD loader.
* *
* @param name The name of the technique, should be set to <code>null</code> * @param name The name of the technique
* for default techniques.
*/ */
public TechniqueDef(String name){ public TechniqueDef(String name, int sortId){
this(); this();
this.name = name == null ? "Default" : name; this.sortId = sortId;
this.name = name;
} }
/** /**
* Serialization only. Do not use. * Serialization only. Do not use.
*/ */
public TechniqueDef(){ public TechniqueDef() {
shaderLanguages=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); shaderLanguages = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
shaderNames=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); shaderNames = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
defineNames = new ArrayList<String>();
defineTypes = new ArrayList<VarType>();
paramToDefineId = new HashMap<String, Integer>();
definesToShaderMap = new HashMap<DefineList, Shader>();
}
/**
* @return A unique sort ID.
* No other technique definition can have the same ID.
*/
public int getSortId() {
return sortId;
} }
/** /**
* Returns the name of this technique as specified in the J3MD file. * Returns the name of this technique as specified in the J3MD file.
* Default techniques have the name "Default". * Default
* techniques have the name {@link #DEFAULT_TECHNIQUE_NAME}.
* *
* @return the name of this technique * @return the name of this technique
*/ */
@ -162,7 +192,15 @@ public class TechniqueDef implements Savable {
public void setLightMode(LightMode lightMode) { public void setLightMode(LightMode lightMode) {
this.lightMode = lightMode; this.lightMode = lightMode;
} }
public void setLogic(TechniqueDefLogic logic) {
this.logic = logic;
}
public TechniqueDefLogic getLogic() {
return logic;
}
/** /**
* Returns the shadow mode. * Returns the shadow mode.
* @return the shadow mode. * @return the shadow mode.
@ -224,14 +262,6 @@ public class TechniqueDef implements Savable {
return noRender; return noRender;
} }
/**
* @deprecated jME3 always requires shaders now
*/
@Deprecated
public boolean isUsingShaders(){
return true;
}
/** /**
* Returns true if this technique uses Shader Nodes, false otherwise. * Returns true if this technique uses Shader Nodes, false otherwise.
* *
@ -273,34 +303,24 @@ public class TechniqueDef implements Savable {
requiredCaps.add(fragCap); requiredCaps.add(fragCap);
} }
/** /**
* Sets the shaders that this technique definition will use. * Set a string which is prepended to every shader used by this technique.
* *
* @param shaderNames EnumMap containing all shader names for this stage * Typically this is used for preset defines.
* @param shaderLanguages EnumMap containing all shader languages for this stage *
* @param shaderPrologue The prologue to append before the technique's shaders.
*/ */
public void setShaderFile(EnumMap<Shader.ShaderType, String> shaderNames, EnumMap<Shader.ShaderType, String> shaderLanguages) { public void setShaderPrologue(String shaderPrologue) {
requiredCaps.clear(); this.shaderPrologue = shaderPrologue;
for (Shader.ShaderType shaderType : shaderNames.keySet()) {
String language = shaderLanguages.get(shaderType);
String shaderFile = shaderNames.get(shaderType);
this.shaderLanguages.put(shaderType, language);
this.shaderNames.put(shaderType, shaderFile);
Caps vertCap = Caps.valueOf(language);
requiredCaps.add(vertCap);
if (shaderType.equals(Shader.ShaderType.Geometry)) {
requiredCaps.add(Caps.GeometryShader);
} else if (shaderType.equals(Shader.ShaderType.TessellationControl)) {
requiredCaps.add(Caps.TesselationShader);
}
}
} }
/**
* @return the shader prologue which is prepended to every shader.
*/
public String getShaderPrologue() {
return shaderPrologue;
}
/** /**
* Returns the define name which the given material parameter influences. * Returns the define name which the given material parameter influences.
* *
@ -310,60 +330,186 @@ public class TechniqueDef implements Savable {
* @see #addShaderParamDefine(java.lang.String, java.lang.String) * @see #addShaderParamDefine(java.lang.String, java.lang.String)
*/ */
public String getShaderParamDefine(String paramName){ public String getShaderParamDefine(String paramName){
if (defineParams == null) { Integer defineId = paramToDefineId.get(paramName);
if (defineId != null) {
return defineNames.get(defineId);
} else {
return null; return null;
} }
return defineParams.get(paramName); }
/**
* Get the define ID for a given material parameter.
*
* @param paramName The parameter name to look up
* @return The define ID, or null if not found.
*/
public Integer getShaderParamDefineId(String paramName) {
return paramToDefineId.get(paramName);
} }
/**
* Get the type of a particular define.
*
* @param defineId The define ID to lookup.
* @return The type of the define, or null if not found.
*/
public VarType getDefineIdType(int defineId) {
return defineId < defineTypes.size() ? defineTypes.get(defineId) : null;
}
/** /**
* Adds a define linked to a material parameter. * Adds a define linked to a material parameter.
* <p> * <p>
* Any time the material parameter on the parent material is altered, * Any time the material parameter on the parent material is altered,
* the appropriate define on the technique will be modified as well. * the appropriate define on the technique will be modified as well.
* See the method * When set, the material parameter will be mapped to an integer define,
* {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) } * typically <code>1</code> if it is set, unless it is an integer or a float,
* on the exact details of how the material parameter changes the define. * in which case it will converted into an integer.
* *
* @param paramName The name of the material parameter to link to. * @param paramName The name of the material parameter to link to.
* @param paramType The type of the material parameter to link to.
* @param defineName The name of the define parameter, e.g. USE_LIGHTING * @param defineName The name of the define parameter, e.g. USE_LIGHTING
*/ */
public void addShaderParamDefine(String paramName, String defineName){ public void addShaderParamDefine(String paramName, VarType paramType, String defineName){
if (defineParams == null) { int defineId = defineNames.size();
defineParams = new HashMap<String, String>();
if (defineId >= DefineList.MAX_DEFINES) {
throw new IllegalStateException("Cannot have more than " +
DefineList.MAX_DEFINES + " defines on a technique.");
}
paramToDefineId.put(paramName, defineId);
defineNames.add(defineName);
defineTypes.add(paramType);
}
/**
* Add an unmapped define which can only be set by define ID.
*
* Unmapped defines are used by technique renderers to
* configure the shader internally before rendering.
*
* @param defineName The define name to create
* @return The define ID of the created define
*/
public int addShaderUnmappedDefine(String defineName, VarType defineType) {
int defineId = defineNames.size();
if (defineId >= DefineList.MAX_DEFINES) {
throw new IllegalStateException("Cannot have more than " +
DefineList.MAX_DEFINES + " defines on a technique.");
} }
defineParams.put(paramName, defineName);
defineNames.add(defineName);
defineTypes.add(defineType);
return defineId;
} }
/** /**
* Returns the {@link DefineList} for the preset defines. * Get the names of all defines declared on this technique definition.
* *
* @return the {@link DefineList} for the preset defines. * The defines are returned in order of declaration.
* *
* @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object) * @return the names of all defines declared.
*/ */
public DefineList getShaderPresetDefines() { public String[] getDefineNames() {
return presetDefines; return defineNames.toArray(new String[0]);
} }
/** /**
* Adds a preset define. * Get the types of all defines declared on this technique definition.
* <p>
* Preset defines do not depend upon any parameters to be activated,
* they are always passed to the shader as long as this technique is used.
* *
* @param defineName The name of the define parameter, e.g. USE_LIGHTING * The types are returned in order of declaration.
* @param type The type of the define. See
* {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
* to see why it matters.
* *
* @param value The value of the define * @return the types of all defines declared.
*/ */
public void addShaderPresetDefine(String defineName, VarType type, Object value){ public VarType[] getDefineTypes() {
if (presetDefines == null) { return defineTypes.toArray(new VarType[0]);
presetDefines = new DefineList(); }
/**
* Create a define list with the size matching the number
* of defines on this technique.
*
* @return a define list with the size matching the number
* of defines on this technique.
*/
public DefineList createDefineList() {
return new DefineList(defineNames.size());
}
private Shader loadShader(AssetManager assetManager, EnumSet<Caps> rendererCaps, DefineList defines) {
StringBuilder sb = new StringBuilder();
sb.append(shaderPrologue);
defines.generateSource(sb, defineNames, defineTypes);
String definesSourceCode = sb.toString();
Shader shader;
if (isUsingShaderNodes()) {
ShaderGenerator shaderGenerator = assetManager.getShaderGenerator(rendererCaps);
if (shaderGenerator == null) {
throw new UnsupportedOperationException("ShaderGenerator was not initialized, "
+ "make sure assetManager.getGenerator(caps) has been called");
}
shaderGenerator.initialize(this);
shader = shaderGenerator.generateShader(definesSourceCode);
} else {
shader = new Shader();
for (ShaderType type : ShaderType.values()) {
String language = shaderLanguages.get(type);
String shaderSourceAssetName = shaderNames.get(type);
if (language == null || shaderSourceAssetName == null) {
continue;
}
String shaderSourceCode = (String) assetManager.loadAsset(shaderSourceAssetName);
shader.addSource(type, shaderSourceAssetName, shaderSourceCode, definesSourceCode, language);
}
}
if (getWorldBindings() != null) {
for (UniformBinding binding : getWorldBindings()) {
shader.addUniformBinding(binding);
}
}
return shader;
}
public Shader getShader(AssetManager assetManager, EnumSet<Caps> rendererCaps, DefineList defines) {
Shader shader = definesToShaderMap.get(defines);
if (shader == null) {
shader = loadShader(assetManager, rendererCaps, defines);
definesToShaderMap.put(defines.deepClone(), shader);
}
return shader;
}
/**
* Sets the shaders that this technique definition will use.
*
* @param shaderNames EnumMap containing all shader names for this stage
* @param shaderLanguages EnumMap containing all shader languages for this stage
*/
public void setShaderFile(EnumMap<Shader.ShaderType, String> shaderNames, EnumMap<Shader.ShaderType, String> shaderLanguages) {
requiredCaps.clear();
for (Shader.ShaderType shaderType : shaderNames.keySet()) {
String language = shaderLanguages.get(shaderType);
String shaderFile = shaderNames.get(shaderType);
this.shaderLanguages.put(shaderType, language);
this.shaderNames.put(shaderType, shaderFile);
Caps vertCap = Caps.valueOf(language);
requiredCaps.add(vertCap);
if (shaderType.equals(Shader.ShaderType.Geometry)) {
requiredCaps.add(Caps.GeometryShader);
} else if (shaderType.equals(Shader.ShaderType.TessellationControl)) {
requiredCaps.add(Caps.TesselationShader);
}
} }
presetDefines.set(defineName, type, value);
} }
/** /**
@ -467,7 +613,7 @@ public class TechniqueDef implements Savable {
oc.write(shaderLanguages.get(Shader.ShaderType.TessellationControl), "tsctrlLanguage", null); oc.write(shaderLanguages.get(Shader.ShaderType.TessellationControl), "tsctrlLanguage", null);
oc.write(shaderLanguages.get(Shader.ShaderType.TessellationEvaluation), "tsevalLanguage", null); oc.write(shaderLanguages.get(Shader.ShaderType.TessellationEvaluation), "tsevalLanguage", null);
oc.write(presetDefines, "presetDefines", null); oc.write(shaderPrologue, "shaderPrologue", null);
oc.write(lightMode, "lightMode", LightMode.Disable); oc.write(lightMode, "lightMode", LightMode.Disable);
oc.write(shadowMode, "shadowMode", ShadowMode.Disable); oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
oc.write(renderState, "renderState", null); oc.write(renderState, "renderState", null);
@ -490,7 +636,7 @@ public class TechniqueDef implements Savable {
shaderNames.put(Shader.ShaderType.Geometry,ic.readString("geomName", null)); shaderNames.put(Shader.ShaderType.Geometry,ic.readString("geomName", null));
shaderNames.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlName", null)); shaderNames.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlName", null));
shaderNames.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalName", null)); shaderNames.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalName", null));
presetDefines = (DefineList) ic.readSavable("presetDefines", null); shaderPrologue = ic.readString("shaderPrologue", null);
lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable); lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable); shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
renderState = (RenderState) ic.readSavable("renderState", null); renderState = (RenderState) ic.readSavable("renderState", null);
@ -547,9 +693,14 @@ public class TechniqueDef implements Savable {
this.shaderGenerationInfo = shaderGenerationInfo; this.shaderGenerationInfo = shaderGenerationInfo;
} }
//todo: make toString return something usefull
@Override @Override
public String toString() { public String toString() {
return "TechniqueDef{" + "requiredCaps=" + requiredCaps + ", name=" + name /*+ ", vertName=" + vertName + ", fragName=" + fragName + ", vertLanguage=" + vertLanguage + ", fragLanguage=" + fragLanguage */+ ", presetDefines=" + presetDefines + ", usesNodes=" + usesNodes + ", shaderNodes=" + shaderNodes + ", shaderGenerationInfo=" + shaderGenerationInfo + ", renderState=" + renderState + ", forcedRenderState=" + forcedRenderState + ", lightMode=" + lightMode + ", shadowMode=" + shadowMode + ", defineParams=" + defineParams + ", worldBinds=" + worldBinds + ", noRender=" + noRender + '}'; return "TechniqueDef[name=" + name
+ ", requiredCaps=" + requiredCaps
+ ", noRender=" + noRender
+ ", lightMode=" + lightMode
+ ", usesNodes=" + usesNodes
+ ", renderState=" + renderState
+ ", forcedRenderState=" + forcedRenderState + "]";
} }
} }

@ -0,0 +1,97 @@
/*
* 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.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.light.AmbientLight;
import com.jme3.light.Light;
import com.jme3.light.LightList;
import com.jme3.material.TechniqueDef;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.instancing.InstancedGeometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import java.util.EnumSet;
public class DefaultTechniqueDefLogic implements TechniqueDefLogic {
protected final TechniqueDef techniqueDef;
public DefaultTechniqueDefLogic(TechniqueDef techniqueDef) {
this.techniqueDef = techniqueDef;
}
@Override
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
return techniqueDef.getShader(assetManager, rendererCaps, defines);
}
public static void renderMeshFromGeometry(Renderer renderer, Geometry geom) {
Mesh mesh = geom.getMesh();
int lodLevel = geom.getLodLevel();
if (geom instanceof InstancedGeometry) {
InstancedGeometry instGeom = (InstancedGeometry) geom;
renderer.renderMesh(mesh, lodLevel, instGeom.getActualNumInstances(),
instGeom.getAllInstanceData());
} else {
renderer.renderMesh(mesh, lodLevel, 1, null);
}
}
protected static ColorRGBA getAmbientColor(LightList lightList, boolean removeLights, ColorRGBA ambientLightColor) {
ambientLightColor.set(0, 0, 0, 1);
for (int j = 0; j < lightList.size(); j++) {
Light l = lightList.get(j);
if (l instanceof AmbientLight) {
ambientLightColor.addLocal(l.getColor());
if (removeLights) {
lightList.remove(l);
}
}
}
ambientLightColor.a = 1.0f;
return ambientLightColor;
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
Renderer renderer = renderManager.getRenderer();
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
}
}

@ -0,0 +1,178 @@
/*
* 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.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.light.LightList;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.material.RenderState;
import com.jme3.material.TechniqueDef;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.scene.Geometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import com.jme3.shader.Uniform;
import com.jme3.shader.VarType;
import com.jme3.util.TempVars;
import java.util.EnumSet;
public final class MultiPassLightingLogic extends DefaultTechniqueDefLogic {
private static final RenderState ADDITIVE_LIGHT = new RenderState();
private static final Quaternion NULL_DIR_LIGHT = new Quaternion(0, -1, 0, -1);
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
static {
ADDITIVE_LIGHT.setBlendMode(RenderState.BlendMode.AlphaAdditive);
ADDITIVE_LIGHT.setDepthWrite(false);
}
public MultiPassLightingLogic(TechniqueDef techniqueDef) {
super(techniqueDef);
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
Renderer r = renderManager.getRenderer();
Uniform lightDir = shader.getUniform("g_LightDirection");
Uniform lightColor = shader.getUniform("g_LightColor");
Uniform lightPos = shader.getUniform("g_LightPosition");
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
boolean isFirstLight = true;
boolean isSecondLight = false;
getAmbientColor(lights, false, ambientLightColor);
for (int i = 0; i < lights.size(); i++) {
Light l = lights.get(i);
if (l instanceof AmbientLight) {
continue;
}
if (isFirstLight) {
// set ambient color for first light only
ambientColor.setValue(VarType.Vector4, ambientLightColor);
isFirstLight = false;
isSecondLight = true;
} else if (isSecondLight) {
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
// apply additive blending for 2nd and future lights
r.applyRenderState(ADDITIVE_LIGHT);
isSecondLight = false;
}
TempVars vars = TempVars.get();
Quaternion tmpLightDirection = vars.quat1;
Quaternion tmpLightPosition = vars.quat2;
ColorRGBA tmpLightColor = vars.color;
Vector4f tmpVec = vars.vect4f1;
ColorRGBA color = l.getColor();
tmpLightColor.set(color);
tmpLightColor.a = l.getType().getId();
lightColor.setValue(VarType.Vector4, tmpLightColor);
switch (l.getType()) {
case Directional:
DirectionalLight dl = (DirectionalLight) l;
Vector3f dir = dl.getDirection();
//FIXME : there is an inconstency here due to backward
//compatibility of the lighting shader.
//The directional light direction is passed in the
//LightPosition uniform. The lighting shader needs to be
//reworked though in order to fix this.
tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
lightPos.setValue(VarType.Vector4, tmpLightPosition);
tmpLightDirection.set(0, 0, 0, 0);
lightDir.setValue(VarType.Vector4, tmpLightDirection);
break;
case Point:
PointLight pl = (PointLight) l;
Vector3f pos = pl.getPosition();
float invRadius = pl.getInvRadius();
tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
lightPos.setValue(VarType.Vector4, tmpLightPosition);
tmpLightDirection.set(0, 0, 0, 0);
lightDir.setValue(VarType.Vector4, tmpLightDirection);
break;
case Spot:
SpotLight sl = (SpotLight) l;
Vector3f pos2 = sl.getPosition();
Vector3f dir2 = sl.getDirection();
float invRange = sl.getInvSpotRange();
float spotAngleCos = sl.getPackedAngleCos();
tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
lightPos.setValue(VarType.Vector4, tmpLightPosition);
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
//one vec4 less and a vec4 that becomes a vec3
//the downside is that spotAngleCos decoding happens now in the frag shader.
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
renderManager.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
lightDir.setValue(VarType.Vector4, tmpLightDirection);
break;
default:
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
}
vars.release();
r.setShader(shader);
renderMeshFromGeometry(r, geometry);
}
if (isFirstLight) {
// Either there are no lights at all, or only ambient lights.
// Render a dummy "normal light" so we can see the ambient color.
ambientColor.setValue(VarType.Vector4, getAmbientColor(lights, false, ambientLightColor));
lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
lightPos.setValue(VarType.Vector4, NULL_DIR_LIGHT);
r.setShader(shader);
renderMeshFromGeometry(r, geometry);
}
}
}

@ -0,0 +1,218 @@
/*
* 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.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.light.LightList;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.material.RenderState;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.material.TechniqueDef;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.scene.Geometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import com.jme3.shader.Uniform;
import com.jme3.shader.VarType;
import com.jme3.util.TempVars;
import java.util.EnumSet;
public final class SinglePassLightingLogic extends DefaultTechniqueDefLogic {
private static final String DEFINE_SINGLE_PASS_LIGHTING = "SINGLE_PASS_LIGHTING";
private static final String DEFINE_NB_LIGHTS = "NB_LIGHTS";
private static final RenderState ADDITIVE_LIGHT = new RenderState();
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
static {
ADDITIVE_LIGHT.setBlendMode(BlendMode.AlphaAdditive);
ADDITIVE_LIGHT.setDepthWrite(false);
}
private final int singlePassLightingDefineId;
private final int nbLightsDefineId;
public SinglePassLightingLogic(TechniqueDef techniqueDef) {
super(techniqueDef);
singlePassLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_SINGLE_PASS_LIGHTING, VarType.Boolean);
nbLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_LIGHTS, VarType.Int);
}
@Override
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3);
defines.set(singlePassLightingDefineId, true);
return super.makeCurrent(assetManager, renderManager, rendererCaps, lights, defines);
}
/**
* Uploads the lights in the light list as two uniform arrays.<br/><br/> *
* <p>
* <code>uniform vec4 g_LightColor[numLights];</code><br/> //
* g_LightColor.rgb is the diffuse/specular color of the light.<br/> //
* g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/> //
* 2 = Spot. <br/> <br/>
* <code>uniform vec4 g_LightPosition[numLights];</code><br/> //
* g_LightPosition.xyz is the position of the light (for point lights)<br/>
* // or the direction of the light (for directional lights).<br/> //
* g_LightPosition.w is the inverse radius (1/r) of the light (for
* attenuation) <br/> </p>
*/
protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex) {
if (numLights == 0) { // this shader does not do lighting, ignore.
return 0;
}
Uniform lightData = shader.getUniform("g_LightData");
lightData.setVector4Length(numLights * 3);//8 lights * max 3
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
if (startIndex != 0) {
// apply additive blending for 2nd and future passes
rm.getRenderer().applyRenderState(ADDITIVE_LIGHT);
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
} else {
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, true, ambientLightColor));
}
int lightDataIndex = 0;
TempVars vars = TempVars.get();
Vector4f tmpVec = vars.vect4f1;
int curIndex;
int endIndex = numLights + startIndex;
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
Light l = lightList.get(curIndex);
if (l.getType() == Light.Type.Ambient) {
endIndex++;
continue;
}
ColorRGBA color = l.getColor();
//Color
lightData.setVector4InArray(color.getRed(),
color.getGreen(),
color.getBlue(),
l.getType().getId(),
lightDataIndex);
lightDataIndex++;
switch (l.getType()) {
case Directional:
DirectionalLight dl = (DirectionalLight) l;
Vector3f dir = dl.getDirection();
//Data directly sent in view space to avoid a matrix mult for each pixel
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
// tmpVec.divideLocal(tmpVec.w);
// tmpVec.normalizeLocal();
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
lightDataIndex++;
//PADDING
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex);
lightDataIndex++;
break;
case Point:
PointLight pl = (PointLight) l;
Vector3f pos = pl.getPosition();
float invRadius = pl.getInvRadius();
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
//tmpVec.divideLocal(tmpVec.w);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
lightDataIndex++;
//PADDING
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex);
lightDataIndex++;
break;
case Spot:
SpotLight sl = (SpotLight) l;
Vector3f pos2 = sl.getPosition();
Vector3f dir2 = sl.getDirection();
float invRange = sl.getInvSpotRange();
float spotAngleCos = sl.getPackedAngleCos();
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
// tmpVec.divideLocal(tmpVec.w);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
lightDataIndex++;
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
//one vec4 less and a vec4 that becomes a vec3
//the downside is that spotAngleCos decoding happens now in the frag shader.
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f);
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
tmpVec.normalizeLocal();
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
lightDataIndex++;
break;
default:
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
}
}
vars.release();
//Padding of unsued buffer space
while(lightDataIndex < numLights * 3) {
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
lightDataIndex++;
}
return curIndex;
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
int nbRenderedLights = 0;
Renderer renderer = renderManager.getRenderer();
int batchSize = renderManager.getSinglePassLightBatchSize();
if (lights.size() == 0) {
updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, 0);
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
} else {
while (nbRenderedLights < lights.size()) {
nbRenderedLights = updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, nbRenderedLights);
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
}
}
}
}

@ -0,0 +1,182 @@
/*
* 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.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.light.LightList;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.material.TechniqueDef;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.scene.Geometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import com.jme3.shader.Uniform;
import com.jme3.shader.VarType;
import java.util.ArrayList;
import java.util.EnumSet;
/**
* Rendering logic for static pass.
*
* @author Kirill Vainer
*/
public final class StaticPassLightingLogic extends DefaultTechniqueDefLogic {
private static final String DEFINE_NUM_DIR_LIGHTS = "NUM_DIR_LIGHTS";
private static final String DEFINE_NUM_POINT_LIGHTS = "NUM_POINT_LIGHTS";
private static final String DEFINE_NUM_SPOT_LIGHTS = "NUM_SPOT_LIGHTS";
private final int numDirLightsDefineId;
private final int numPointLightsDefineId;
private final int numSpotLightsDefineId;
private final ArrayList<DirectionalLight> tempDirLights = new ArrayList<DirectionalLight>();
private final ArrayList<PointLight> tempPointLights = new ArrayList<PointLight>();
private final ArrayList<SpotLight> tempSpotLights = new ArrayList<SpotLight>();
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
private final Vector3f tempPosition = new Vector3f();
private final Vector3f tempDirection = new Vector3f();
public StaticPassLightingLogic(TechniqueDef techniqueDef) {
super(techniqueDef);
numDirLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_DIR_LIGHTS, VarType.Int);
numPointLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_POINT_LIGHTS, VarType.Int);
numSpotLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_SPOT_LIGHTS, VarType.Int);
}
@Override
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
// TODO: if it ever changes that render isn't called
// right away with the same geometry after makeCurrent, it would be
// a problem.
// Do a radix sort.
tempDirLights.clear();
tempPointLights.clear();
tempSpotLights.clear();
for (Light light : lights) {
switch (light.getType()) {
case Directional:
tempDirLights.add((DirectionalLight) light);
break;
case Point:
tempPointLights.add((PointLight) light);
break;
case Spot:
tempSpotLights.add((SpotLight) light);
break;
}
}
defines.set(numDirLightsDefineId, tempDirLights.size());
defines.set(numPointLightsDefineId, tempPointLights.size());
defines.set(numSpotLightsDefineId, tempSpotLights.size());
return techniqueDef.getShader(assetManager, rendererCaps, defines);
}
private void transformDirection(Matrix4f viewMatrix, Vector3f direction) {
viewMatrix.multNormal(direction, direction);
}
private void transformPosition(Matrix4f viewMatrix, Vector3f location) {
viewMatrix.mult(location, location);
}
private void updateLightListUniforms(Matrix4f viewMatrix, Shader shader, LightList lights) {
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
ambientColor.setValue(VarType.Vector4, getAmbientColor(lights, true, ambientLightColor));
Uniform lightData = shader.getUniform("g_LightData");
int totalSize = tempDirLights.size() * 2
+ tempPointLights.size() * 2
+ tempSpotLights.size() * 3;
lightData.setVector4Length(totalSize);
int index = 0;
for (DirectionalLight light : tempDirLights) {
ColorRGBA color = light.getColor();
tempDirection.set(light.getDirection());
transformDirection(viewMatrix, tempDirection);
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, 1f, index++);
}
for (PointLight light : tempPointLights) {
ColorRGBA color = light.getColor();
tempPosition.set(light.getPosition());
float invRadius = light.getInvRadius();
transformPosition(viewMatrix, tempPosition);
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRadius, index++);
}
for (SpotLight light : tempSpotLights) {
ColorRGBA color = light.getColor();
Vector3f pos = light.getPosition();
Vector3f dir = light.getDirection();
tempPosition.set(light.getPosition());
tempDirection.set(light.getDirection());
transformPosition(viewMatrix, tempPosition);
transformDirection(viewMatrix, tempDirection);
float invRange = light.getInvSpotRange();
float spotAngleCos = light.getPackedAngleCos();
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRange, index++);
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, spotAngleCos, index++);
}
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
Renderer renderer = renderManager.getRenderer();
Matrix4f viewMatrix = renderManager.getCurrentCamera().getViewMatrix();
updateLightListUniforms(viewMatrix, shader, lights);
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
}
}

@ -0,0 +1,97 @@
/*
* 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.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.light.LightList;
import com.jme3.material.TechniqueDef.LightMode;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import com.jme3.shader.Uniform;
import com.jme3.shader.UniformBinding;
import com.jme3.texture.Texture;
import java.util.EnumSet;
/**
* <code>TechniqueDefLogic</code> is used to customize how
* a material should be rendered.
*
* Typically used to implement {@link LightMode lighting modes}.
* Implementations can register
* {@link TechniqueDef#addShaderUnmappedDefine(java.lang.String) unmapped defines}
* in their constructor and then later set them based on the geometry
* or light environment being rendered.
*
* @author Kirill Vainer
*/
public interface TechniqueDefLogic {
/**
* Determine the shader to use for the given geometry / material combination.
*
* @param assetManager The asset manager to use for loading shader source code,
* shader nodes, and and lookup textures.
* @param renderManager The render manager for which rendering is to be performed.
* @param rendererCaps Renderer capabilities. The returned shader must
* support these capabilities.
* @param lights The lights with which the geometry shall be rendered. This
* list must not include culled lights.
* @param defines The define list used by the technique, any
* {@link TechniqueDef#addShaderUnmappedDefine(java.lang.String) unmapped defines}
* should be set here to change shader behavior.
*
* @return The shader to use for rendering.
*/
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines);
/**
* Requests that the <code>TechniqueDefLogic</code> renders the given geometry.
*
* Fixed material functionality such as {@link RenderState},
* {@link MatParam material parameters}, and
* {@link UniformBinding uniform bindings}
* have already been applied by the material, however,
* {@link RenderState}, {@link Uniform uniforms}, {@link Texture textures},
* can still be overriden.
*
* @param renderManager The render manager to perform the rendering against.
* * @param shader The shader that was selected by this logic in
* {@link #makeCurrent(com.jme3.asset.AssetManager, com.jme3.renderer.RenderManager, java.util.EnumSet, com.jme3.shader.DefineList)}.
* @param geometry The geometry to render
* @param lights Lights which influence the geometry.
*/
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights);
}

@ -1005,12 +1005,12 @@ public class Camera implements Savable, Cloneable {
* *
* NOTE: This method is used internally for culling, for public usage, * NOTE: This method is used internally for culling, for public usage,
* the plane state of the bounding volume must be saved and restored, e.g: * the plane state of the bounding volume must be saved and restored, e.g:
* <code>BoundingVolume bv;<br/> * <code>BoundingVolume bv;<br>
* Camera c;<br/> * Camera c;<br>
* int planeState = bv.getPlaneState();<br/> * int planeState = bv.getPlaneState();<br>
* bv.setPlaneState(0);<br/> * bv.setPlaneState(0);<br>
* c.contains(bv);<br/> * c.contains(bv);<br>
* bv.setPlaneState(plateState);<br/> * bv.setPlaneState(plateState);<br>
* </code> * </code>
* *
* @param bound the bound to check for culling * @param bound the bound to check for culling

@ -32,51 +32,34 @@
package com.jme3.renderer; package com.jme3.renderer;
/** /**
* <code>Limits</code> allows querying the limits of certain features in * <code>Limits</code> allows querying the limits of certain features in
* {@link Renderer}. * {@link Renderer}.
* <p> * <p>
* For example, maximum texture sizes or number of samples. * For example, maximum texture sizes or number of samples.
* *
* @author Kirill Vainer * @author Kirill Vainer
*/ */
public enum Limits { public enum Limits {
/** /**
* Maximum number of vertex texture units, or number of textures * Maximum number of vertex texture units, or number of textures that can be
* that can be used in the vertex shader. * used in the vertex shader.
*/ */
VertexTextureUnits, VertexTextureUnits,
/** /**
* Maximum number of fragment texture units, or number of textures * Maximum number of fragment texture units, or number of textures that can
* that can be used in the fragment shader. * be used in the fragment shader.
*/ */
FragmentTextureUnits, FragmentTextureUnits,
FragmentUniformVectors,
FragmentUniforms, VertexUniformVectors,
VertexAttributes, VertexAttributes,
FrameBufferSamples, FrameBufferSamples,
FrameBufferAttachments, FrameBufferAttachments,
FrameBufferMrtAttachments, FrameBufferMrtAttachments,
RenderBufferSize, RenderBufferSize,
TextureSize, TextureSize,
CubemapSize, CubemapSize,
VertexCount,
TriangleCount,
ColorTextureSamples, ColorTextureSamples,
DepthTextureSamples, DepthTextureSamples,
VertexUniformVectors,
TextureAnisotropy, TextureAnisotropy,
} }

@ -55,16 +55,6 @@ public class RenderContext {
*/ */
public boolean depthTestEnabled = false; public boolean depthTestEnabled = false;
/**
* @see RenderState#setAlphaFallOff(float)
*/
public float alphaTestFallOff = 0f;
/**
* @see RenderState#setAlphaTest(boolean)
*/
public boolean alphaTestEnabled = false;
/** /**
* @see RenderState#setDepthWrite(boolean) * @see RenderState#setDepthWrite(boolean)
*/ */
@ -111,14 +101,19 @@ public class RenderContext {
public RenderState.BlendMode blendMode = RenderState.BlendMode.Off; public RenderState.BlendMode blendMode = RenderState.BlendMode.Off;
/** /**
* @see RenderState#setWireframe(boolean) * @see RenderState#setBlendEquation(com.jme3.material.RenderState.BlendEquation)
*/ */
public boolean wireframe = false; public RenderState.BlendEquation blendEquation = RenderState.BlendEquation.Add;
/**
* @see RenderState#setBlendEquationAlpha(com.jme3.material.RenderState.BlendEquationAlpha)
*/
public RenderState.BlendEquationAlpha blendEquationAlpha = RenderState.BlendEquationAlpha.InheritColor;
/** /**
* @see RenderState#setPointSprite(boolean) * @see RenderState#setWireframe(boolean)
*/ */
public boolean pointSprite = false; public boolean wireframe = false;
/** /**
* @see Renderer#setShader(com.jme3.shader.Shader) * @see Renderer#setShader(com.jme3.shader.Shader)
@ -261,7 +256,6 @@ public class RenderContext {
public void reset(){ public void reset(){
cullMode = RenderState.FaceCullMode.Off; cullMode = RenderState.FaceCullMode.Off;
depthTestEnabled = false; depthTestEnabled = false;
alphaTestFallOff = 0f;
depthWriteEnabled = false; depthWriteEnabled = false;
colorWriteEnabled = false; colorWriteEnabled = false;
clipRectEnabled = false; clipRectEnabled = false;
@ -270,6 +264,8 @@ public class RenderContext {
polyOffsetUnits = 0; polyOffsetUnits = 0;
pointSize = 1; pointSize = 1;
blendMode = RenderState.BlendMode.Off; blendMode = RenderState.BlendMode.Off;
blendEquation = RenderState.BlendEquation.Add;
blendEquationAlpha = RenderState.BlendEquationAlpha.InheritColor;
wireframe = false; wireframe = false;
boundShaderProgram = 0; boundShaderProgram = 0;
boundShader = null; boundShader = null;

@ -34,8 +34,13 @@ package com.jme3.renderer;
import com.jme3.light.DefaultLightFilter; import com.jme3.light.DefaultLightFilter;
import com.jme3.light.LightFilter; import com.jme3.light.LightFilter;
import com.jme3.light.LightList; import com.jme3.light.LightList;
import com.jme3.material.*; import com.jme3.material.MatParamOverride;
import com.jme3.math.Matrix4f; import com.jme3.material.Material;
import com.jme3.material.MaterialDef;
import com.jme3.material.RenderState;
import com.jme3.material.Technique;
import com.jme3.material.TechniqueDef;
import com.jme3.math.*;
import com.jme3.post.SceneProcessor; import com.jme3.post.SceneProcessor;
import com.jme3.profile.AppProfiler; import com.jme3.profile.AppProfiler;
import com.jme3.profile.AppStep; import com.jme3.profile.AppStep;
@ -45,13 +50,12 @@ import com.jme3.renderer.queue.RenderQueue;
import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.*; import com.jme3.scene.*;
import com.jme3.shader.Uniform; import com.jme3.shader.Shader;
import com.jme3.shader.UniformBinding; import com.jme3.shader.UniformBinding;
import com.jme3.shader.UniformBindingManager; import com.jme3.shader.UniformBindingManager;
import com.jme3.system.NullRenderer; import com.jme3.system.NullRenderer;
import com.jme3.system.Timer; import com.jme3.system.Timer;
import com.jme3.util.SafeArrayList; import com.jme3.util.SafeArrayList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -70,25 +74,26 @@ import java.util.logging.Logger;
public class RenderManager { public class RenderManager {
private static final Logger logger = Logger.getLogger(RenderManager.class.getName()); private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
private Renderer renderer; private final Renderer renderer;
private UniformBindingManager uniformBindingManager = new UniformBindingManager(); private final UniformBindingManager uniformBindingManager = new UniformBindingManager();
private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>(); private final ArrayList<ViewPort> preViewPorts = new ArrayList<>();
private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>(); private final ArrayList<ViewPort> viewPorts = new ArrayList<>();
private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>(); private final ArrayList<ViewPort> postViewPorts = new ArrayList<>();
private Camera prevCam = null; private Camera prevCam = null;
private Material forcedMaterial = null; private Material forcedMaterial = null;
private String forcedTechnique = null; private String forcedTechnique = null;
private RenderState forcedRenderState = null; private RenderState forcedRenderState = null;
private final List<MatParamOverride> forcedOverrides = new ArrayList<>();
private int viewX, viewY, viewWidth, viewHeight; private int viewX, viewY, viewWidth, viewHeight;
private Matrix4f orthoMatrix = new Matrix4f(); private final Matrix4f orthoMatrix = new Matrix4f();
private LightList filteredLightList = new LightList(null); private final LightList filteredLightList = new LightList(null);
private String tmpTech;
private boolean handleTranlucentBucket = true; private boolean handleTranlucentBucket = true;
private AppProfiler prof; private AppProfiler prof;
private LightFilter lightFilter = new DefaultLightFilter(); private LightFilter lightFilter = new DefaultLightFilter();
private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass; private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass;
private int singlePassLightBatchSize = 1; private int singlePassLightBatchSize = 1;
/** /**
* Create a high-level rendering interface over the * Create a high-level rendering interface over the
* low-level rendering interface. * low-level rendering interface.
@ -423,6 +428,44 @@ public class RenderManager {
this.forcedTechnique = forcedTechnique; this.forcedTechnique = forcedTechnique;
} }
/**
* Adds a forced material parameter to use when rendering geometries.
* <p>
* The provided parameter takes precedence over parameters set on the
* material or any overrides that exist in the scene graph that have the
* same name.
*
* @param override The override to add
* @see MatParamOverride
* @see #removeForcedMatParam(com.jme3.material.MatParamOverride)
*/
public void addForcedMatParam(MatParamOverride override) {
forcedOverrides.add(override);
}
/**
* Remove a forced material parameter previously added.
*
* @param override The override to remove.
* @see #addForcedMatParam(com.jme3.material.MatParamOverride)
*/
public void removeForcedMatParam(MatParamOverride override) {
forcedOverrides.remove(override);
}
/**
* Get the forced material parameters applied to rendered geometries.
* <p>
* Forced parameters can be added via
* {@link #addForcedMatParam(com.jme3.material.MatParamOverride)} or removed
* via {@link #removeForcedMatParam(com.jme3.material.MatParamOverride)}.
*
* @return The forced material parameters.
*/
public List<MatParamOverride> getForcedMatParams() {
return forcedOverrides;
}
/** /**
* Enable or disable alpha-to-coverage. * Enable or disable alpha-to-coverage.
* <p> * <p>
@ -480,8 +523,8 @@ public class RenderManager {
* Updates the given list of uniforms with {@link UniformBinding uniform bindings} * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
* based on the current world state. * based on the current world state.
*/ */
public void updateUniformBindings(List<Uniform> params) { public void updateUniformBindings(Shader shader) {
uniformBindingManager.updateUniformBindings(params); uniformBindingManager.updateUniformBindings(shader);
} }
/** /**
@ -508,45 +551,54 @@ public class RenderManager {
* for rendering the material, and the material's own render state is ignored. * for rendering the material, and the material's own render state is ignored.
* Otherwise, the material's render state is used as intended. * Otherwise, the material's render state is used as intended.
* *
* @param g The geometry to render * @param geom The geometry to render
* *
* @see Technique * @see Technique
* @see RenderState * @see RenderState
* @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
* @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
*/ */
public void renderGeometry(Geometry g) { public void renderGeometry(Geometry geom) {
if (g.isIgnoreTransform()) { if (geom.isIgnoreTransform()) {
setWorldMatrix(Matrix4f.IDENTITY); setWorldMatrix(Matrix4f.IDENTITY);
} else { } else {
setWorldMatrix(g.getWorldMatrix()); setWorldMatrix(geom.getWorldMatrix());
} }
// Perform light filtering if we have a light filter. // Perform light filtering if we have a light filter.
LightList lightList = g.getWorldLightList(); LightList lightList = geom.getWorldLightList();
if (lightFilter != null) { if (lightFilter != null) {
filteredLightList.clear(); filteredLightList.clear();
lightFilter.filterLights(g, filteredLightList); lightFilter.filterLights(geom, filteredLightList);
lightList = filteredLightList; lightList = filteredLightList;
} }
Material material = geom.getMaterial();
//if forcedTechnique we try to force it for render, //if forcedTechnique we try to force it for render,
//if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
//else the geom is not rendered //else the geom is not rendered
if (forcedTechnique != null) { if (forcedTechnique != null) {
if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) { MaterialDef matDef = material.getMaterialDef();
tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default"; if (matDef.getTechniqueDefs(forcedTechnique) != null) {
g.getMaterial().selectTechnique(forcedTechnique, this);
Technique activeTechnique = material.getActiveTechnique();
String previousTechniqueName = activeTechnique != null
? activeTechnique.getDef().getName()
: TechniqueDef.DEFAULT_TECHNIQUE_NAME;
geom.getMaterial().selectTechnique(forcedTechnique, this);
//saving forcedRenderState for future calls //saving forcedRenderState for future calls
RenderState tmpRs = forcedRenderState; RenderState tmpRs = forcedRenderState;
if (g.getMaterial().getActiveTechnique().getDef().getForcedRenderState() != null) { if (geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState() != null) {
//forcing forced technique renderState //forcing forced technique renderState
forcedRenderState = g.getMaterial().getActiveTechnique().getDef().getForcedRenderState(); forcedRenderState = geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState();
} }
// use geometry's material // use geometry's material
g.getMaterial().render(g, lightList, this); material.render(geom, lightList, this);
g.getMaterial().selectTechnique(tmpTech, this); material.selectTechnique(previousTechniqueName, this);
//restoring forcedRenderState //restoring forcedRenderState
forcedRenderState = tmpRs; forcedRenderState = tmpRs;
@ -555,13 +607,13 @@ public class RenderManager {
//If forcedTechnique does not exists, and forcedMaterial is not set, the geom MUST NOT be rendered //If forcedTechnique does not exists, and forcedMaterial is not set, the geom MUST NOT be rendered
} else if (forcedMaterial != null) { } else if (forcedMaterial != null) {
// use forced material // use forced material
forcedMaterial.render(g, lightList, this); forcedMaterial.render(geom, lightList, this);
} }
} else if (forcedMaterial != null) { } else if (forcedMaterial != null) {
// use forced material // use forced material
forcedMaterial.render(g, lightList, this); forcedMaterial.render(geom, lightList, this);
} else { } else {
g.getMaterial().render(g, lightList, this); material.render(geom, lightList, this);
} }
} }
@ -612,7 +664,9 @@ public class RenderManager {
gm.getMaterial().preload(this); gm.getMaterial().preload(this);
Mesh mesh = gm.getMesh(); Mesh mesh = gm.getMesh();
if (mesh != null) { if (mesh != null
&& mesh.getVertexCount() != 0
&& mesh.getTriangleCount() != 0) {
for (VertexBuffer vb : mesh.getBufferList().getArray()) { for (VertexBuffer vb : mesh.getBufferList().getArray()) {
if (vb.getData() != null && vb.getUsage() != VertexBuffer.Usage.CpuOnly) { if (vb.getData() != null && vb.getUsage() != VertexBuffer.Usage.CpuOnly) {
renderer.updateBufferData(vb); renderer.updateBufferData(vb);
@ -637,8 +691,10 @@ public class RenderManager {
* <p> * <p>
* In addition to enqueuing the visible geometries, this method * In addition to enqueuing the visible geometries, this method
* also scenes which cast or receive shadows, by putting them into the * also scenes which cast or receive shadows, by putting them into the
* RenderQueue's {@link RenderQueue#renderShadowQueue(GeometryList, RenderManager, Camera, boolean) shadow queue}. * RenderQueue's
* Each Spatial which has its {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode} * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
* shadow queue}. Each Spatial which has its
* {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
* set to not off, will be put into the appropriate shadow queue, note that * set to not off, will be put into the appropriate shadow queue, note that
* this process does not check for frustum culling on any * this process does not check for frustum culling on any
* {@link ShadowMode#Cast shadow casters}, as they don't have to be * {@link ShadowMode#Cast shadow casters}, as they don't have to be
@ -985,7 +1041,8 @@ public class RenderManager {
* (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li> * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
* <li>If any objects remained in the render queue, they are removed * <li>If any objects remained in the render queue, they are removed
* from the queue. This is generally objects added to the * from the queue. This is generally objects added to the
* {@link RenderQueue#renderShadowQueue(GeometryList, RenderManager, Camera, boolean) shadow queue} * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
* shadow queue}
* which were not rendered because of a missing shadow renderer.</li> * which were not rendered because of a missing shadow renderer.</li>
* </ul> * </ul>
* *

@ -43,6 +43,7 @@ import com.jme3.texture.Image;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.util.NativeObject; import com.jme3.util.NativeObject;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.EnumMap;
import java.util.EnumSet; import java.util.EnumSet;
/** /**
@ -66,6 +67,13 @@ public interface Renderer {
*/ */
public EnumSet<Caps> getCaps(); public EnumSet<Caps> getCaps();
/**
* Get the limits of the renderer.
*
* @return The limits of the renderer.
*/
public EnumMap<Limits, Integer> getLimits();
/** /**
* The statistics allow tracking of how data * The statistics allow tracking of how data
* per frame, such as number of objects rendered, number of triangles, etc. * per frame, such as number of objects rendered, number of triangles, etc.
@ -302,7 +310,21 @@ public interface Renderer {
* @see NativeObject#deleteObject(java.lang.Object) * @see NativeObject#deleteObject(java.lang.Object)
*/ */
public void cleanup(); public void cleanup();
/**
* Set the default anisotropic filter level for textures.
*
* If the
* {@link Texture#setAnisotropicFilter(int) texture anisotropic filter} is
* set to 0, then the default level is used. Otherwise if the texture level
* is 1 or greater, then the texture's value overrides the default value.
*
* @param level The default anisotropic filter level to use. Default: 1.
*
* @throws IllegalArgumentException If level is less than 1.
*/
public void setDefaultAnisotropicFilter(int level);
/** /**
* Sets the alpha to coverage state. * Sets the alpha to coverage state.
* <p> * <p>

@ -45,142 +45,149 @@ import java.nio.ShortBuffer;
*/ */
public interface GL { public interface GL {
public static final int GL_ALPHA = 0x1906; public static final int GL_ALPHA = 0x1906;
public static final int GL_ALWAYS = 0x207; public static final int GL_ALWAYS = 0x207;
public static final int GL_ARRAY_BUFFER = 0x8892; public static final int GL_ARRAY_BUFFER = 0x8892;
public static final int GL_BACK = 0x405; public static final int GL_BACK = 0x405;
public static final int GL_BLEND = 0xBE2; public static final int GL_BLEND = 0xBE2;
public static final int GL_BYTE = 0x1400; public static final int GL_BYTE = 0x1400;
public static final int GL_CLAMP_TO_EDGE = 0x812F; public static final int GL_CLAMP_TO_EDGE = 0x812F;
public static final int GL_COLOR_BUFFER_BIT = 0x4000; public static final int GL_COLOR_BUFFER_BIT = 0x4000;
public static final int GL_COMPILE_STATUS = 0x8B81; public static final int GL_COMPILE_STATUS = 0x8B81;
public static final int GL_CULL_FACE = 0xB44; public static final int GL_CULL_FACE = 0xB44;
public static final int GL_DECR = 0x1E03; public static final int GL_DECR = 0x1E03;
public static final int GL_DECR_WRAP = 0x8508; public static final int GL_DECR_WRAP = 0x8508;
public static final int GL_DEPTH_BUFFER_BIT = 0x100; public static final int GL_DEPTH_BUFFER_BIT = 0x100;
public static final int GL_DEPTH_COMPONENT = 0x1902; public static final int GL_DEPTH_COMPONENT = 0x1902;
public static final int GL_DEPTH_COMPONENT16 = 0x81A5; public static final int GL_DEPTH_COMPONENT16 = 0x81A5;
public static final int GL_DEPTH_TEST = 0xB71; public static final int GL_DEPTH_TEST = 0xB71;
public static final int GL_DOUBLE = 0x140A; public static final int GL_DOUBLE = 0x140A;
public static final int GL_DST_COLOR = 0x306; public static final int GL_DST_COLOR = 0x306;
public static final int GL_DYNAMIC_DRAW = 0x88E8; public static final int GL_DYNAMIC_DRAW = 0x88E8;
public static final int GL_ELEMENT_ARRAY_BUFFER = 0x8893; public static final int GL_ELEMENT_ARRAY_BUFFER = 0x8893;
public static final int GL_EQUAL = 0x202; public static final int GL_EQUAL = 0x202;
public static final int GL_EXTENSIONS = 0x1F03; public static final int GL_EXTENSIONS = 0x1F03;
public static final int GL_FALSE = 0x0; public static final int GL_FALSE = 0x0;
public static final int GL_FLOAT = 0x1406; public static final int GL_FLOAT = 0x1406;
public static final int GL_FRAGMENT_SHADER = 0x8B30; public static final int GL_FRAGMENT_SHADER = 0x8B30;
public static final int GL_FRONT = 0x404; public static final int GL_FRONT = 0x404;
public static final int GL_FRONT_AND_BACK = 0x408; public static final int GL_FUNC_ADD = 0x8006;
public static final int GL_GEQUAL = 0x206; public static final int GL_FUNC_SUBTRACT = 0x800A;
public static final int GL_GREATER = 0x204; public static final int GL_FUNC_REVERSE_SUBTRACT = 0x800B;
public static final int GL_GREEN = 0x1904; public static final int GL_FRONT_AND_BACK = 0x408;
public static final int GL_INCR = 0x1E02; public static final int GL_GEQUAL = 0x206;
public static final int GL_INCR_WRAP = 0x8507; public static final int GL_GREATER = 0x204;
public static final int GL_INFO_LOG_LENGTH = 0x8B84; public static final int GL_GREEN = 0x1904;
public static final int GL_INT = 0x1404; public static final int GL_INCR = 0x1E02;
public static final int GL_INVALID_ENUM = 0x500; public static final int GL_INCR_WRAP = 0x8507;
public static final int GL_INVALID_VALUE = 0x501; public static final int GL_INFO_LOG_LENGTH = 0x8B84;
public static final int GL_INVALID_OPERATION = 0x502; public static final int GL_INT = 0x1404;
public static final int GL_INVERT = 0x150A; public static final int GL_INVALID_ENUM = 0x500;
public static final int GL_KEEP = 0x1E00; public static final int GL_INVALID_VALUE = 0x501;
public static final int GL_LEQUAL = 0x203; public static final int GL_INVALID_OPERATION = 0x502;
public static final int GL_LESS = 0x201; public static final int GL_INVERT = 0x150A;
public static final int GL_LINEAR = 0x2601; public static final int GL_KEEP = 0x1E00;
public static final int GL_LINEAR_MIPMAP_LINEAR = 0x2703; public static final int GL_LEQUAL = 0x203;
public static final int GL_LINEAR_MIPMAP_NEAREST = 0x2701; public static final int GL_LESS = 0x201;
public static final int GL_LINES = 0x1; public static final int GL_LINEAR = 0x2601;
public static final int GL_LINE_LOOP = 0x2; public static final int GL_LINEAR_MIPMAP_LINEAR = 0x2703;
public static final int GL_LINE_STRIP = 0x3; public static final int GL_LINEAR_MIPMAP_NEAREST = 0x2701;
public static final int GL_LINK_STATUS = 0x8B82; public static final int GL_LINES = 0x1;
public static final int GL_LUMINANCE = 0x1909; public static final int GL_LINE_LOOP = 0x2;
public static final int GL_LUMINANCE_ALPHA = 0x190A; public static final int GL_LINE_STRIP = 0x3;
public static final int GL_MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C; public static final int GL_LINK_STATUS = 0x8B82;
public static final int GL_MAX_TEXTURE_IMAGE_UNITS = 0x8872; public static final int GL_LUMINANCE = 0x1909;
public static final int GL_MAX_TEXTURE_SIZE = 0xD33; public static final int GL_LUMINANCE_ALPHA = 0x190A;
public static final int GL_MAX_VERTEX_ATTRIBS = 0x8869; public static final int GL_MAX = 0x8008;
public static final int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C; public static final int GL_MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C;
public static final int GL_MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A; public static final int GL_MAX_FRAGMENT_UNIFORM_COMPONENTS = 0x8B49;
public static final int GL_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB; public static final int GL_MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD;
public static final int GL_MIRRORED_REPEAT = 0x8370; public static final int GL_MAX_TEXTURE_IMAGE_UNITS = 0x8872;
public static final int GL_NEAREST = 0x2600; public static final int GL_MAX_TEXTURE_SIZE = 0xD33;
public static final int GL_NEAREST_MIPMAP_LINEAR = 0x2702; public static final int GL_MAX_VERTEX_ATTRIBS = 0x8869;
public static final int GL_NEAREST_MIPMAP_NEAREST = 0x2700; public static final int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C;
public static final int GL_NEVER = 0x200; public static final int GL_MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A;
public static final int GL_NO_ERROR = 0x0; public static final int GL_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB;
public static final int GL_NONE = 0x0; public static final int GL_MIRRORED_REPEAT = 0x8370;
public static final int GL_NOTEQUAL = 0x205; public static final int GL_MIN = 0x8007;
public static final int GL_ONE = 0x1; public static final int GL_NEAREST = 0x2600;
public static final int GL_ONE_MINUS_DST_COLOR = 0x307; public static final int GL_NEAREST_MIPMAP_LINEAR = 0x2702;
public static final int GL_ONE_MINUS_SRC_ALPHA = 0x303; public static final int GL_NEAREST_MIPMAP_NEAREST = 0x2700;
public static final int GL_ONE_MINUS_SRC_COLOR = 0x301; public static final int GL_NEVER = 0x200;
public static final int GL_OUT_OF_MEMORY = 0x505; public static final int GL_NO_ERROR = 0x0;
public static final int GL_POINTS = 0x0; public static final int GL_NONE = 0x0;
public static final int GL_POLYGON_OFFSET_FILL = 0x8037; public static final int GL_NOTEQUAL = 0x205;
public static final int GL_RED = 0x1903; public static final int GL_ONE = 0x1;
public static final int GL_RENDERER = 0x1F01; public static final int GL_ONE_MINUS_DST_COLOR = 0x307;
public static final int GL_REPEAT = 0x2901; public static final int GL_ONE_MINUS_SRC_ALPHA = 0x303;
public static final int GL_REPLACE = 0x1E01; public static final int GL_ONE_MINUS_SRC_COLOR = 0x301;
public static final int GL_RGB = 0x1907; public static final int GL_OUT_OF_MEMORY = 0x505;
public static final int GL_RGB565 = 0x8D62; public static final int GL_POINTS = 0x0;
public static final int GL_RGB5_A1 = 0x8057; public static final int GL_POLYGON_OFFSET_FILL = 0x8037;
public static final int GL_RGBA = 0x1908; public static final int GL_RED = 0x1903;
public static final int GL_RGBA4 = 0x8056; public static final int GL_RENDERER = 0x1F01;
public static final int GL_SCISSOR_TEST = 0xC11; public static final int GL_REPEAT = 0x2901;
public static final int GL_SHADING_LANGUAGE_VERSION = 0x8B8C; public static final int GL_REPLACE = 0x1E01;
public static final int GL_SHORT = 0x1402; public static final int GL_RGB = 0x1907;
public static final int GL_SRC_ALPHA = 0x302; public static final int GL_RGB565 = 0x8D62;
public static final int GL_SRC_COLOR = 0x300; public static final int GL_RGB5_A1 = 0x8057;
public static final int GL_STATIC_DRAW = 0x88E4; public static final int GL_RGBA = 0x1908;
public static final int GL_STENCIL_BUFFER_BIT = 0x400; public static final int GL_RGBA4 = 0x8056;
public static final int GL_STENCIL_TEST = 0xB90; public static final int GL_SCISSOR_TEST = 0xC11;
public static final int GL_STREAM_DRAW = 0x88E0; public static final int GL_SHADING_LANGUAGE_VERSION = 0x8B8C;
public static final int GL_STREAM_READ = 0x88E1; public static final int GL_SHORT = 0x1402;
public static final int GL_TEXTURE = 0x1702; public static final int GL_SRC_ALPHA = 0x302;
public static final int GL_TEXTURE0 = 0x84C0; public static final int GL_SRC_COLOR = 0x300;
public static final int GL_TEXTURE1 = 0x84C1; public static final int GL_STATIC_DRAW = 0x88E4;
public static final int GL_TEXTURE2 = 0x84C2; public static final int GL_STENCIL_BUFFER_BIT = 0x400;
public static final int GL_TEXTURE3 = 0x84C3; public static final int GL_STENCIL_TEST = 0xB90;
public static final int GL_TEXTURE4 = 0x84C4; public static final int GL_STREAM_DRAW = 0x88E0;
public static final int GL_TEXTURE5 = 0x84C5; public static final int GL_STREAM_READ = 0x88E1;
public static final int GL_TEXTURE6 = 0x84C6; public static final int GL_TEXTURE = 0x1702;
public static final int GL_TEXTURE7 = 0x84C7; public static final int GL_TEXTURE0 = 0x84C0;
public static final int GL_TEXTURE8 = 0x84C8; public static final int GL_TEXTURE1 = 0x84C1;
public static final int GL_TEXTURE9 = 0x84C9; public static final int GL_TEXTURE2 = 0x84C2;
public static final int GL_TEXTURE10 = 0x84CA; public static final int GL_TEXTURE3 = 0x84C3;
public static final int GL_TEXTURE11 = 0x84CB; public static final int GL_TEXTURE4 = 0x84C4;
public static final int GL_TEXTURE12 = 0x84CC; public static final int GL_TEXTURE5 = 0x84C5;
public static final int GL_TEXTURE13 = 0x84CD; public static final int GL_TEXTURE6 = 0x84C6;
public static final int GL_TEXTURE14 = 0x84CE; public static final int GL_TEXTURE7 = 0x84C7;
public static final int GL_TEXTURE15 = 0x84CF; public static final int GL_TEXTURE8 = 0x84C8;
public static final int GL_TEXTURE_2D = 0xDE1; public static final int GL_TEXTURE9 = 0x84C9;
public static final int GL_TEXTURE_CUBE_MAP = 0x8513; public static final int GL_TEXTURE10 = 0x84CA;
public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515; public static final int GL_TEXTURE11 = 0x84CB;
public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516; public static final int GL_TEXTURE12 = 0x84CC;
public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517; public static final int GL_TEXTURE13 = 0x84CD;
public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518; public static final int GL_TEXTURE14 = 0x84CE;
public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519; public static final int GL_TEXTURE15 = 0x84CF;
public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A; public static final int GL_TEXTURE_2D = 0xDE1;
public static final int GL_TEXTURE_BASE_LEVEL = 0x813C; public static final int GL_TEXTURE_CUBE_MAP = 0x8513;
public static final int GL_TEXTURE_MAG_FILTER = 0x2800; public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515;
public static final int GL_TEXTURE_MAX_LEVEL = 0x813D; public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516;
public static final int GL_TEXTURE_MIN_FILTER = 0x2801; public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517;
public static final int GL_TEXTURE_WRAP_S = 0x2802; public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
public static final int GL_TEXTURE_WRAP_T = 0x2803; public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
public static final int GL_TRIANGLES = 0x4; public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
public static final int GL_TRIANGLE_FAN = 0x6; public static final int GL_TEXTURE_BASE_LEVEL = 0x813C;
public static final int GL_TRIANGLE_STRIP = 0x5; public static final int GL_TEXTURE_MAG_FILTER = 0x2800;
public static final int GL_TRUE = 0x1; public static final int GL_TEXTURE_MAX_LEVEL = 0x813D;
public static final int GL_UNPACK_ALIGNMENT = 0xCF5; public static final int GL_TEXTURE_MIN_FILTER = 0x2801;
public static final int GL_UNSIGNED_BYTE = 0x1401; public static final int GL_TEXTURE_WRAP_S = 0x2802;
public static final int GL_UNSIGNED_INT = 0x1405; public static final int GL_TEXTURE_WRAP_T = 0x2803;
public static final int GL_UNSIGNED_SHORT = 0x1403; public static final int GL_TRIANGLES = 0x4;
public static final int GL_UNSIGNED_SHORT_5_6_5 = 0x8363; public static final int GL_TRIANGLE_FAN = 0x6;
public static final int GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034; public static final int GL_TRIANGLE_STRIP = 0x5;
public static final int GL_VENDOR = 0x1F00; public static final int GL_TRUE = 0x1;
public static final int GL_VERSION = 0x1F02; public static final int GL_UNPACK_ALIGNMENT = 0xCF5;
public static final int GL_VERTEX_SHADER = 0x8B31; public static final int GL_UNSIGNED_BYTE = 0x1401;
public static final int GL_ZERO = 0x0; public static final int GL_UNSIGNED_INT = 0x1405;
public static final int GL_UNSIGNED_SHORT = 0x1403;
public static final int GL_UNSIGNED_SHORT_5_6_5 = 0x8363;
public static final int GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034;
public static final int GL_VENDOR = 0x1F00;
public static final int GL_VERSION = 0x1F02;
public static final int GL_VERTEX_SHADER = 0x8B31;
public static final int GL_ZERO = 0x0;
public void resetStats(); public void resetStats();
@ -188,6 +195,7 @@ public interface GL {
public void glAttachShader(int program, int shader); public void glAttachShader(int program, int shader);
public void glBindBuffer(int target, int buffer); public void glBindBuffer(int target, int buffer);
public void glBindTexture(int target, int texture); public void glBindTexture(int target, int texture);
public void glBlendEquationSeparate(int colorMode, int alphaMode);
public void glBlendFunc(int sfactor, int dfactor); public void glBlendFunc(int sfactor, int dfactor);
public void glBufferData(int target, long data_size, int usage); public void glBufferData(int target, long data_size, int usage);
public void glBufferData(int target, FloatBuffer data, int usage); public void glBufferData(int target, FloatBuffer data, int usage);

@ -100,4 +100,9 @@ public class GLDebugDesktop extends GLDebugES implements GL2, GL3, GL4 {
gl3.glFramebufferTextureLayer(param1, param2, param3, param4, param5); gl3.glFramebufferTextureLayer(param1, param2, param3, param4, param5);
checkError(); checkError();
} }
public void glBlendEquationSeparate(int colorMode, int alphaMode) {
gl.glBlendEquationSeparate(colorMode, alphaMode);
checkError();
}
} }

@ -560,4 +560,9 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
checkError(); checkError();
return sync; return sync;
} }
public void glBlendEquationSeparate(int colorMode, int alphaMode) {
gl.glBlendEquationSeparate(colorMode, alphaMode);
checkError();
}
} }

@ -90,6 +90,7 @@ public final class GLRenderer implements Renderer {
private final Statistics statistics = new Statistics(); private final Statistics statistics = new Statistics();
private int vpX, vpY, vpW, vpH; private int vpX, vpY, vpW, vpH;
private int clipX, clipY, clipW, clipH; private int clipX, clipY, clipW, clipH;
private int defaultAnisotropicFilter = 1;
private boolean linearizeSrgbImages; private boolean linearizeSrgbImages;
private HashSet<String> extensions; private HashSet<String> extensions;
@ -252,18 +253,14 @@ public final class GLRenderer implements Renderer {
limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS)); limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS));
// gl.glGetInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16);
// vertexUniforms = intBuf16.get(0);
// logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
//
// gl.glGetInteger(GL.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16);
// fragUniforms = intBuf16.get(0);
// logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
if (caps.contains(Caps.OpenGLES20)) { if (caps.contains(Caps.OpenGLES20)) {
limits.put(Limits.FragmentUniformVectors, getInteger(GL.GL_MAX_FRAGMENT_UNIFORM_VECTORS));
limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_VECTORS)); limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_VECTORS));
} else { } else {
limits.put(Limits.FragmentUniformVectors, getInteger(GL.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4);
limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4); limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4);
} }
limits.put(Limits.VertexAttributes, getInteger(GL.GL_MAX_VERTEX_ATTRIBS)); limits.put(Limits.VertexAttributes, getInteger(GL.GL_MAX_VERTEX_ATTRIBS));
limits.put(Limits.TextureSize, getInteger(GL.GL_MAX_TEXTURE_SIZE)); limits.put(Limits.TextureSize, getInteger(GL.GL_MAX_TEXTURE_SIZE));
limits.put(Limits.CubemapSize, getInteger(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE)); limits.put(Limits.CubemapSize, getInteger(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE));
@ -474,6 +471,17 @@ public final class GLRenderer implements Renderer {
{ {
sb.append("\t").append(cap.toString()).append("\n"); sb.append("\t").append(cap.toString()).append("\n");
} }
sb.append("\nHardware limits: \n");
for (Limits limit : Limits.values()) {
Integer value = limits.get(limit);
if (value == null) {
value = 0;
}
sb.append("\t").append(limit.name()).append(" = ")
.append(value).append("\n");
}
logger.log(Level.FINE, sb.toString()); logger.log(Level.FINE, sb.toString());
} }
@ -522,7 +530,6 @@ public final class GLRenderer implements Renderer {
gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE); gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
if (!caps.contains(Caps.CoreProfile)) { if (!caps.contains(Caps.CoreProfile)) {
gl2.glEnable(GL2.GL_POINT_SPRITE); gl2.glEnable(GL2.GL_POINT_SPRITE);
context.pointSprite = true;
} }
} }
} }
@ -594,6 +601,14 @@ public final class GLRenderer implements Renderer {
} }
} }
@Override
public void setDefaultAnisotropicFilter(int level) {
if (level < 1) {
throw new IllegalArgumentException("level cannot be less than 1");
}
this.defaultAnisotropicFilter = level;
}
public void setAlphaToCoverage(boolean value) { public void setAlphaToCoverage(boolean value) {
if (caps.contains(Caps.Multisample)) { if (caps.contains(Caps.Multisample)) {
if (value) { if (value) {
@ -735,6 +750,19 @@ public final class GLRenderer implements Renderer {
throw new UnsupportedOperationException("Unrecognized blend mode: " throw new UnsupportedOperationException("Unrecognized blend mode: "
+ state.getBlendMode()); + state.getBlendMode());
} }
if (state.getBlendEquation() != context.blendEquation || state.getBlendEquationAlpha() != context.blendEquationAlpha) {
int colorMode = convertBlendEquation(state.getBlendEquation());
int alphaMode;
if (state.getBlendEquationAlpha() == RenderState.BlendEquationAlpha.InheritColor) {
alphaMode = colorMode;
} else {
alphaMode = convertBlendEquationAlpha(state.getBlendEquationAlpha());
}
gl.glBlendEquationSeparate(colorMode, alphaMode);
context.blendEquation = state.getBlendEquation();
context.blendEquationAlpha = state.getBlendEquationAlpha();
}
} }
context.blendMode = state.getBlendMode(); context.blendMode = state.getBlendMode();
@ -785,6 +813,41 @@ public final class GLRenderer implements Renderer {
} }
} }
private int convertBlendEquation(RenderState.BlendEquation blendEquation) {
switch (blendEquation) {
case Add:
return GL2.GL_FUNC_ADD;
case Subtract:
return GL2.GL_FUNC_SUBTRACT;
case ReverseSubtract:
return GL2.GL_FUNC_REVERSE_SUBTRACT;
case Min:
return GL2.GL_MIN;
case Max:
return GL2.GL_MAX;
default:
throw new UnsupportedOperationException("Unrecognized blend operation: " + blendEquation);
}
}
private int convertBlendEquationAlpha(RenderState.BlendEquationAlpha blendEquationAlpha) {
//Note: InheritColor mode should already be handled, that is why it does not belong the the switch case.
switch (blendEquationAlpha) {
case Add:
return GL2.GL_FUNC_ADD;
case Subtract:
return GL2.GL_FUNC_SUBTRACT;
case ReverseSubtract:
return GL2.GL_FUNC_REVERSE_SUBTRACT;
case Min:
return GL2.GL_MIN;
case Max:
return GL2.GL_MAX;
default:
throw new UnsupportedOperationException("Unrecognized alpha blend operation: " + blendEquationAlpha);
}
}
private int convertStencilOperation(StencilOperation stencilOp) { private int convertStencilOperation(StencilOperation stencilOp) {
switch (stencilOp) { switch (stencilOp) {
case Keep: case Keep:
@ -964,12 +1027,12 @@ public final class GLRenderer implements Renderer {
gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE); gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE);
break; break;
case Matrix3: case Matrix3:
fb = (FloatBuffer) uniform.getValue(); fb = uniform.getMultiData();
assert fb.remaining() == 9; assert fb.remaining() == 9;
gl.glUniformMatrix3(loc, false, fb); gl.glUniformMatrix3(loc, false, fb);
break; break;
case Matrix4: case Matrix4:
fb = (FloatBuffer) uniform.getValue(); fb = uniform.getMultiData();
assert fb.remaining() == 16; assert fb.remaining() == 16;
gl.glUniformMatrix4(loc, false, fb); gl.glUniformMatrix4(loc, false, fb);
break; break;
@ -978,23 +1041,23 @@ public final class GLRenderer implements Renderer {
gl.glUniform1(loc, ib); gl.glUniform1(loc, ib);
break; break;
case FloatArray: case FloatArray:
fb = (FloatBuffer) uniform.getValue(); fb = uniform.getMultiData();
gl.glUniform1(loc, fb); gl.glUniform1(loc, fb);
break; break;
case Vector2Array: case Vector2Array:
fb = (FloatBuffer) uniform.getValue(); fb = uniform.getMultiData();
gl.glUniform2(loc, fb); gl.glUniform2(loc, fb);
break; break;
case Vector3Array: case Vector3Array:
fb = (FloatBuffer) uniform.getValue(); fb = uniform.getMultiData();
gl.glUniform3(loc, fb); gl.glUniform3(loc, fb);
break; break;
case Vector4Array: case Vector4Array:
fb = (FloatBuffer) uniform.getValue(); fb = uniform.getMultiData();
gl.glUniform4(loc, fb); gl.glUniform4(loc, fb);
break; break;
case Matrix4Array: case Matrix4Array:
fb = (FloatBuffer) uniform.getValue(); fb = uniform.getMultiData();
gl.glUniformMatrix4(loc, false, fb); gl.glUniformMatrix4(loc, false, fb);
break; break;
case Int: case Int:
@ -1872,13 +1935,18 @@ public final class GLRenderer implements Renderer {
gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, convertMinFilter(tex.getMinFilter(), haveMips)); gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, convertMinFilter(tex.getMinFilter(), haveMips));
curState.minFilter = tex.getMinFilter(); curState.minFilter = tex.getMinFilter();
} }
int desiredAnisoFilter = tex.getAnisotropicFilter() == 0
? defaultAnisotropicFilter
: tex.getAnisotropicFilter();
if (caps.contains(Caps.TextureFilterAnisotropic) if (caps.contains(Caps.TextureFilterAnisotropic)
&& curState.anisoFilter != tex.getAnisotropicFilter()) { && curState.anisoFilter != desiredAnisoFilter) {
bindTextureAndUnit(target, image, unit); bindTextureAndUnit(target, image, unit);
gl.glTexParameterf(target, gl.glTexParameterf(target,
GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT, GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT,
tex.getAnisotropicFilter()); desiredAnisoFilter);
curState.anisoFilter = tex.getAnisotropicFilter(); curState.anisoFilter = desiredAnisoFilter;
} }
switch (tex.getType()) { switch (tex.getType()) {
@ -2689,12 +2757,15 @@ public final class GLRenderer implements Renderer {
} }
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
if (mesh.getVertexCount() == 0) { if (mesh.getVertexCount() == 0 || mesh.getTriangleCount() == 0 || count == 0) {
return; return;
} }
//this is kept for backward compatibility. if (count > 1 && !caps.contains(Caps.MeshInstancing)) {
if (mesh.getLineWidth() != -1 && context.lineWidth != mesh.getLineWidth()) { throw new RendererException("Mesh instancing is not supported by the video hardware");
}
if (mesh.getLineWidth() != 1f && context.lineWidth != mesh.getLineWidth()) {
gl.glLineWidth(mesh.getLineWidth()); gl.glLineWidth(mesh.getLineWidth());
context.lineWidth = mesh.getLineWidth(); context.lineWidth = mesh.getLineWidth();
} }

@ -99,6 +99,16 @@ public class GeometryList implements Iterable<Geometry>{
return size; return size;
} }
/**
* Sets the element at the given index.
*
* @param index The index to set
* @param value The value
*/
public void set(int index, Geometry value) {
geometries[index] = value;
}
/** /**
* Returns the element at the given index. * Returns the element at the given index.
* *

@ -69,11 +69,12 @@ public class OpaqueComparator implements GeometryComparator {
return spat.queueDistance; return spat.queueDistance;
} }
@Override
public int compare(Geometry o1, Geometry o2) { public int compare(Geometry o1, Geometry o2) {
Material m1 = o1.getMaterial(); Material m1 = o1.getMaterial();
Material m2 = o2.getMaterial(); Material m2 = o2.getMaterial();
int compareResult = m2.getSortId() - m1.getSortId(); int compareResult = Integer.compare(m1.getSortId(), m2.getSortId());
if (compareResult == 0){ if (compareResult == 0){
// use the same shader. // use the same shader.
// sort front-to-back then. // sort front-to-back then.

@ -175,7 +175,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>(); private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
private VertexBuffer[] lodLevels; private VertexBuffer[] lodLevels;
private float pointSize = 1; private float pointSize = 1;
private float lineWidth = -1; private float lineWidth = 1;
private transient int vertexArrayID = -1; private transient int vertexArrayID = -1;
@ -589,28 +589,26 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
} }
/** /**
* Returns the size of points for point meshes * @deprecated Always returns <code>1.0</code> since point size is
* determined in the vertex shader.
* *
* @return the size of points * @return <code>1.0</code>
* *
* @see #setPointSize(float) * @see #setPointSize(float)
*/ */
@Deprecated
public float getPointSize() { public float getPointSize() {
return pointSize; return 1.0f;
} }
/** /**
* Set the size of points for meshes of mode {@link Mode#Points}. * @deprecated Does nothing, since the size of {@link Mode#Points points} is
* The point size is specified as on-screen pixels, the default * determined via the vertex shader's <code>gl_PointSize</code> output.
* value is 1.0. The point size
* does nothing if {@link RenderState#setPointSprite(boolean) point sprite}
* render state is enabled, in that case, the vertex shader must specify the
* point size by writing to <code>gl_PointSize</code>.
* *
* @param pointSize The size of points * @param pointSize ignored
*/ */
@Deprecated
public void setPointSize(float pointSize) { public void setPointSize(float pointSize) {
this.pointSize = pointSize;
} }
/** /**
@ -634,6 +632,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
*/ */
@Deprecated @Deprecated
public void setLineWidth(float lineWidth) { public void setLineWidth(float lineWidth) {
if (lineWidth < 1f) {
throw new IllegalArgumentException("lineWidth must be greater than or equal to 1.0");
}
this.lineWidth = lineWidth; this.lineWidth = lineWidth;
} }

@ -75,7 +75,6 @@ public class Node extends Spatial {
* requiresUpdate() method. * requiresUpdate() method.
*/ */
private SafeArrayList<Spatial> updateList = null; private SafeArrayList<Spatial> updateList = null;
/** /**
* False if the update list requires rebuilding. This is Node.class * False if the update list requires rebuilding. This is Node.class
* specific and therefore not included as part of the Spatial update flags. * specific and therefore not included as part of the Spatial update flags.
@ -100,7 +99,6 @@ public class Node extends Spatial {
*/ */
public Node(String name) { public Node(String name) {
super(name); super(name);
// For backwards compatibility, only clear the "requires // For backwards compatibility, only clear the "requires
// update" flag if we are not a subclass of Node. // update" flag if we are not a subclass of Node.
// This prevents subclass from silently failing to receive // This prevents subclass from silently failing to receive
@ -141,10 +139,21 @@ public class Node extends Spatial {
} }
} }
@Override
protected void setMatParamOverrideRefresh() {
super.setMatParamOverrideRefresh();
for (Spatial child : children.getArray()) {
if ((child.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
continue;
}
child.setMatParamOverrideRefresh();
}
}
@Override @Override
protected void updateWorldBound(){ protected void updateWorldBound(){
super.updateWorldBound(); super.updateWorldBound();
// for a node, the world bound is a combination of all it's children // for a node, the world bound is a combination of all it's children
// bounds // bounds
BoundingVolume resultBound = null; BoundingVolume resultBound = null;
@ -239,19 +248,19 @@ public class Node extends Spatial {
// This branch has no geometric state that requires updates. // This branch has no geometric state that requires updates.
return; return;
} }
if ((refreshFlags & RF_LIGHTLIST) != 0){ if ((refreshFlags & RF_LIGHTLIST) != 0){
updateWorldLightList(); updateWorldLightList();
} }
if ((refreshFlags & RF_TRANSFORM) != 0){ if ((refreshFlags & RF_TRANSFORM) != 0){
// combine with parent transforms- same for all spatial // combine with parent transforms- same for all spatial
// subclasses. // subclasses.
updateWorldTransforms(); updateWorldTransforms();
} }
if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
updateMatParamOverrides();
}
refreshFlags &= ~RF_CHILD_LIGHTLIST; refreshFlags &= ~RF_CHILD_LIGHTLIST;
if (!children.isEmpty()) { if (!children.isEmpty()) {
// the important part- make sure child geometric state is refreshed // the important part- make sure child geometric state is refreshed
// first before updating own world bound. This saves // first before updating own world bound. This saves
@ -287,7 +296,6 @@ public class Node extends Spatial {
return count; return count;
} }
/** /**
* <code>getVertexCount</code> returns the number of vertices contained * <code>getVertexCount</code> returns the number of vertices contained
* in all sub-branches of this node that contain geometry. * in all sub-branches of this node that contain geometry.
@ -321,7 +329,6 @@ public class Node extends Spatial {
public int attachChild(Spatial child) { public int attachChild(Spatial child) {
return attachChildAt(child, children.size()); return attachChildAt(child, children.size());
} }
/** /**
* *
* <code>attachChildAt</code> attaches a child to this node at an index. This node * <code>attachChildAt</code> attaches a child to this node at an index. This node
@ -345,20 +352,18 @@ public class Node extends Spatial {
} }
child.setParent(this); child.setParent(this);
children.add(index, child); children.add(index, child);
// XXX: Not entirely correct? Forces bound update up the // XXX: Not entirely correct? Forces bound update up the
// tree stemming from the attached child. Also forces // tree stemming from the attached child. Also forces
// transform update down the tree- // transform update down the tree-
child.setTransformRefresh(); child.setTransformRefresh();
child.setLightListRefresh(); child.setLightListRefresh();
child.setMatParamOverrideRefresh();
if (logger.isLoggable(Level.FINE)) { if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,"Child ({0}) attached to this node ({1})", logger.log(Level.FINE,"Child ({0}) attached to this node ({1})",
new Object[]{child.getName(), getName()}); new Object[]{child.getName(), getName()});
} }
invalidateUpdateList(); invalidateUpdateList();
} }
return children.size(); return children.size();
} }
@ -433,7 +438,8 @@ public class Node extends Spatial {
child.setTransformRefresh(); child.setTransformRefresh();
// lights are also inherited from parent // lights are also inherited from parent
child.setLightListRefresh(); child.setLightListRefresh();
child.setMatParamOverrideRefresh();
invalidateUpdateList(); invalidateUpdateList();
} }
return child; return child;
@ -519,7 +525,6 @@ public class Node extends Spatial {
} }
return null; return null;
} }
/** /**
* determines if the provided Spatial is contained in the children list of * determines if the provided Spatial is contained in the children list of
* this node. * this node.
@ -567,39 +572,32 @@ public class Node extends Spatial {
public int collideWith(Collidable other, CollisionResults results){ public int collideWith(Collidable other, CollisionResults results){
int total = 0; int total = 0;
// optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children // optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
// number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all. // number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
// The idea is when there are few children, it can be too expensive to test boundingVolume first. // The idea is when there are few children, it can be too expensive to test boundingVolume first.
/* /*
I'm removing this change until some issues can be addressed and I really I'm removing this change until some issues can be addressed and I really
think it needs to be implemented a better way anyway. think it needs to be implemented a better way anyway.
First, it causes issues for anyone doing collideWith() with BoundingVolumes First, it causes issues for anyone doing collideWith() with BoundingVolumes
and expecting it to trickle down to the children. For example, children and expecting it to trickle down to the children. For example, children
with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing
a collision check at the parent level then has to do a BoundingSphere to BoundingBox a collision check at the parent level then has to do a BoundingSphere to BoundingBox
collision which isn't resolved. (Having to come up with a collision point in that collision which isn't resolved. (Having to come up with a collision point in that
case is tricky and the first sign that this is the wrong approach.) case is tricky and the first sign that this is the wrong approach.)
Second, the rippling changes this caused to 'optimize' collideWith() for this Second, the rippling changes this caused to 'optimize' collideWith() for this
special use-case are another sign that this approach was a bit dodgy. The whole special use-case are another sign that this approach was a bit dodgy. The whole
idea of calculating a full collision just to see if the two shapes collide at all idea of calculating a full collision just to see if the two shapes collide at all
is very wasteful. is very wasteful.
A proper implementation should support a simpler boolean check that doesn't do A proper implementation should support a simpler boolean check that doesn't do
all of that calculation. For example, if 'other' is also a BoundingVolume (ie: 99.9% all of that calculation. For example, if 'other' is also a BoundingVolume (ie: 99.9%
of all non-Ray cases) then a direct BV to BV intersects() test can be done. So much of all non-Ray cases) then a direct BV to BV intersects() test can be done. So much
faster. And if 'other' _is_ a Ray then the BV.intersects(Ray) call can be done. faster. And if 'other' _is_ a Ray then the BV.intersects(Ray) call can be done.
I don't have time to do it right now but I'll at least un-break a bunch of peoples' I don't have time to do it right now but I'll at least un-break a bunch of peoples'
code until it can be 'optimized' properly. Hopefully it's not too late to back out code until it can be 'optimized' properly. Hopefully it's not too late to back out
the other dodgy ripples this caused. -pspeed (hindsight-expert ;)) the other dodgy ripples this caused. -pspeed (hindsight-expert ;))
Note: the code itself is relatively simple to implement but I don't have time to Note: the code itself is relatively simple to implement but I don't have time to
a) test it, and b) see if '> 4' is still a decent check for it. Could be it's fast a) test it, and b) see if '> 4' is still a decent check for it. Could be it's fast
enough to do all the time for > 1. enough to do all the time for > 1.
if (children.size() > 4) if (children.size() > 4)
{ {
BoundingVolume bv = this.getWorldBound(); BoundingVolume bv = this.getWorldBound();
@ -692,7 +690,6 @@ public class Node extends Spatial {
// Reset the fields of the clone that should be in a 'new' state. // Reset the fields of the clone that should be in a 'new' state.
nodeClone.updateList = null; nodeClone.updateList = null;
nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone() nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone()
return nodeClone; return nodeClone;
} }
@ -732,7 +729,6 @@ public class Node extends Spatial {
// cloning this list is fine. // cloning this list is fine.
this.updateList = cloner.clone(updateList); this.updateList = cloner.clone(updateList);
} }
@Override @Override
public void write(JmeExporter e) throws IOException { public void write(JmeExporter e) throws IOException {
super.write(e); super.write(e);
@ -744,7 +740,6 @@ public class Node extends Spatial {
// XXX: Load children before loading itself!! // XXX: Load children before loading itself!!
// This prevents empty children list if controls query // This prevents empty children list if controls query
// it in Control.setSpatial(). // it in Control.setSpatial().
children = new SafeArrayList( Spatial.class, children = new SafeArrayList( Spatial.class,
e.getCapsule(this).readSavableArrayList("children", null) ); e.getCapsule(this).readSavableArrayList("children", null) );
@ -754,7 +749,6 @@ public class Node extends Spatial {
child.parent = this; child.parent = this;
} }
} }
super.read(e); super.read(e);
} }
@ -775,7 +769,6 @@ public class Node extends Spatial {
} }
} }
} }
@Override @Override
public void depthFirstTraversal(SceneGraphVisitor visitor) { public void depthFirstTraversal(SceneGraphVisitor visitor) {
for (Spatial child : children.getArray()) { for (Spatial child : children.getArray()) {
@ -783,7 +776,6 @@ public class Node extends Spatial {
} }
visitor.visit(this); visitor.visit(this);
} }
@Override @Override
protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) { protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
queue.addAll(children); queue.addAll(children);

@ -38,6 +38,7 @@ import com.jme3.collision.Collidable;
import com.jme3.export.*; import com.jme3.export.*;
import com.jme3.light.Light; import com.jme3.light.Light;
import com.jme3.light.LightList; import com.jme3.light.LightList;
import com.jme3.material.MatParamOverride;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.*; import com.jme3.math.*;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
@ -121,9 +122,10 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*/ */
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
RF_BOUND = 0x02, RF_BOUND = 0x02,
RF_LIGHTLIST = 0x04, // changes in light lists RF_LIGHTLIST = 0x04, // changes in light lists
RF_CHILD_LIGHTLIST = 0x08; // some child need geometry update RF_CHILD_LIGHTLIST = 0x08, // some child need geometry update
RF_MATPARAM_OVERRIDE = 0x10;
protected CullHint cullHint = CullHint.Inherit; protected CullHint cullHint = CullHint.Inherit;
protected BatchHint batchHint = BatchHint.Inherit; protected BatchHint batchHint = BatchHint.Inherit;
/** /**
@ -135,7 +137,11 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*/ */
protected LightList localLights; protected LightList localLights;
protected transient LightList worldLights; protected transient LightList worldLights;
/**
protected ArrayList<MatParamOverride> localOverrides;
protected ArrayList<MatParamOverride> worldOverrides;
/**
* This spatial's name. * This spatial's name.
*/ */
protected String name; protected String name;
@ -195,13 +201,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*/ */
protected Spatial(String name) { protected Spatial(String name) {
this.name = name; this.name = name;
localTransform = new Transform(); localTransform = new Transform();
worldTransform = new Transform(); worldTransform = new Transform();
localLights = new LightList(this); localLights = new LightList(this);
worldLights = new LightList(this); worldLights = new LightList(this);
localOverrides = new ArrayList<>();
worldOverrides = new ArrayList<>();
refreshFlags |= RF_BOUND; refreshFlags |= RF_BOUND;
} }
@ -222,7 +229,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
boolean requiresUpdates() { boolean requiresUpdates() {
return requiresUpdates | !controls.isEmpty(); return requiresUpdates | !controls.isEmpty();
} }
/** /**
* Subclasses can call this with true to denote that they require * Subclasses can call this with true to denote that they require
* updateLogicalState() to be called even if they contain no controls. * updateLogicalState() to be called even if they contain no controls.
@ -272,35 +278,33 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
protected void setLightListRefresh() { protected void setLightListRefresh() {
refreshFlags |= RF_LIGHTLIST; refreshFlags |= RF_LIGHTLIST;
// Make sure next updateGeometricState() visits this branch // Make sure next updateGeometricState() visits this branch
// to update lights. // to update lights.
Spatial p = parent; Spatial p = parent;
while (p != null) { while (p != null) {
//if (p.refreshFlags != 0) {
// any refresh flag is sufficient,
// as each propagates to the root Node
// 2015/2/8:
// This is not true, because using e.g. getWorldBound()
// or getWorldTransform() activates a "partial refresh"
// which does not update the lights but does clear
// the refresh flags on the ancestors!
// return;
//}
if ((p.refreshFlags & RF_CHILD_LIGHTLIST) != 0) { if ((p.refreshFlags & RF_CHILD_LIGHTLIST) != 0) {
// The parent already has this flag, // The parent already has this flag,
// so must all ancestors. // so must all ancestors.
return; return;
} }
p.refreshFlags |= RF_CHILD_LIGHTLIST; p.refreshFlags |= RF_CHILD_LIGHTLIST;
p = p.parent; p = p.parent;
} }
} }
protected void setMatParamOverrideRefresh() {
refreshFlags |= RF_MATPARAM_OVERRIDE;
Spatial p = parent;
while (p != null) {
if ((p.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
return;
}
p.refreshFlags |= RF_MATPARAM_OVERRIDE;
p = p.parent;
}
}
/** /**
* Indicate that the bounding of this spatial has changed and that * Indicate that the bounding of this spatial has changed and that
* a refresh is required. * a refresh is required.
@ -318,7 +322,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
p = p.parent; p = p.parent;
} }
} }
/** /**
* (Internal use only) Forces a refresh of the given types of data. * (Internal use only) Forces a refresh of the given types of data.
* *
@ -424,6 +427,29 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
return worldLights; return worldLights;
} }
/**
* Get the local material parameter overrides.
*
* @return The list of local material parameter overrides.
*/
public List<MatParamOverride> getLocalMatParamOverrides() {
return localOverrides;
}
/**
* Get the world material parameter overrides.
*
* Note that this list is only updated on a call to
* {@link #updateGeometricState()}. After update, the world overrides list
* will contain the {@link #getParent() parent's} world overrides combined
* with this spatial's {@link #getLocalMatParamOverrides() local overrides}.
*
* @return The list of world material parameter overrides.
*/
public List<MatParamOverride> getWorldMatParamOverrides() {
return worldOverrides;
}
/** /**
* <code>getWorldRotation</code> retrieves the absolute rotation of the * <code>getWorldRotation</code> retrieves the absolute rotation of the
* Spatial. * Spatial.
@ -525,10 +551,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
TempVars vars = TempVars.get(); TempVars vars = TempVars.get();
Vector3f compVecA = vars.vect4; Vector3f compVecA = vars.vect4;
compVecA.set(position).subtractLocal(worldTranslation); compVecA.set(position).subtractLocal(worldTranslation);
getLocalRotation().lookAt(compVecA, upVector); getLocalRotation().lookAt(compVecA, upVector);
if ( getParent() != null ) { if ( getParent() != null ) {
Quaternion rot=vars.quat1; Quaternion rot=vars.quat1;
rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation()); rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation());
@ -555,15 +579,63 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
worldLights.update(localLights, null); worldLights.update(localLights, null);
refreshFlags &= ~RF_LIGHTLIST; refreshFlags &= ~RF_LIGHTLIST;
} else { } else {
if ((parent.refreshFlags & RF_LIGHTLIST) == 0) { assert (parent.refreshFlags & RF_LIGHTLIST) == 0;
worldLights.update(localLights, parent.worldLights); worldLights.update(localLights, parent.worldLights);
refreshFlags &= ~RF_LIGHTLIST; refreshFlags &= ~RF_LIGHTLIST;
} else { }
assert false; }
}
protected void updateMatParamOverrides() {
refreshFlags &= ~RF_MATPARAM_OVERRIDE;
worldOverrides.clear();
if (parent == null) {
worldOverrides.addAll(localOverrides);
} else {
assert (parent.refreshFlags & RF_MATPARAM_OVERRIDE) == 0;
worldOverrides.addAll(parent.worldOverrides);
worldOverrides.addAll(localOverrides);
}
}
/**
* Adds a local material parameter override.
*
* @param override The override to add.
* @see MatParamOverride
*/
public void addMatParamOverride(MatParamOverride override) {
if (override == null) {
throw new IllegalArgumentException("override cannot be null");
}
localOverrides.add(override);
setMatParamOverrideRefresh();
}
/**
* Remove a local material parameter override if it exists.
*
* @param override The override to remove.
* @see MatParamOverride
*/
public void removeMatParamOverride(MatParamOverride override) {
if (localOverrides.remove(override)) {
setMatParamOverrideRefresh();
} }
} }
/**
* Remove all local material parameter overrides.
*
* @see #addMatParamOverride(com.jme3.material.MatParamOverride)
*/
public void clearMatParamOverrides() {
if (!localOverrides.isEmpty()) {
setMatParamOverrideRefresh();
}
localOverrides.clear();
}
/** /**
* Should only be called from updateGeometricState(). * Should only be called from updateGeometricState().
* In most cases should not be subclassed. * In most cases should not be subclassed.
@ -696,7 +768,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
controls.add(control); controls.add(control);
control.setSpatial(this); control.setSpatial(this);
boolean after = requiresUpdates(); boolean after = requiresUpdates();
// If the requirement to be updated has changed // If the requirement to be updated has changed
// then we need to let the parent node know so it // then we need to let the parent node know so it
// can rebuild its update list. // can rebuild its update list.
@ -720,7 +791,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
} }
} }
boolean after = requiresUpdates(); boolean after = requiresUpdates();
// If the requirement to be updated has changed // If the requirement to be updated has changed
// then we need to let the parent node know so it // then we need to let the parent node know so it
// can rebuild its update list. // can rebuild its update list.
@ -746,14 +816,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
} }
boolean after = requiresUpdates(); boolean after = requiresUpdates();
// If the requirement to be updated has changed // If the requirement to be updated has changed
// then we need to let the parent node know so it // then we need to let the parent node know so it
// can rebuild its update list. // can rebuild its update list.
if( parent != null && before != after ) { if( parent != null && before != after ) {
parent.invalidateUpdateList(); parent.invalidateUpdateList();
} }
return result; return result;
} }
@ -838,7 +906,10 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
if ((refreshFlags & RF_BOUND) != 0) { if ((refreshFlags & RF_BOUND) != 0) {
updateWorldBound(); updateWorldBound();
} }
if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
updateMatParamOverrides();
}
assert refreshFlags == 0; assert refreshFlags == 0;
} }
@ -1292,6 +1363,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
// the transforms and stuff get refreshed. // the transforms and stuff get refreshed.
clone.setTransformRefresh(); clone.setTransformRefresh();
clone.setLightListRefresh(); clone.setLightListRefresh();
clone.setMatParamOverrideRefresh();
return clone; return clone;
} }
@ -1312,6 +1384,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
clone.localLights.setOwner(clone); clone.localLights.setOwner(clone);
clone.worldLights.setOwner(clone); clone.worldLights.setOwner(clone);
clone.worldOverrides = new ArrayList<MatParamOverride>();
clone.localOverrides = new ArrayList<MatParamOverride>();
for (MatParamOverride override : localOverrides) {
clone.localOverrides.add((MatParamOverride) override.clone());
}
// No need to force cloned to update. // No need to force cloned to update.
// This node already has the refresh flags // This node already has the refresh flags
// set below so it will have to update anyway. // set below so it will have to update anyway.
@ -1333,6 +1412,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
clone.setBoundRefresh(); clone.setBoundRefresh();
clone.setTransformRefresh(); clone.setTransformRefresh();
clone.setLightListRefresh(); clone.setLightListRefresh();
clone.setMatParamOverrideRefresh();
clone.controls = new SafeArrayList<Control>(Control.class); clone.controls = new SafeArrayList<Control>(Control.class);
for (int i = 0; i < controls.size(); i++) { for (int i = 0; i < controls.size(); i++) {
@ -1388,6 +1468,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
// the transforms and stuff get refreshed. // the transforms and stuff get refreshed.
clone.setTransformRefresh(); clone.setTransformRefresh();
clone.setLightListRefresh(); clone.setLightListRefresh();
clone.setMatParamOverrideRefresh();
return clone; return clone;
} }
@ -1419,6 +1500,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
this.localLights = cloner.clone(localLights); this.localLights = cloner.clone(localLights);
this.worldTransform = cloner.clone(worldTransform); this.worldTransform = cloner.clone(worldTransform);
this.localTransform = cloner.clone(localTransform); this.localTransform = cloner.clone(localTransform);
this.worldOverrides = cloner.clone(worldOverrides);
this.localOverrides = cloner.clone(localOverrides);
this.controls = cloner.clone(controls); this.controls = cloner.clone(controls);
// Cloner doesn't handle maps on its own just yet. // Cloner doesn't handle maps on its own just yet.
@ -1515,6 +1598,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit); capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
capsule.write(localTransform, "transform", Transform.IDENTITY); capsule.write(localTransform, "transform", Transform.IDENTITY);
capsule.write(localLights, "lights", null); capsule.write(localLights, "lights", null);
capsule.writeSavableArrayList(localOverrides, "overrides", null);
// Shallow clone the controls array to convert its type. // Shallow clone the controls array to convert its type.
capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null); capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null);
@ -1538,6 +1622,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
localLights = (LightList) ic.readSavable("lights", null); localLights = (LightList) ic.readSavable("lights", null);
localLights.setOwner(this); localLights.setOwner(this);
localOverrides = ic.readSavableArrayList("overrides", null);
if (localOverrides == null) {
localOverrides = new ArrayList<>();
}
worldOverrides = new ArrayList<>();
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split //changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
//the AnimControl creates the SkeletonControl for old files and add it to the spatial. //the AnimControl creates the SkeletonControl for old files and add it to the spatial.
//The SkeletonControl must be the last in the stack so we add the list of all other control before it. //The SkeletonControl must be the last in the stack so we add the list of all other control before it.

@ -108,8 +108,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
* Do not use. * Do not use.
*/ */
@Deprecated @Deprecated
MiscAttrib, Reserved0,
/** /**
* Specifies the index buffer, must contain integer data * Specifies the index buffer, must contain integer data
* (ubyte, ushort, or uint). * (ubyte, ushort, or uint).
@ -522,6 +521,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
// throw new UnsupportedOperationException("Data has already been sent. Cannot set usage."); // throw new UnsupportedOperationException("Data has already been sent. Cannot set usage.");
this.usage = usage; this.usage = usage;
this.setUpdateNeeded();
} }
/** /**

@ -101,14 +101,6 @@ public class WireBox extends Mesh {
); );
updateBound(); updateBound();
} }
/**
* Old method retained for compatibility: use makeGeometry instead.
*/
@Deprecated
public void fromBoundingBox(BoundingBox bbox) {
updatePositions(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent());
}
/** /**
* Create a geometry suitable for visualizing the specified bounding box. * Create a geometry suitable for visualizing the specified bounding box.

@ -1,286 +1,179 @@
/* /*
* Copyright (c) 2009-2012 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
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
* met: * met:
* *
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* *
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software * may be used to endorse or promote products derived from this software
* without specific prior written permission. * without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.shader; package com.jme3.shader;
import com.jme3.export.*; import java.util.Arrays;
import com.jme3.material.MatParam; import java.util.List;
import com.jme3.material.TechniqueDef;
import com.jme3.util.ListMap; /**
* The new define list.
import java.io.IOException; *
import java.util.Map; * @author Kirill Vainer
import java.util.TreeMap; */
public final class DefineList {
public final class DefineList implements Savable, Cloneable {
public static final int MAX_DEFINES = 64;
private static final String ONE = "1";
private long hash;
private TreeMap<String, String> defines = new TreeMap<String, String>(); private final int[] vals;
private String compiled = null;
private int cachedHashCode = 0; public DefineList(int numValues) {
if (numValues < 0 || numValues > MAX_DEFINES) {
public void write(JmeExporter ex) throws IOException{ throw new IllegalArgumentException("numValues must be between 0 and 64");
OutputCapsule oc = ex.getCapsule(this); }
vals = new int[numValues];
String[] keys = new String[defines.size()]; }
String[] vals = new String[defines.size()];
private DefineList(DefineList original) {
int i = 0; this.hash = original.hash;
for (Map.Entry<String, String> define : defines.entrySet()){ this.vals = new int[original.vals.length];
keys[i] = define.getKey(); System.arraycopy(original.vals, 0, vals, 0, vals.length);
vals[i] = define.getValue(); }
i++;
} public void set(int id, int val) {
assert 0 <= id && id < 64;
oc.write(keys, "keys", null); if (val != 0) {
oc.write(vals, "vals", null); hash |= (1L << id);
} } else {
hash &= ~(1L << id);
public void read(JmeImporter im) throws IOException{ }
InputCapsule ic = im.getCapsule(this); vals[id] = val;
}
String[] keys = ic.readStringArray("keys", null);
String[] vals = ic.readStringArray("vals", null); public void set(int id, float val) {
for (int i = 0; i < keys.length; i++){ set(id, Float.floatToIntBits(val));
defines.put(keys[i], vals[i]); }
}
} public void set(int id, boolean val) {
set(id, val ? 1 : 0);
public void clear() { }
defines.clear();
compiled = ""; public void set(int id, VarType type, Object value) {
cachedHashCode = 0; if (value == null) {
} set(id, 0);
return;
public String get(String key){ }
return defines.get(key);
} switch (type) {
case Int:
@Override set(id, (Integer) value);
public DefineList clone() { break;
try { case Float:
DefineList clone = (DefineList) super.clone(); set(id, (Float) value);
clone.cachedHashCode = 0; break;
clone.compiled = null; case Boolean:
clone.defines = (TreeMap<String, String>) defines.clone(); set(id, ((Boolean) value));
return clone; break;
} catch (CloneNotSupportedException ex) { default:
throw new AssertionError(); set(id, 1);
} break;
} }
}
public boolean set(String key, VarType type, Object val){
if (val == null){ public void setAll(DefineList other) {
defines.remove(key); for (int i = 0; i < other.vals.length; i++) {
compiled = null; if (other.vals[i] != 0) {
cachedHashCode = 0; vals[i] = other.vals[i];
return true; }
} }
}
switch (type){
case Boolean: public void clear() {
if (((Boolean) val).booleanValue()) { hash = 0;
// same literal, != will work Arrays.fill(vals, 0);
if (defines.put(key, ONE) != ONE) { }
compiled = null;
cachedHashCode = 0; public boolean getBoolean(int id) {
return true; return vals[id] != 0;
} }
} else if (defines.containsKey(key)) {
defines.remove(key); public float getFloat(int id) {
compiled = null; return Float.intBitsToFloat(vals[id]);
cachedHashCode = 0; }
return true;
} public int getInt(int id) {
return vals[id];
break; }
case Float:
case Int: @Override
String newValue = val.toString(); public int hashCode() {
String original = defines.put(key, newValue); return (int)((hash >> 32) ^ hash);
if (!val.equals(original)) { }
compiled = null;
cachedHashCode = 0; @Override
return true; public boolean equals(Object other) {
} DefineList o = (DefineList) other;
break; if (hash == o.hash) {
default: for (int i = 0; i < vals.length; i++) {
// same literal, != will work if (vals[i] != o.vals[i]) return false;
if (defines.put(key, ONE) != ONE) { }
compiled = null; return true;
cachedHashCode = 0; }
return true; return false;
} }
break;
} public DefineList deepClone() {
return new DefineList(this);
return false; }
}
public void generateSource(StringBuilder sb, List<String> defineNames, List<VarType> defineTypes) {
public boolean remove(String key){ for (int i = 0; i < vals.length; i++) {
if (defines.remove(key) != null) { if (vals[i] != 0) {
compiled = null; String defineName = defineNames.get(i);
cachedHashCode = 0;
return true; sb.append("#define ");
} sb.append(defineName);
return false; sb.append(" ");
}
if (defineTypes != null && defineTypes.get(i) == VarType.Float) {
public void addFrom(DefineList other){ float val = Float.intBitsToFloat(vals[i]);
if (other == null) { if (Float.isInfinite(val) || Float.isNaN(val)) {
return; throw new IllegalArgumentException(
} "GLSL does not support NaN "
compiled = null; + "or Infinite float literals");
cachedHashCode = 0; }
defines.putAll(other.defines); sb.append(val);
} } else {
sb.append(vals[i]);
public String getCompiled(){ }
if (compiled == null){
StringBuilder sb = new StringBuilder(); sb.append("\n");
for (Map.Entry<String, String> entry : defines.entrySet()){ }
sb.append("#define ").append(entry.getKey()).append(" "); }
sb.append(entry.getValue()).append('\n'); }
}
compiled = sb.toString(); public String generateSource(List<String> defineNames, List<VarType> defineTypes) {
} StringBuilder sb = new StringBuilder();
return compiled; generateSource(sb, defineNames, defineTypes);
} return sb.toString();
}
@Override }
public boolean equals(Object obj) {
final DefineList other = (DefineList) obj;
return defines.equals(other.defines);
}
/**
* Update defines if the define list changed based on material parameters.
* @param params
* @param def
* @return true if defines was updated
*/
public boolean update(ListMap params, TechniqueDef def){
if(equalsParams(params, def)){
return false;
}
// Defines were changed, update define list
clear();
for(int i=0;i<params.size();i++) {
MatParam param = (MatParam)params.getValue(i);
String defineName = def.getShaderParamDefine(param.getName());
if (defineName != null) {
set(defineName, param.getVarType(), param.getValue());
}
}
return true;
}
private boolean equalsParams(ListMap params, TechniqueDef def) {
int size = 0;
for(int i = 0; i < params.size() ; i++ ) {
MatParam param = (MatParam)params.getValue(i);
String key = def.getShaderParamDefine(param.getName());
if (key != null) {
Object val = param.getValue();
if (val != null) {
switch (param.getVarType()) {
case Boolean: {
String current = defines.get(key);
if (((Boolean) val).booleanValue()) {
if (current == null || current != ONE) {
return false;
}
size++;
} else {
if (current != null) {
return false;
}
}
}
break;
case Float:
case Int: {
String newValue = val.toString();
String current = defines.get(key);
if (!newValue.equals(current)) {
return false;
}
size++;
}
break;
default: {
if (!defines.containsKey(key)) {
return false;
}
size++;
}
break;
}
}
}
}
if (size != defines.size()) {
return false;
}
return true;
}
@Override
public int hashCode() {
if (cachedHashCode == 0) {
cachedHashCode = defines.hashCode();
}
return cachedHashCode;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
int i = 0;
for (Map.Entry<String, String> entry : defines.entrySet()) {
sb.append(entry.getKey()).append("=").append(entry.getValue());
if (i != defines.size() - 1) {
sb.append(", ");
}
i++;
}
return sb.toString();
}
}

@ -45,44 +45,63 @@ public final class Shader extends NativeObject {
/** /**
* A list of all shader sources currently attached. * A list of all shader sources currently attached.
*/ */
private ArrayList<ShaderSource> shaderSourceList; private final ArrayList<ShaderSource> shaderSourceList;
/** /**
* Maps uniform name to the uniform variable. * Maps uniform name to the uniform variable.
*/ */
private ListMap<String, Uniform> uniforms; private final ListMap<String, Uniform> uniforms;
/**
* Uniforms bound to {@link UniformBinding}s.
*
* Managed by the {@link UniformBindingManager}.
*/
private final ArrayList<Uniform> boundUniforms;
/** /**
* Maps attribute name to the location of the attribute in the shader. * Maps attribute name to the location of the attribute in the shader.
*/ */
private IntMap<Attribute> attribs; private final IntMap<Attribute> attribs;
/** /**
* Type of shader. The shader will control the pipeline of it's type. * Type of shader. The shader will control the pipeline of it's type.
*/ */
public static enum ShaderType { public static enum ShaderType {
/** /**
* Control fragment rasterization. (e.g color of pixel). * Control fragment rasterization. (e.g color of pixel).
*/ */
Fragment, Fragment("frag"),
/** /**
* Control vertex processing. (e.g transform of model to clip space) * Control vertex processing. (e.g transform of model to clip space)
*/ */
Vertex, Vertex("vert"),
/** /**
* Control geometry assembly. (e.g compile a triangle list from input data) * Control geometry assembly. (e.g compile a triangle list from input
* data)
*/ */
Geometry, Geometry("geom"),
/** /**
* Controls tesselation factor (e.g how often a input patch should be subdivided) * Controls tesselation factor (e.g how often a input patch should be
* subdivided)
*/ */
TessellationControl, TessellationControl("tsctrl"),
/** /**
* Controls tesselation transform (e.g similar to the vertex shader, but required to mix inputs manual) * Controls tesselation transform (e.g similar to the vertex shader, but
* required to mix inputs manual)
*/ */
TessellationEvaluation; TessellationEvaluation("tseval");
private String extension;
public String getExtension() {
return extension;
}
private ShaderType(String extension) {
this.extension = extension;
}
} }
/** /**
@ -195,22 +214,16 @@ public final class Shader extends NativeObject {
} }
} }
/**
* Initializes the shader for use, must be called after the
* constructor without arguments is used.
*/
public void initialize() {
shaderSourceList = new ArrayList<ShaderSource>();
uniforms = new ListMap<String, Uniform>();
attribs = new IntMap<Attribute>();
}
/** /**
* Creates a new shader, {@link #initialize() } must be called * Creates a new shader, {@link #initialize() } must be called
* after this constructor for the shader to be usable. * after this constructor for the shader to be usable.
*/ */
public Shader(){ public Shader(){
super(); super();
shaderSourceList = new ArrayList<ShaderSource>();
uniforms = new ListMap<String, Uniform>();
attribs = new IntMap<Attribute>();
boundUniforms = new ArrayList<Uniform>();
} }
/** /**
@ -225,6 +238,10 @@ public final class Shader extends NativeObject {
for (ShaderSource source : s.shaderSourceList){ for (ShaderSource source : s.shaderSourceList){
shaderSourceList.add( (ShaderSource)source.createDestructableClone() ); shaderSourceList.add( (ShaderSource)source.createDestructableClone() );
} }
uniforms = null;
boundUniforms = null;
attribs = null;
} }
/** /**
@ -248,6 +265,18 @@ public final class Shader extends NativeObject {
setUpdateNeeded(); setUpdateNeeded();
} }
public void addUniformBinding(UniformBinding binding){
String uniformName = "g_" + binding.name();
Uniform uniform = uniforms.get(uniformName);
if (uniform == null) {
uniform = new Uniform();
uniform.name = uniformName;
uniform.binding = binding;
uniforms.put(uniformName, uniform);
boundUniforms.add(uniform);
}
}
public Uniform getUniform(String name){ public Uniform getUniform(String name){
assert name.startsWith("m_") || name.startsWith("g_"); assert name.startsWith("m_") || name.startsWith("g_");
Uniform uniform = uniforms.get(name); Uniform uniform = uniforms.get(name);
@ -277,6 +306,10 @@ public final class Shader extends NativeObject {
public ListMap<String, Uniform> getUniformMap(){ public ListMap<String, Uniform> getUniformMap(){
return uniforms; return uniforms;
} }
public ArrayList<Uniform> getBoundUniforms() {
return boundUniforms;
}
public Collection<ShaderSource> getSources(){ public Collection<ShaderSource> getSources(){
return shaderSourceList; return shaderSourceList;

@ -57,9 +57,9 @@ public abstract class ShaderGenerator {
*/ */
protected int indent; protected int indent;
/** /**
* the technique to use for the shader generation * the technique def to use for the shader generation
*/ */
protected Technique technique = null; protected TechniqueDef techniqueDef = null;
/** /**
* Build a shaderGenerator * Build a shaderGenerator
@ -70,8 +70,8 @@ public abstract class ShaderGenerator {
this.assetManager = assetManager; this.assetManager = assetManager;
} }
public void initialize(Technique technique){ public void initialize(TechniqueDef techniqueDef){
this.technique = technique; this.techniqueDef = techniqueDef;
} }
/** /**
@ -79,24 +79,29 @@ public abstract class ShaderGenerator {
* *
* @return a Shader program * @return a Shader program
*/ */
public Shader generateShader() { public Shader generateShader(String definesSourceCode) {
if(technique == null){ if (techniqueDef == null) {
throw new UnsupportedOperationException("The shaderGenerator was not properly initialized, call initialize(Technique) before any generation"); throw new UnsupportedOperationException("The shaderGenerator was not "
+ "properly initialized, call "
+ "initialize(TechniqueDef) before any generation");
} }
DefineList defines = technique.getAllDefines(); String techniqueName = techniqueDef.getName();
TechniqueDef def = technique.getDef(); ShaderGenerationInfo info = techniqueDef.getShaderGenerationInfo();
ShaderGenerationInfo info = def.getShaderGenerationInfo();
String vertexSource = buildShader(def.getShaderNodes(), info, ShaderType.Vertex);
String fragmentSource = buildShader(def.getShaderNodes(), info, ShaderType.Fragment);
Shader shader = new Shader(); Shader shader = new Shader();
shader.initialize(); for (ShaderType type : ShaderType.values()) {
shader.addSource(Shader.ShaderType.Vertex, technique.getDef().getName() + ".vert", vertexSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Vertex)); String extension = type.getExtension();
shader.addSource(Shader.ShaderType.Fragment, technique.getDef().getName() + ".frag", fragmentSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Fragment)); String language = getLanguageAndVersion(type);
String shaderSourceCode = buildShader(techniqueDef.getShaderNodes(), info, type);
if (shaderSourceCode != null) {
String shaderSourceAssetName = techniqueName + "." + extension;
shader.addSource(type, shaderSourceAssetName, shaderSourceCode, definesSourceCode, language);
}
}
technique = null; techniqueDef = null;
return shader; return shader;
} }
@ -109,6 +114,14 @@ public abstract class ShaderGenerator {
* @return the code of the generated vertex shader * @return the code of the generated vertex shader
*/ */
protected String buildShader(List<ShaderNode> shaderNodes, ShaderGenerationInfo info, ShaderType type) { protected String buildShader(List<ShaderNode> shaderNodes, ShaderGenerationInfo info, ShaderType type) {
if (type == ShaderType.TessellationControl ||
type == ShaderType.TessellationEvaluation ||
type == ShaderType.Geometry) {
// TODO: Those are not supported.
// Too much code assumes that type is either Vertex or Fragment
return null;
}
indent = 0; indent = 0;
StringBuilder sourceDeclaration = new StringBuilder(); StringBuilder sourceDeclaration = new StringBuilder();

@ -1,201 +0,0 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.shader;
import com.jme3.asset.AssetKey;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import java.io.IOException;
import java.util.EnumMap;
import java.util.Set;
public class ShaderKey extends AssetKey<Shader> {
protected EnumMap<Shader.ShaderType,String> shaderLanguage;
protected EnumMap<Shader.ShaderType,String> shaderName;
protected DefineList defines;
protected int cachedHashedCode = 0;
protected boolean usesShaderNodes = false;
public ShaderKey(){
shaderLanguage=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
shaderName=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
}
public ShaderKey(DefineList defines, EnumMap<Shader.ShaderType,String> shaderLanguage,EnumMap<Shader.ShaderType,String> shaderName){
super("");
this.name = reducePath(getShaderName(Shader.ShaderType.Vertex));
this.shaderLanguage=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
this.shaderName=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
this.defines = defines;
for (Shader.ShaderType shaderType : shaderName.keySet()) {
this.shaderName.put(shaderType,shaderName.get(shaderType));
this.shaderLanguage.put(shaderType,shaderLanguage.get(shaderType));
}
}
@Override
public ShaderKey clone() {
ShaderKey clone = (ShaderKey) super.clone();
clone.cachedHashedCode = 0;
clone.defines = defines.clone();
return clone;
}
@Override
public String toString(){
//todo:
return "V="+name+";";
}
private final String getShaderName(Shader.ShaderType type) {
if (shaderName == null) {
return "";
}
String shName = shaderName.get(type);
return shName != null ? shName : "";
}
//todo: make equals and hashCode work
@Override
public boolean equals(Object obj) {
final ShaderKey other = (ShaderKey) obj;
if (name.equals(other.name) && getShaderName(Shader.ShaderType.Fragment).equals(other.getShaderName(Shader.ShaderType.Fragment))){
if (defines != null && other.defines != null) {
return defines.equals(other.defines);
} else if (defines != null || other.defines != null) {
return false;
} else {
return true;
}
}
return false;
}
@Override
public int hashCode() {
if (cachedHashedCode == 0) {
int hash = 7;
hash = 41 * hash + name.hashCode();
hash = 41 * hash + getShaderName(Shader.ShaderType.Fragment).hashCode();
hash = getShaderName(Shader.ShaderType.Geometry) == null ? hash : 41 * hash + getShaderName(Shader.ShaderType.Geometry).hashCode();
hash = getShaderName(Shader.ShaderType.TessellationControl) == null ? hash : 41 * hash + getShaderName(Shader.ShaderType.TessellationControl).hashCode();
hash = getShaderName(Shader.ShaderType.TessellationEvaluation) == null ? hash : 41 * hash + getShaderName(Shader.ShaderType.TessellationEvaluation).hashCode();
hash = 41 * hash + (defines != null ? defines.hashCode() : 0);
cachedHashedCode = hash;
}
return cachedHashedCode;
}
public DefineList getDefines() {
return defines;
}
public String getVertName(){
return getShaderName(Shader.ShaderType.Vertex);
}
public String getFragName() {
return getShaderName(Shader.ShaderType.Fragment);
}
/**
* @deprecated Use {@link #getVertexShaderLanguage() } instead.
*/
@Deprecated
public String getLanguage() {
return shaderLanguage.get(Shader.ShaderType.Vertex);
}
public String getVertexShaderLanguage() {
return shaderLanguage.get(Shader.ShaderType.Vertex);
}
public String getFragmentShaderLanguage() {
return shaderLanguage.get(Shader.ShaderType.Vertex);
}
public boolean isUsesShaderNodes() {
return usesShaderNodes;
}
public void setUsesShaderNodes(boolean usesShaderNodes) {
this.usesShaderNodes = usesShaderNodes;
}
public Set<Shader.ShaderType> getUsedShaderPrograms(){
return shaderName.keySet();
}
public String getShaderProgramLanguage(Shader.ShaderType shaderType){
return shaderLanguage.get(shaderType);
}
public String getShaderProgramName(Shader.ShaderType shaderType){
return getShaderName(shaderType);
}
@Override
public void write(JmeExporter ex) throws IOException{
super.write(ex);
OutputCapsule oc = ex.getCapsule(this);
oc.write(shaderName.get(Shader.ShaderType.Fragment), "fragment_name", null);
oc.write(shaderName.get(Shader.ShaderType.Geometry), "geometry_name", null);
oc.write(shaderName.get(Shader.ShaderType.TessellationControl), "tessControl_name", null);
oc.write(shaderName.get(Shader.ShaderType.TessellationEvaluation), "tessEval_name", null);
oc.write(shaderLanguage.get(Shader.ShaderType.Vertex), "language", null);
oc.write(shaderLanguage.get(Shader.ShaderType.Fragment), "frag_language", null);
oc.write(shaderLanguage.get(Shader.ShaderType.Geometry), "geom_language", null);
oc.write(shaderLanguage.get(Shader.ShaderType.TessellationControl), "tsctrl_language", null);
oc.write(shaderLanguage.get(Shader.ShaderType.TessellationEvaluation), "tseval_language", null);
}
@Override
public void read(JmeImporter im) throws IOException{
super.read(im);
InputCapsule ic = im.getCapsule(this);
shaderName.put(Shader.ShaderType.Vertex,name);
shaderName.put(Shader.ShaderType.Fragment,ic.readString("fragment_name", null));
shaderName.put(Shader.ShaderType.Geometry,ic.readString("geometry_name", null));
shaderName.put(Shader.ShaderType.TessellationControl,ic.readString("tessControl_name", null));
shaderName.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tessEval_name", null));
shaderLanguage.put(Shader.ShaderType.Vertex,ic.readString("language", null));
shaderLanguage.put(Shader.ShaderType.Fragment,ic.readString("frag_language", null));
shaderLanguage.put(Shader.ShaderType.Geometry,ic.readString("geom_language", null));
shaderLanguage.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrl_language", null));
shaderLanguage.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tseval_language", null));
}
}

@ -70,6 +70,30 @@ public class Uniform extends ShaderVariable {
*/ */
protected boolean setByCurrentMaterial = false; protected boolean setByCurrentMaterial = false;
@Override
public int hashCode() {
int hash = 5;
hash = 31 * hash + (this.value != null ? this.value.hashCode() : 0);
hash = 31 * hash + (this.varType != null ? this.varType.hashCode() : 0);
hash = 31 * hash + (this.binding != null ? this.binding.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
final Uniform other = (Uniform) obj;
if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
return false;
}
return this.binding == other.binding && this.varType == other.varType;
}
@Override @Override
public String toString(){ public String toString(){
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -102,6 +126,10 @@ public class Uniform extends ShaderVariable {
public Object getValue(){ public Object getValue(){
return value; return value;
} }
public FloatBuffer getMultiData() {
return multiData;
}
public boolean isSetByCurrentMaterial() { public boolean isSetByCurrentMaterial() {
return setByCurrentMaterial; return setByCurrentMaterial;
@ -111,21 +139,6 @@ public class Uniform extends ShaderVariable {
setByCurrentMaterial = false; setByCurrentMaterial = false;
} }
private static void setVector4(Vector4f vec, Object value) {
if (value instanceof ColorRGBA) {
ColorRGBA color = (ColorRGBA) value;
vec.set(color.r, color.g, color.b, color.a);
} else if (value instanceof Quaternion) {
Quaternion quat = (Quaternion) value;
vec.set(quat.getX(), quat.getY(), quat.getZ(), quat.getW());
} else if (value instanceof Vector4f) {
Vector4f vec4 = (Vector4f) value;
vec.set(vec4);
} else{
throw new IllegalArgumentException();
}
}
public void clearValue(){ public void clearValue(){
updateNeeded = true; updateNeeded = true;
@ -182,27 +195,43 @@ public class Uniform extends ShaderVariable {
} }
if (value == null) { if (value == null) {
throw new NullPointerException(); throw new IllegalArgumentException("for uniform " + name + ": value cannot be null");
} }
setByCurrentMaterial = true; setByCurrentMaterial = true;
switch (type){ switch (type){
case Matrix3: case Matrix3:
if (value.equals(this.value)) {
return;
}
Matrix3f m3 = (Matrix3f) value; Matrix3f m3 = (Matrix3f) value;
if (multiData == null) { if (multiData == null) {
multiData = BufferUtils.createFloatBuffer(9); multiData = BufferUtils.createFloatBuffer(9);
} }
m3.fillFloatBuffer(multiData, true); m3.fillFloatBuffer(multiData, true);
multiData.clear(); multiData.clear();
if (this.value == null) {
this.value = new Matrix3f(m3);
} else {
((Matrix3f)this.value).set(m3);
}
break; break;
case Matrix4: case Matrix4:
if (value.equals(this.value)) {
return;
}
Matrix4f m4 = (Matrix4f) value; Matrix4f m4 = (Matrix4f) value;
if (multiData == null) { if (multiData == null) {
multiData = BufferUtils.createFloatBuffer(16); multiData = BufferUtils.createFloatBuffer(16);
} }
m4.fillFloatBuffer(multiData, true); m4.fillFloatBuffer(multiData, true);
multiData.clear(); multiData.clear();
if (this.value == null) {
this.value = new Matrix4f(m4);
} else {
((Matrix4f)this.value).copy(m4);
}
break; break;
case IntArray: case IntArray:
int[] ia = (int[]) value; int[] ia = (int[]) value;
@ -283,11 +312,32 @@ public class Uniform extends ShaderVariable {
} }
multiData.clear(); multiData.clear();
break; break;
case Vector4:
if (value.equals(this.value)) {
return;
}
if (value instanceof ColorRGBA) {
if (this.value == null) {
this.value = new ColorRGBA();
}
((ColorRGBA) this.value).set((ColorRGBA) value);
} else if (value instanceof Vector4f) {
if (this.value == null) {
this.value = new Vector4f();
}
((Vector4f) this.value).set((Vector4f) value);
} else {
if (this.value == null) {
this.value = new Quaternion();
}
((Quaternion) this.value).set((Quaternion) value);
}
break;
// Only use check if equals optimization for primitive values // Only use check if equals optimization for primitive values
case Int: case Int:
case Float: case Float:
case Boolean: case Boolean:
if (this.value != null && this.value.equals(value)) { if (value.equals(this.value)) {
return; return;
} }
this.value = value; this.value = value;
@ -297,39 +347,38 @@ public class Uniform extends ShaderVariable {
break; break;
} }
if (multiData != null) { // if (multiData != null) {
this.value = multiData; // this.value = multiData;
} // }
varType = type; varType = type;
updateNeeded = true; updateNeeded = true;
} }
public void setVector4Length(int length){ public void setVector4Length(int length){
if (location == -1) if (location == -1) {
return; return;
FloatBuffer fb = (FloatBuffer) value;
if (fb == null || fb.capacity() < length * 4) {
value = BufferUtils.createFloatBuffer(length * 4);
} }
multiData = BufferUtils.ensureLargeEnough(multiData, length * 4);
value = multiData;
varType = VarType.Vector4Array; varType = VarType.Vector4Array;
updateNeeded = true; updateNeeded = true;
setByCurrentMaterial = true; setByCurrentMaterial = true;
} }
public void setVector4InArray(float x, float y, float z, float w, int index){ public void setVector4InArray(float x, float y, float z, float w, int index){
if (location == -1) if (location == -1) {
return; return;
}
if (varType != null && varType != VarType.Vector4Array) if (varType != null && varType != VarType.Vector4Array) {
throw new IllegalArgumentException("Expected a "+varType.name()+" value!"); throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
}
FloatBuffer fb = (FloatBuffer) value; multiData.position(index * 4);
fb.position(index * 4); multiData.put(x).put(y).put(z).put(w);
fb.put(x).put(y).put(z).put(w); multiData.rewind();
fb.rewind();
updateNeeded = true; updateNeeded = true;
setByCurrentMaterial = true; setByCurrentMaterial = true;
} }

@ -36,7 +36,7 @@ import com.jme3.math.*;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.system.Timer; import com.jme3.system.Timer;
import java.util.List; import java.util.ArrayList;
/** /**
* <code>UniformBindingManager</code> helps {@link RenderManager} to manage * <code>UniformBindingManager</code> helps {@link RenderManager} to manage
@ -84,7 +84,8 @@ public class UniformBindingManager {
* Updates the given list of uniforms with {@link UniformBinding uniform bindings} * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
* based on the current world state. * based on the current world state.
*/ */
public void updateUniformBindings(List<Uniform> params) { public void updateUniformBindings(Shader shader) {
ArrayList<Uniform> params = shader.getBoundUniforms();
for (int i = 0; i < params.size(); i++) { for (int i = 0; i < params.size(); i++) {
Uniform u = params.get(i); Uniform u = params.get(i);
switch (u.getBinding()) { switch (u.getBinding()) {

@ -330,12 +330,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
public void initialize(RenderManager rm, ViewPort vp) { public void initialize(RenderManager rm, ViewPort vp) {
renderManager = rm; renderManager = rm;
viewPort = vp; viewPort = vp;
//checking for caps to chosse the appropriate post material technique postTechniqueName = "PostShadow";
if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) {
postTechniqueName = "PostShadow15";
} else {
postTechniqueName = "PostShadow";
}
if(zFarOverride>0 && frustumCam == null){ if(zFarOverride>0 && frustumCam == null){
initFrustumCam(); initFrustumCam();
} }
@ -587,7 +582,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
for (int i = 0; i < l.size(); i++) { for (int i = 0; i < l.size(); i++) {
Material mat = l.get(i).getMaterial(); Material mat = l.get(i).getMaterial();
//checking if the material has the post technique and adding it to the material cache //checking if the material has the post technique and adding it to the material cache
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) { if (mat.getMaterialDef().getTechniqueDefs(postTechniqueName) != null) {
if (!matCache.contains(mat)) { if (!matCache.contains(mat)) {
matCache.add(mat); matCache.add(mat);
} }

@ -355,12 +355,7 @@ public class PssmShadowRenderer implements SceneProcessor {
public void initialize(RenderManager rm, ViewPort vp) { public void initialize(RenderManager rm, ViewPort vp) {
renderManager = rm; renderManager = rm;
viewPort = vp; viewPort = vp;
//checking for caps to chosse the appropriate post material technique postTechniqueName = "PostShadow";
if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) {
postTechniqueName = "PostShadow15";
} else {
postTechniqueName = "PostShadow";
}
} }
public boolean isInitialized() { public boolean isInitialized() {
@ -533,7 +528,7 @@ public class PssmShadowRenderer implements SceneProcessor {
for (int i = 0; i < l.size(); i++) { for (int i = 0; i < l.size(); i++) {
Material mat = l.get(i).getMaterial(); Material mat = l.get(i).getMaterial();
//checking if the material has the post technique and adding it to the material cache //checking if the material has the post technique and adding it to the material cache
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) { if (mat.getMaterialDef().getTechniqueDefs(postTechniqueName) != null) {
if (!matCache.contains(mat)) { if (!matCache.contains(mat)) {
matCache.add(mat); matCache.add(mat);
} }

@ -128,12 +128,13 @@ public class NullContext implements JmeContext, Runnable {
public void run(){ public void run(){
initInThread(); initInThread();
while (!needClose.get()){ do {
listener.update(); listener.update();
if (frameRate > 0) if (frameRate > 0) {
sync(frameRate); sync(frameRate);
} }
} while (!needClose.get());
deinitInThread(); deinitInThread();

@ -39,6 +39,7 @@ import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Matrix4f; import com.jme3.math.Matrix4f;
import com.jme3.renderer.Caps; import com.jme3.renderer.Caps;
import com.jme3.renderer.Limits;
import com.jme3.renderer.Renderer; import com.jme3.renderer.Renderer;
import com.jme3.renderer.Statistics; import com.jme3.renderer.Statistics;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
@ -48,15 +49,25 @@ import com.jme3.shader.Shader.ShaderSource;
import com.jme3.texture.FrameBuffer; import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import java.util.EnumMap;
public class NullRenderer implements Renderer { public class NullRenderer implements Renderer {
private static final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class); private final EnumSet<Caps> caps = EnumSet.allOf(Caps.class);
private static final Statistics stats = new Statistics(); private final EnumMap<Limits, Integer> limits = new EnumMap<>(Limits.class);
private final Statistics stats = new Statistics();
public void initialize() { public void initialize() {
for (Limits limit : Limits.values()) {
limits.put(limit, Integer.MAX_VALUE);
}
} }
@Override
public EnumMap<Limits, Integer> getLimits() {
return limits;
}
public EnumSet<Caps> getCaps() { public EnumSet<Caps> getCaps() {
return caps; return caps;
} }
@ -164,4 +175,7 @@ public class NullRenderer implements Renderer {
public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) { public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
} }
@Override
public void setDefaultAnisotropicFilter(int level) {
}
} }

@ -58,7 +58,7 @@ public final class LastTextureState {
rWrap = null; rWrap = null;
magFilter = null; magFilter = null;
minFilter = null; minFilter = null;
anisoFilter = 0; anisoFilter = 1;
// The default in OpenGL is OFF, so we avoid setting this per texture // The default in OpenGL is OFF, so we avoid setting this per texture
// if its not used. // if its not used.

@ -207,10 +207,10 @@ public class Cloner {
// Check the index to see if we already have it // Check the index to see if we already have it
Object clone = index.get(object); Object clone = index.get(object);
if( clone != null ) { if( clone != null || index.containsKey(object) ) {
if( log.isLoggable(Level.FINER) ) { if( log.isLoggable(Level.FINER) ) {
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object) log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
+ " as cached:" + clone.getClass() + "@" + System.identityHashCode(clone)); + " as cached:" + (clone == null ? "null" : (clone.getClass() + "@" + System.identityHashCode(clone))));
} }
return type.cast(clone); return type.cast(clone);
} }

@ -114,10 +114,10 @@ MaterialDef Phong Lighting {
//For instancing //For instancing
Boolean UseInstancing Boolean UseInstancing
Boolean BackfaceShadows: false Boolean BackfaceShadows : false
} }
Technique { Technique {
LightMode SinglePass LightMode SinglePass
VertexShader GLSL100: Common/MatDefs/Light/SPLighting.vert VertexShader GLSL100: Common/MatDefs/Light/SPLighting.vert
@ -149,7 +149,7 @@ MaterialDef Phong Lighting {
SEPARATE_TEXCOORD : SeparateTexCoord SEPARATE_TEXCOORD : SeparateTexCoord
DISCARD_ALPHA : AlphaDiscardThreshold DISCARD_ALPHA : AlphaDiscardThreshold
USE_REFLECTION : EnvMap USE_REFLECTION : EnvMap
SPHERE_MAP : SphereMap SPHERE_MAP : EnvMapAsSphereMap
NUM_BONES : NumberOfBones NUM_BONES : NumberOfBones
INSTANCING : UseInstancing INSTANCING : UseInstancing
} }
@ -188,7 +188,7 @@ MaterialDef Phong Lighting {
SEPARATE_TEXCOORD : SeparateTexCoord SEPARATE_TEXCOORD : SeparateTexCoord
DISCARD_ALPHA : AlphaDiscardThreshold DISCARD_ALPHA : AlphaDiscardThreshold
USE_REFLECTION : EnvMap USE_REFLECTION : EnvMap
SPHERE_MAP : SphereMap SPHERE_MAP : EnvMapAsSphereMap
NUM_BONES : NumberOfBones NUM_BONES : NumberOfBones
INSTANCING : UseInstancing INSTANCING : UseInstancing
} }
@ -209,7 +209,6 @@ MaterialDef Phong Lighting {
} }
Defines { Defines {
COLOR_MAP : ColorMap
DISCARD_ALPHA : AlphaDiscardThreshold DISCARD_ALPHA : AlphaDiscardThreshold
NUM_BONES : NumberOfBones NUM_BONES : NumberOfBones
INSTANCING : UseInstancing INSTANCING : UseInstancing
@ -218,7 +217,7 @@ MaterialDef Phong Lighting {
} }
Technique PostShadow15{ Technique PostShadow {
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag
@ -234,8 +233,7 @@ MaterialDef Phong Lighting {
HARDWARE_SHADOWS : HardwareShadows HARDWARE_SHADOWS : HardwareShadows
FILTER_MODE : FilterMode FILTER_MODE : FilterMode
PCFEDGE : PCFEdge PCFEDGE : PCFEdge
DISCARD_ALPHA : AlphaDiscardThreshold DISCARD_ALPHA : AlphaDiscardThreshold
COLOR_MAP : ColorMap
SHADOWMAP_SIZE : ShadowMapSize SHADOWMAP_SIZE : ShadowMapSize
FADE : FadeInfo FADE : FadeInfo
PSSM : Splits PSSM : Splits
@ -268,8 +266,7 @@ MaterialDef Phong Lighting {
HARDWARE_SHADOWS : HardwareShadows HARDWARE_SHADOWS : HardwareShadows
FILTER_MODE : FilterMode FILTER_MODE : FilterMode
PCFEDGE : PCFEdge PCFEDGE : PCFEdge
DISCARD_ALPHA : AlphaDiscardThreshold DISCARD_ALPHA : AlphaDiscardThreshold
COLOR_MAP : ColorMap
SHADOWMAP_SIZE : ShadowMapSize SHADOWMAP_SIZE : ShadowMapSize
FADE : FadeInfo FADE : FadeInfo
PSSM : Splits PSSM : Splits
@ -343,10 +340,6 @@ MaterialDef Phong Lighting {
Defines { Defines {
VERTEX_COLOR : UseVertexColor VERTEX_COLOR : UseVertexColor
MATERIAL_COLORS : UseMaterialColors MATERIAL_COLORS : UseMaterialColors
V_TANGENT : VTangent
MINNAERT : Minnaert
WARDISO : WardIso
DIFFUSEMAP : DiffuseMap DIFFUSEMAP : DiffuseMap
NORMALMAP : NormalMap NORMALMAP : NormalMap
SPECULARMAP : SpecularMap SPECULARMAP : SpecularMap

@ -148,7 +148,7 @@ MaterialDef Unshaded {
} }
Technique PostShadow15{ Technique PostShadow {
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag
@ -181,7 +181,7 @@ MaterialDef Unshaded {
} }
} }
Technique PostShadow{ Technique PostShadow {
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag

@ -22,5 +22,4 @@ LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material
LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene
LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, geom, tsctrl, tseval, glsl, glsllib LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, geom, tsctrl, tseval, glsl, glsllib
LOADER com.jme3.scene.plugins.fbx.SceneLoader : fbx LOADER com.jme3.scene.plugins.fbx.FbxLoader : fbx
LOADER com.jme3.scene.plugins.fbx.SceneWithAnimationLoader : fba

@ -31,8 +31,12 @@
*/ */
package com.jme3.material.plugins; package com.jme3.material.plugins;
import com.jme3.material.logic.MultiPassLightingLogic;
import com.jme3.material.logic.SinglePassLightingLogic;
import com.jme3.material.logic.DefaultTechniqueDefLogic;
import com.jme3.asset.*; import com.jme3.asset.*;
import com.jme3.material.*; import com.jme3.material.*;
import com.jme3.material.RenderState.BlendEquation;
import com.jme3.material.RenderState.BlendMode; import com.jme3.material.RenderState.BlendMode;
import com.jme3.material.RenderState.FaceCullMode; import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.material.TechniqueDef.LightMode; import com.jme3.material.TechniqueDef.LightMode;
@ -40,6 +44,7 @@ import com.jme3.material.TechniqueDef.ShadowMode;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.shader.DefineList;
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;
@ -73,15 +78,16 @@ public class J3MLoader implements AssetLoader {
private Material material; private Material material;
private TechniqueDef technique; private TechniqueDef technique;
private RenderState renderState; private RenderState renderState;
private ArrayList<String> presetDefines = new ArrayList<String>();
private EnumMap<Shader.ShaderType, String> shaderLanguage; private EnumMap<Shader.ShaderType, String> shaderLanguages;
private EnumMap<Shader.ShaderType, String> shaderName; private EnumMap<Shader.ShaderType, String> shaderNames;
private static final String whitespacePattern = "\\p{javaWhitespace}+"; private static final String whitespacePattern = "\\p{javaWhitespace}+";
public J3MLoader() { public J3MLoader() {
shaderLanguage = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); shaderLanguages = new EnumMap<>(Shader.ShaderType.class);
shaderName = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); shaderNames = new EnumMap<>(Shader.ShaderType.class);
} }
@ -104,8 +110,8 @@ public class J3MLoader implements AssetLoader {
} }
private void readShaderDefinition(Shader.ShaderType shaderType, String name, String language) { private void readShaderDefinition(Shader.ShaderType shaderType, String name, String language) {
shaderName.put(shaderType, name); shaderNames.put(shaderType, name);
shaderLanguage.put(shaderType, language); shaderLanguages.put(shaderType, language);
} }
// LightMode <MODE> // LightMode <MODE>
@ -443,9 +449,12 @@ public class J3MLoader implements AssetLoader {
renderState.setDepthTest(parseBoolean(split[1])); renderState.setDepthTest(parseBoolean(split[1]));
}else if (split[0].equals("Blend")){ }else if (split[0].equals("Blend")){
renderState.setBlendMode(BlendMode.valueOf(split[1])); renderState.setBlendMode(BlendMode.valueOf(split[1]));
}else if (split[0].equals("BlendEquation")){
renderState.setBlendEquation(BlendEquation.valueOf(split[1]));
}else if (split[0].equals("BlendEquationAlpha")){
renderState.setBlendEquationAlpha(RenderState.BlendEquationAlpha.valueOf(split[1]));
}else if (split[0].equals("AlphaTestFalloff")){ }else if (split[0].equals("AlphaTestFalloff")){
renderState.setAlphaTest(true); // Ignore for backwards compatbility
renderState.setAlphaFallOff(Float.parseFloat(split[1]));
}else if (split[0].equals("PolyOffset")){ }else if (split[0].equals("PolyOffset")){
float factor = Float.parseFloat(split[1]); float factor = Float.parseFloat(split[1]);
float units = Float.parseFloat(split[2]); float units = Float.parseFloat(split[2]);
@ -453,7 +462,7 @@ public class J3MLoader implements AssetLoader {
}else if (split[0].equals("ColorWrite")){ }else if (split[0].equals("ColorWrite")){
renderState.setColorWrite(parseBoolean(split[1])); renderState.setColorWrite(parseBoolean(split[1]));
}else if (split[0].equals("PointSprite")){ }else if (split[0].equals("PointSprite")){
renderState.setPointSprite(parseBoolean(split[1])); // Ignore for backwards compatbility
}else if (split[0].equals("DepthFunc")){ }else if (split[0].equals("DepthFunc")){
renderState.setDepthFunc(RenderState.TestFunction.valueOf(split[1])); renderState.setDepthFunc(RenderState.TestFunction.valueOf(split[1]));
}else if (split[0].equals("AlphaFunc")){ }else if (split[0].equals("AlphaFunc")){
@ -495,10 +504,22 @@ public class J3MLoader implements AssetLoader {
private void readDefine(String statement) throws IOException{ private void readDefine(String statement) throws IOException{
String[] split = statement.split(":"); String[] split = statement.split(":");
if (split.length == 1){ if (split.length == 1){
// add preset define String defineName = split[0].trim();
technique.addShaderPresetDefine(split[0].trim(), VarType.Boolean, true); presetDefines.add(defineName);
}else if (split.length == 2){ }else if (split.length == 2){
technique.addShaderParamDefine(split[1].trim(), split[0].trim()); String defineName = split[0].trim();
String paramName = split[1].trim();
MatParam param = materialDef.getMaterialParam(paramName);
if (param == null) {
logger.log(Level.WARNING, "In technique ''{0}'':\n"
+ "Define ''{1}'' mapped to non-existent"
+ " material parameter ''{2}'', ignoring.",
new Object[]{technique.getName(), defineName, paramName});
return;
}
VarType paramType = param.getVarType();
technique.addShaderParamDefine(paramName, paramType, defineName);
}else{ }else{
throw new IOException("Define syntax incorrect"); throw new IOException("Define syntax incorrect");
} }
@ -560,37 +581,80 @@ public class J3MLoader implements AssetLoader {
} }
material.setTransparent(parseBoolean(split[1])); material.setTransparent(parseBoolean(split[1]));
} }
private static String createShaderPrologue(List<String> presetDefines) {
DefineList dl = new DefineList(presetDefines.size());
for (int i = 0; i < presetDefines.size(); i++) {
dl.set(i, 1);
}
StringBuilder sb = new StringBuilder();
dl.generateSource(sb, presetDefines, null);
return sb.toString();
}
private void readTechnique(Statement techStat) throws IOException{ private void readTechnique(Statement techStat) throws IOException{
isUseNodes = false; isUseNodes = false;
String[] split = techStat.getLine().split(whitespacePattern); String[] split = techStat.getLine().split(whitespacePattern);
String name;
if (split.length == 1) { if (split.length == 1) {
technique = new TechniqueDef(null); name = TechniqueDef.DEFAULT_TECHNIQUE_NAME;
} else if (split.length == 2) { } else if (split.length == 2) {
String techName = split[1]; name = split[1];
technique = new TechniqueDef(techName);
} else { } else {
throw new IOException("Technique statement syntax incorrect"); throw new IOException("Technique statement syntax incorrect");
} }
String techniqueUniqueName = materialDef.getAssetName() + "@" + name;
technique = new TechniqueDef(name, techniqueUniqueName.hashCode());
for (Statement statement : techStat.getContents()){ for (Statement statement : techStat.getContents()){
readTechniqueStatement(statement); readTechniqueStatement(statement);
} }
if(isUseNodes){ if(isUseNodes){
nodesLoaderDelegate.computeConditions(); nodesLoaderDelegate.computeConditions();
//used for caching later, the shader here is not a file. //used for caching later, the shader here is not a file.
// KIRILL 9/19/2015
// Not sure if this is needed anymore, since shader caching
// is now done by TechniqueDef.
technique.setShaderFile(technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100"); technique.setShaderFile(technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100");
} }else if (shaderNames.containsKey(Shader.ShaderType.Vertex) && shaderNames.containsKey(Shader.ShaderType.Fragment)) {
technique.setShaderFile(shaderNames, shaderLanguages);
if (shaderName.containsKey(Shader.ShaderType.Vertex) && shaderName.containsKey(Shader.ShaderType.Fragment)) { } else {
technique.setShaderFile(shaderName, shaderLanguage); technique = null;
shaderLanguages.clear();
shaderNames.clear();
presetDefines.clear();
logger.log(Level.WARNING, "Fixed function technique was ignored");
logger.log(Level.WARNING, "Fixed function technique ''{0}'' was ignored for material {1}",
new Object[]{name, key});
return;
}
technique.setShaderPrologue(createShaderPrologue(presetDefines));
switch (technique.getLightMode()) {
case Disable:
technique.setLogic(new DefaultTechniqueDefLogic(technique));
break;
case MultiPass:
technique.setLogic(new MultiPassLightingLogic(technique));
break;
case SinglePass:
technique.setLogic(new SinglePassLightingLogic(technique));
break;
default:
throw new UnsupportedOperationException();
} }
materialDef.addTechniqueDef(technique); materialDef.addTechniqueDef(technique);
technique = null; technique = null;
shaderLanguage.clear(); shaderLanguages.clear();
shaderName.clear(); shaderNames.clear();
presetDefines.clear();
} }
private void loadFromRoot(List<Statement> roots) throws IOException{ private void loadFromRoot(List<Statement> roots) throws IOException{
@ -711,7 +775,7 @@ public class J3MLoader implements AssetLoader {
protected void initNodesLoader() { protected void initNodesLoader() {
if (!isUseNodes) { if (!isUseNodes) {
isUseNodes = shaderName.get(Shader.ShaderType.Vertex) == null && shaderName.get(Shader.ShaderType.Fragment) == null; isUseNodes = shaderNames.get(Shader.ShaderType.Vertex) == null && shaderNames.get(Shader.ShaderType.Fragment) == null;
if (isUseNodes) { if (isUseNodes) {
if (nodesLoaderDelegate == null) { if (nodesLoaderDelegate == null) {
nodesLoaderDelegate = new ShaderNodeLoaderDelegate(); nodesLoaderDelegate = new ShaderNodeLoaderDelegate();

@ -44,6 +44,7 @@ import com.jme3.shader.ShaderNodeDefinition;
import com.jme3.shader.ShaderNodeVariable; import com.jme3.shader.ShaderNodeVariable;
import com.jme3.shader.ShaderUtils; import com.jme3.shader.ShaderUtils;
import com.jme3.shader.UniformBinding; import com.jme3.shader.UniformBinding;
import com.jme3.shader.VarType;
import com.jme3.shader.VariableMapping; import com.jme3.shader.VariableMapping;
import com.jme3.util.blockparser.Statement; import com.jme3.util.blockparser.Statement;
import java.io.IOException; import java.io.IOException;
@ -583,7 +584,7 @@ public class ShaderNodeLoaderDelegate {
//multiplicity is not an int attempting to find for a material parameter. //multiplicity is not an int attempting to find for a material parameter.
MatParam mp = findMatParam(multiplicity); MatParam mp = findMatParam(multiplicity);
if (mp != null) { if (mp != null) {
addDefine(multiplicity); addDefine(multiplicity, VarType.Int);
multiplicity = multiplicity.toUpperCase(); multiplicity = multiplicity.toUpperCase();
} else { } else {
throw new MatParseException("Wrong multiplicity for variable" + mapping.getLeftVariable().getName() + ". " + multiplicity + " should be an int or a declared material parameter.", statement); throw new MatParseException("Wrong multiplicity for variable" + mapping.getLeftVariable().getName() + ". " + multiplicity + " should be an int or a declared material parameter.", statement);
@ -625,9 +626,9 @@ public class ShaderNodeLoaderDelegate {
* *
* @param paramName * @param paramName
*/ */
public void addDefine(String paramName) { public void addDefine(String paramName, VarType paramType) {
if (techniqueDef.getShaderParamDefine(paramName) == null) { if (techniqueDef.getShaderParamDefine(paramName) == null) {
techniqueDef.addShaderParamDefine(paramName, paramName.toUpperCase()); techniqueDef.addShaderParamDefine(paramName, paramType, paramName.toUpperCase());
} }
} }
@ -660,7 +661,7 @@ public class ShaderNodeLoaderDelegate {
for (String string : defines) { for (String string : defines) {
MatParam param = findMatParam(string); MatParam param = findMatParam(string);
if (param != null) { if (param != null) {
addDefine(param.getName()); addDefine(param.getName(), param.getVarType());
} else { } else {
throw new MatParseException("Invalid condition, condition must match a Material Parameter named " + cond, statement); throw new MatParseException("Invalid condition, condition must match a Material Parameter named " + cond, statement);
} }

@ -149,8 +149,7 @@ public class MTLLoader implements AssetLoader {
if (transparent){ if (transparent){
material.setTransparent(true); material.setTransparent(true);
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
material.getAdditionalRenderState().setAlphaTest(true); material.setFloat("AlphaDiscardThreshold", 0.01f);
material.getAdditionalRenderState().setAlphaFallOff(0.01f);
} }
matList.put(matName, material); matList.put(matName, material);

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 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
@ -29,16 +29,24 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.animation; package com.jme3.asset;
/** import com.jme3.asset.plugins.ClasspathLocator;
* @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack) import com.jme3.shader.plugins.GLSLLoader;
*/ import com.jme3.system.JmeSystem;
@Deprecated import com.jme3.system.MockJmeSystemDelegate;
public final class BoneAnimation extends Animation { import org.junit.Test;
public class LoadShaderSourceTest {
@Deprecated @Test
public BoneAnimation(String name, float length) { public void testLoadShaderSource() {
super(name, length); JmeSystem.setSystemDelegate(new MockJmeSystemDelegate());
AssetManager assetManager = new DesktopAssetManager();
assetManager.registerLocator(null, ClasspathLocator.class);
assetManager.registerLoader(GLSLLoader.class, "frag");
String showNormals = (String) assetManager.loadAsset("Common/MatDefs/Misc/ShowNormals.frag");
System.out.println(showNormals);
} }
} }

@ -0,0 +1,600 @@
/*
* Copyright (c) 2009-2016 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.material;
import com.jme3.asset.AssetManager;
import com.jme3.light.LightList;
import com.jme3.math.Matrix4f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.shader.Shader;
import com.jme3.shader.Uniform;
import com.jme3.shader.VarType;
import java.util.Arrays;
import java.util.HashSet;
import org.junit.Assert;
import org.junit.Test;
import static com.jme3.scene.MPOTestUtils.*;
import com.jme3.scene.Node;
import com.jme3.shader.DefineList;
import com.jme3.system.NullRenderer;
import com.jme3.system.TestUtil;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
/**
* Validates how {@link MatParamOverride MPOs} work on the material level.
*
* @author Kirill Vainer
*/
public class MaterialMatParamOverrideTest {
private static final HashSet<String> IGNORED_UNIFORMS = new HashSet<String>(
Arrays.asList(new String[]{"m_ParallaxHeight", "m_Shininess", "m_BackfaceShadows"}));
@Test
public void testBoolMpoOnly() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMpo(mpoBool("UseMaterialColors", true));
outDefines(def("MATERIAL_COLORS", VarType.Boolean, true));
outUniforms(uniform("UseMaterialColors", VarType.Boolean, true));
}
@Test
public void testBoolMpOnly() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMp(mpoBool("UseMaterialColors", true));
outDefines(def("MATERIAL_COLORS", VarType.Boolean, true));
outUniforms(uniform("UseMaterialColors", VarType.Boolean, true));
}
@Test
public void testBoolOverrideFalse() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMp(mpoBool("UseMaterialColors", true));
inputMpo(mpoBool("UseMaterialColors", false));
outDefines();
outUniforms(uniform("UseMaterialColors", VarType.Boolean, false));
}
@Test
public void testBoolOverrideTrue() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMp(mpoBool("UseMaterialColors", false));
inputMpo(mpoBool("UseMaterialColors", true));
outDefines(def("MATERIAL_COLORS", VarType.Boolean, true));
outUniforms(uniform("UseMaterialColors", VarType.Boolean, true));
}
@Test
public void testFloatMpoOnly() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMpo(mpoFloat("AlphaDiscardThreshold", 3.12f));
outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f));
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f));
}
@Test
public void testFloatMpOnly() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f));
outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f));
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f));
}
@Test
public void testFloatOverride() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f));
inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f));
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f));
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f));
}
@Test
public void testForcedOverride() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f));
inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f));
inputFpo(mpoFloat("AlphaDiscardThreshold", 1.23f));
outDefines(def("DISCARD_ALPHA", VarType.Float, 1.23f));
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 1.23f));
reset();
root.clearMatParamOverrides();
root.updateGeometricState();
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f));
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f));
}
@Test
public void testChildOverridesParent() {
material("Common/MatDefs/Light/Lighting.j3md");
inputParentMpo(mpoFloat("AlphaDiscardThreshold", 3.12f));
inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f));
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f));
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f));
}
@Test
public void testMpoDisable() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f));
MatParamOverride override = mpoFloat("AlphaDiscardThreshold", 2.79f);
inputMpo(override);
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f));
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f));
reset();
override.setEnabled(false);
outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f));
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f));
reset();
override.setEnabled(true);
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f));
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f));
}
@Test
public void testIntMpoOnly() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMpo(mpoInt("NumberOfBones", 1234));
outDefines(def("NUM_BONES", VarType.Int, 1234));
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
}
@Test
public void testIntMpOnly() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMp(mpoInt("NumberOfBones", 1234));
outDefines(def("NUM_BONES", VarType.Int, 1234));
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
}
@Test
public void testIntOverride() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMp(mpoInt("NumberOfBones", 1234));
inputMpo(mpoInt("NumberOfBones", 4321));
outDefines(def("NUM_BONES", VarType.Int, 4321));
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
}
@Test
public void testMatrixArray() {
Matrix4f[] matrices = new Matrix4f[]{
new Matrix4f()
};
material("Common/MatDefs/Light/Lighting.j3md");
inputMpo(mpoMatrix4Array("BoneMatrices", matrices));
outDefines();
outUniforms(uniform("BoneMatrices", VarType.Matrix4Array, matrices));
}
@Test
public void testNonExistentParameter() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMpo(mpoInt("NonExistent", 4321));
outDefines();
outUniforms();
}
@Test
public void testWrongType() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMpo(mpoInt("UseMaterialColors", 4321));
outDefines();
outUniforms();
}
@Test
public void testParamOnly() {
material("Common/MatDefs/Light/Lighting.j3md");
inputMpo(mpoFloat("ShadowMapSize", 3.12f));
outDefines();
outUniforms(uniform("ShadowMapSize", VarType.Float, 3.12f));
}
@Test
public void testRemove() {
material("Common/MatDefs/Light/Lighting.j3md");
reset();
inputMp(mpoInt("NumberOfBones", 1234));
outDefines(def("NUM_BONES", VarType.Int, 1234));
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
reset();
inputMpo(mpoInt("NumberOfBones", 4321));
outDefines(def("NUM_BONES", VarType.Int, 4321));
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
reset();
geometry.clearMatParamOverrides();
root.updateGeometricState();
outDefines(def("NUM_BONES", VarType.Int, 1234));
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
reset();
geometry.getMaterial().clearParam("NumberOfBones");
outDefines();
outUniforms();
reset();
inputMpo(mpoInt("NumberOfBones", 4321));
outDefines(def("NUM_BONES", VarType.Int, 4321));
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
reset();
inputMp(mpoInt("NumberOfBones", 1234));
outDefines(def("NUM_BONES", VarType.Int, 4321));
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
}
public void testRemoveOverride() {
material("Common/MatDefs/Light/Lighting.j3md");
reset();
inputMp(mpoInt("NumberOfBones", 1234));
outDefines(def("NUM_BONES", VarType.Int, 1234));
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
reset();
inputMpo(mpoInt("NumberOfBones", 4321));
outDefines(def("NUM_BONES", VarType.Int, 4321));
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
reset();
geometry.clearMatParamOverrides();
outDefines(def("NUM_BONES", VarType.Int, 1234));
outUniforms(uniform("NumberOfBones", VarType.Int, 1234));
}
@Test
public void testRemoveMpoOnly() {
material("Common/MatDefs/Light/Lighting.j3md");
reset();
inputMpo(mpoInt("NumberOfBones", 4321));
outDefines(def("NUM_BONES", VarType.Int, 4321));
outUniforms(uniform("NumberOfBones", VarType.Int, 4321));
reset();
geometry.clearMatParamOverrides();
root.updateGeometricState();
outDefines();
outUniforms();
}
@Test
public void testTextureMpoOnly() {
material("Common/MatDefs/Light/Lighting.j3md");
Texture2D tex = new Texture2D(128, 128, Format.RGBA8);
inputMpo(mpoTexture2D("DiffuseMap", tex));
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex));
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
outTextures(tex);
}
@Test
public void testTextureOverride() {
material("Common/MatDefs/Light/Lighting.j3md");
Texture2D tex1 = new Texture2D(128, 128, Format.RGBA8);
Texture2D tex2 = new Texture2D(128, 128, Format.RGBA8);
inputMp(mpoTexture2D("DiffuseMap", tex1));
inputMpo(mpoTexture2D("DiffuseMap", tex2));
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex2));
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
outTextures(tex2);
}
@Test
public void testRemoveTexture() {
material("Common/MatDefs/Light/Lighting.j3md");
Texture2D tex = new Texture2D(128, 128, Format.RGBA8);
reset();
inputMpo(mpoTexture2D("DiffuseMap", tex));
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex));
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
outTextures(tex);
reset();
geometry.clearMatParamOverrides();
root.updateGeometricState();
outDefines();
outUniforms();
outTextures();
}
@Test
public void testRemoveTextureOverride() {
material("Common/MatDefs/Light/Lighting.j3md");
Texture2D tex1 = new Texture2D(128, 128, Format.RGBA8);
Texture2D tex2 = new Texture2D(128, 128, Format.RGBA8);
reset();
inputMp(mpoTexture2D("DiffuseMap", tex1));
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1));
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
outTextures(tex1);
reset();
inputMpo(mpoTexture2D("DiffuseMap", tex2));
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex2));
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
outTextures(tex2);
reset();
geometry.clearMatParamOverrides();
root.updateGeometricState();
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1));
outUniforms(uniform("DiffuseMap", VarType.Int, 0));
outTextures(tex1);
}
private static final class Define {
public String name;
public VarType type;
public Object value;
@Override
public int hashCode() {
int hash = 3;
hash = 89 * hash + this.name.hashCode();
hash = 89 * hash + this.type.hashCode();
hash = 89 * hash + this.value.hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
final Define other = (Define) obj;
return this.name.equals(other.name) && this.type.equals(other.type) && this.value.equals(other.value);
}
}
private final Geometry geometry = new Geometry("Geometry", new Box(1, 1, 1));
private final Node root = new Node("Root Node");
private final LightList lightList = new LightList(geometry);
@Before
public void setUp() {
root.attachChild(geometry);
}
private final NullRenderer renderer = new NullRenderer() {
@Override
public void setShader(Shader shader) {
MaterialMatParamOverrideTest.this.usedShader = shader;
evaluated = true;
}
@Override
public void setTexture(int unit, Texture texture) {
MaterialMatParamOverrideTest.this.usedTextures[unit] = texture;
}
};
private final RenderManager renderManager = new RenderManager(renderer);
private boolean evaluated = false;
private Shader usedShader = null;
private final Texture[] usedTextures = new Texture[32];
private void inputMp(MatParam... params) {
if (evaluated) {
throw new IllegalStateException();
}
Material mat = geometry.getMaterial();
for (MatParam param : params) {
mat.setParam(param.getName(), param.getVarType(), param.getValue());
}
}
private void inputMpo(MatParamOverride... overrides) {
if (evaluated) {
throw new IllegalStateException();
}
for (MatParamOverride override : overrides) {
geometry.addMatParamOverride(override);
}
root.updateGeometricState();
}
private void inputParentMpo(MatParamOverride... overrides) {
if (evaluated) {
throw new IllegalStateException();
}
for (MatParamOverride override : overrides) {
root.addMatParamOverride(override);
}
root.updateGeometricState();
}
private void inputFpo(MatParamOverride... overrides) {
if (evaluated) {
throw new IllegalStateException();
}
for (MatParamOverride override : overrides) {
renderManager.addForcedMatParam(override);
}
}
private void reset() {
evaluated = false;
usedShader = null;
Arrays.fill(usedTextures, null);
for (MatParamOverride override : new ArrayList<>(renderManager.getForcedMatParams())) {
renderManager.removeForcedMatParam(override);
}
}
private Define def(String name, VarType type, Object value) {
Define d = new Define();
d.name = name;
d.type = type;
d.value = value;
return d;
}
private Uniform uniform(String name, VarType type, Object value) {
Uniform u = new Uniform();
u.setName("m_" + name);
u.setValue(type, value);
return u;
}
private void material(String path) {
AssetManager assetManager = TestUtil.createAssetManager();
geometry.setMaterial(new Material(assetManager, path));
}
private void evaluateTechniqueDef() {
Assert.assertFalse(evaluated);
Material mat = geometry.getMaterial();
mat.render(geometry, lightList, renderManager);
Assert.assertTrue(evaluated);
}
private void outTextures(Texture... textures) {
for (int i = 0; i < usedTextures.length; i++) {
if (i < textures.length) {
Assert.assertSame(textures[i], usedTextures[i]);
} else {
Assert.assertNull(usedTextures[i]);
}
}
}
private void outDefines(Define... expectedDefinesArray) {
Map<String, Define> nameToDefineMap = new HashMap<String, Define>();
for (Define define : expectedDefinesArray) {
nameToDefineMap.put(define.name, define);
}
if (!evaluated) {
evaluateTechniqueDef();
}
Material mat = geometry.getMaterial();
Technique tech = mat.getActiveTechnique();
TechniqueDef def = tech.getDef();
DefineList actualDefines = tech.getDynamicDefines();
String[] defineNames = def.getDefineNames();
VarType[] defineTypes = def.getDefineTypes();
Assert.assertEquals(defineNames.length, defineTypes.length);
for (int index = 0; index < defineNames.length; index++) {
String name = defineNames[index];
VarType type = defineTypes[index];
Define expectedDefine = nameToDefineMap.remove(name);
Object expectedValue = null;
if (expectedDefine != null) {
Assert.assertEquals(expectedDefine.type, type);
expectedValue = expectedDefine.value;
}
switch (type) {
case Boolean:
if (expectedValue != null) {
Assert.assertEquals((boolean) (Boolean) expectedValue, actualDefines.getBoolean(index));
} else {
Assert.assertEquals(false, actualDefines.getBoolean(index));
}
break;
case Int:
if (expectedValue != null) {
Assert.assertEquals((int) (Integer) expectedValue, actualDefines.getInt(index));
} else {
Assert.assertEquals(0, actualDefines.getInt(index));
}
break;
case Float:
if (expectedValue != null) {
Assert.assertEquals((float) (Float) expectedValue, actualDefines.getFloat(index), 0f);
} else {
Assert.assertEquals(0f, actualDefines.getFloat(index), 0f);
}
break;
default:
if (expectedValue != null) {
Assert.assertEquals(1, actualDefines.getInt(index));
} else {
Assert.assertEquals(0, actualDefines.getInt(index));
}
break;
}
}
Assert.assertTrue(nameToDefineMap.isEmpty());
}
private void outUniforms(Uniform... uniforms) {
if (!evaluated) {
evaluateTechniqueDef();
}
HashSet<Uniform> actualUniforms = new HashSet<>();
for (Uniform uniform : usedShader.getUniformMap().values()) {
if (uniform.getName().startsWith("m_")
&& !IGNORED_UNIFORMS.contains(uniform.getName())) {
actualUniforms.add(uniform);
}
}
HashSet<Uniform> expectedUniforms = new HashSet<>(Arrays.asList(uniforms));
if (!expectedUniforms.equals(actualUniforms)) {
Assert.fail("Uniform lists must match: " + expectedUniforms + " != " + actualUniforms);
}
}
}

@ -0,0 +1,171 @@
/*
* Copyright (c) 2009-2016 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.material;
import com.jme3.asset.AssetManager;
import com.jme3.light.LightList;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.system.NullRenderer;
import com.jme3.system.TestUtil;
import java.util.Arrays;
import java.util.EnumSet;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class MaterialTest {
private Material material;
private final Geometry geometry = new Geometry("Geometry", new Box(1, 1, 1));
private final EnumSet<Caps> myCaps = EnumSet.noneOf(Caps.class);
private final RenderManager renderManager = new RenderManager(new NullRenderer() {
@Override
public EnumSet<Caps> getCaps() {
return MaterialTest.this.myCaps;
}
});
@Test(expected = IllegalArgumentException.class)
public void testSelectNonExistentTechnique() {
material("Common/MatDefs/Gui/Gui.j3md");
material.selectTechnique("Doesn't Exist", renderManager);
}
@Test(expected = UnsupportedOperationException.class)
public void testSelectDefaultTechnique_NoCaps() {
material("Common/MatDefs/Gui/Gui.j3md");
material.selectTechnique("Default", renderManager);
}
@Test
public void testSelectDefaultTechnique_GLSL100Cap() {
supportGlsl(100);
material("Common/MatDefs/Gui/Gui.j3md");
material.selectTechnique("Default", renderManager);
checkRequiredCaps(Caps.GLSL100);
}
@Test
public void testSelectDefaultTechnique_GLSL150Cap() {
supportGlsl(150);
material("Common/MatDefs/Gui/Gui.j3md");
material.selectTechnique("Default", renderManager);
checkRequiredCaps(Caps.GLSL150);
}
@Test
public void testSelectDefaultTechnique_GLSL120Cap_MultipleLangs() {
supportGlsl(120);
material("Common/MatDefs/Misc/Particle.j3md");
material.selectTechnique("Default", renderManager);
checkRequiredCaps(Caps.GLSL100, Caps.GLSL120);
}
@Test
public void testSelectDefaultTechnique_GLSL100Cap_MultipleLangs() {
supportGlsl(100);
material("Common/MatDefs/Misc/Particle.j3md");
material.selectTechnique("Default", renderManager);
checkRequiredCaps(Caps.GLSL100);
}
@Test
public void testSelectNamedTechnique_GLSL150Cap() {
supportGlsl(150);
material("Common/MatDefs/Light/Lighting.j3md");
material.selectTechnique("PostShadow", renderManager);
checkRequiredCaps(Caps.GLSL150);
}
@Test
public void testSelectNamedTechnique_GLSL100Cap() {
supportGlsl(100);
material("Common/MatDefs/Light/Lighting.j3md");
material.selectTechnique("PostShadow", renderManager);
checkRequiredCaps(Caps.GLSL100);
}
private void checkRequiredCaps(Caps... caps) {
EnumSet<Caps> expected = EnumSet.noneOf(Caps.class);
expected.addAll(Arrays.asList(caps));
Technique tech = material.getActiveTechnique();
assertEquals(expected, tech.getDef().getRequiredCaps());
}
private void supportGlsl(int version) {
switch (version) {
case 150:
myCaps.add(Caps.GLSL150);
case 140:
myCaps.add(Caps.GLSL140);
case 130:
myCaps.add(Caps.GLSL130);
case 120:
myCaps.add(Caps.GLSL120);
case 110:
myCaps.add(Caps.GLSL110);
case 100:
myCaps.add(Caps.GLSL100);
break;
}
}
private void caps(Caps... caps) {
myCaps.addAll(Arrays.asList(caps));
}
private void material(String path) {
AssetManager assetManager = TestUtil.createAssetManager();
material = new Material(assetManager, path);
geometry.setMaterial(material);
}
}

@ -7,8 +7,11 @@ import com.jme3.asset.TextureKey;
import com.jme3.material.MatParamTexture; import com.jme3.material.MatParamTexture;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.MaterialDef; import com.jme3.material.MaterialDef;
import com.jme3.renderer.Caps;
import com.jme3.shader.VarType; import com.jme3.shader.VarType;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import java.io.IOException;
import java.util.EnumSet;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -18,6 +21,7 @@ import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
@ -51,6 +55,30 @@ public class J3MLoaderTest {
j3MLoader = new J3MLoader(); j3MLoader = new J3MLoader();
} }
@Test
public void noDefaultTechnique_shouldBeSupported() throws IOException {
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/no-default-technique.j3md"));
MaterialDef def = (MaterialDef) j3MLoader.load(assetInfo);
assertEquals(1, def.getTechniqueDefs("Test").size());
}
@Test
public void fixedPipelineTechnique_shouldBeIgnored() throws IOException {
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/no-shader-specified.j3md"));
MaterialDef def = (MaterialDef) j3MLoader.load(assetInfo);
assertEquals(null, def.getTechniqueDefs("A"));
assertEquals(1, def.getTechniqueDefs("B").size());
}
@Test
public void multipleSameNamedTechniques_shouldBeSupported() throws IOException {
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/same-name-technique.j3md"));
MaterialDef def = (MaterialDef) j3MLoader.load(assetInfo);
assertEquals(2, def.getTechniqueDefs("Test").size());
assertEquals(EnumSet.of(Caps.GLSL150), def.getTechniqueDefs("Test").get(0).getRequiredCaps());
assertEquals(EnumSet.of(Caps.GLSL100), def.getTechniqueDefs("Test").get(1).getRequiredCaps());
}
@Test @Test
public void oldStyleTextureParameters_shouldBeSupported() throws Exception { public void oldStyleTextureParameters_shouldBeSupported() throws Exception {
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-oldstyle.j3m")); when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-oldstyle.j3m"));
@ -107,7 +135,7 @@ public class J3MLoaderTest {
} }
private TextureKey setupMockForTexture(final String paramName, final String path, final boolean flipY, final Texture texture) { 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)); when(materialDef.getMaterialParam(paramName)).thenReturn(new MatParamTexture(VarType.Texture2D, paramName, texture, null));
final TextureKey textureKey = new TextureKey(path, flipY); final TextureKey textureKey = new TextureKey(path, flipY);
textureKey.setGenerateMips(true); textureKey.setGenerateMips(true);

@ -0,0 +1,100 @@
/*
* Copyright (c) 2009-2016 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.material.plugins;
import com.jme3.asset.AssetManager;
import com.jme3.material.*;
import com.jme3.renderer.*;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.shader.Shader;
import com.jme3.system.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.*;
import static org.junit.Assert.assertEquals;
@RunWith(MockitoJUnitRunner.class)
public class LoadJ3mdTest {
private Material material;
private final Geometry geometry = new Geometry("Geometry", new Box(1, 1, 1));
private final EnumSet<Caps> myCaps = EnumSet.noneOf(Caps.class);
private final RenderManager renderManager = new RenderManager(new NullRenderer() {
@Override
public EnumSet<Caps> getCaps() {
return LoadJ3mdTest.this.myCaps;
}
});
@Test
public void testShaderNodesMaterialDefLoading() {
supportGlsl(100);
material("testMatDef.j3md");
material.selectTechnique("Default", renderManager);
assertEquals(material.getActiveTechnique().getDef().getShaderNodes().size(), 2);
Shader s = material.getActiveTechnique().getDef().getShader(TestUtil.createAssetManager(), myCaps, material.getActiveTechnique().getDynamicDefines());
assertEquals(s.getSources().size(), 2);
}
private void supportGlsl(int version) {
switch (version) {
case 150:
myCaps.add(Caps.GLSL150);
case 140:
myCaps.add(Caps.GLSL140);
case 130:
myCaps.add(Caps.GLSL130);
case 120:
myCaps.add(Caps.GLSL120);
case 110:
myCaps.add(Caps.GLSL110);
case 100:
myCaps.add(Caps.GLSL100);
break;
}
}
private void caps(Caps... caps) {
myCaps.addAll(Arrays.asList(caps));
}
private void material(String path) {
AssetManager assetManager = TestUtil.createAssetManager();
material = new Material(assetManager, path);
geometry.setMaterial(material);
}
}

@ -33,6 +33,9 @@ package com.jme3.math;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals;
import org.junit.Ignore;
/** /**
* Verifies that algorithms in {@link FastMath} are working correctly. * Verifies that algorithms in {@link FastMath} are working correctly.
* *
@ -56,4 +59,39 @@ public class FastMathTest {
assert nextPowerOf2 == nearestPowerOfTwoSlow(i); assert nextPowerOf2 == nearestPowerOfTwoSlow(i);
} }
} }
private static int fastCounterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) {
float result = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
return (int) Math.signum(result);
}
private static Vector2f randomVector() {
return new Vector2f(FastMath.nextRandomFloat(),
FastMath.nextRandomFloat());
}
@Ignore
@Test
public void testCounterClockwise() {
for (int i = 0; i < 100; i++) {
Vector2f p0 = randomVector();
Vector2f p1 = randomVector();
Vector2f p2 = randomVector();
int fastResult = fastCounterClockwise(p0, p1, p2);
int slowResult = FastMath.counterClockwise(p0, p1, p2);
assert fastResult == slowResult;
}
// duplicate test
Vector2f p0 = new Vector2f(0,0);
Vector2f p1 = new Vector2f(0,0);
Vector2f p2 = new Vector2f(0,1);
int fastResult = fastCounterClockwise(p0, p1, p2);
int slowResult = FastMath.counterClockwise(p0, p1, p2);
assertEquals(slowResult, fastResult);
}
} }

@ -0,0 +1,343 @@
/*
* 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.renderer;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.TechniqueDef;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.renderer.queue.OpaqueComparator;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.shape.Box;
import com.jme3.system.TestUtil;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.texture.image.ColorSpace;
import com.jme3.util.BufferUtils;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class OpaqueComparatorTest {
private final Mesh mesh = new Box(1,1,1);
private Camera cam = new Camera(1, 1);
private RenderManager renderManager;
private AssetManager assetManager;
private OpaqueComparator comparator = new OpaqueComparator();
@Before
public void setUp() {
assetManager = TestUtil.createAssetManager();
renderManager = TestUtil.createRenderManager();
comparator.setCamera(cam);
}
/**
* Given a correctly sorted list of materials, check if the
* opaque comparator can sort a reversed list of them.
*
* Each material will be cloned so that none of them will be equal to each other
* in reference, forcing the comparator to compare the material sort ID.
*
* E.g. for a list of materials A, B, C, the following list will be generated:
* <pre>C, B, A, C, B, A, C, B, A</pre>, it should result in
* <pre>A, A, A, B, B, B, C, C, C</pre>.
*
* @param materials The pre-sorted list of materials to check sorting for.
*/
private void testSort(Material ... materials) {
GeometryList gl = new GeometryList(comparator);
for (int g = 0; g < 5; g++) {
for (int i = materials.length - 1; i >= 0; i--) {
Geometry geom = new Geometry("geom", mesh);
Material clonedMaterial = materials[i].clone();
if (materials[i].getActiveTechnique() != null) {
String techniqueName = materials[i].getActiveTechnique().getDef().getName();
clonedMaterial.selectTechnique(techniqueName, renderManager);
} else {
clonedMaterial.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
}
geom.setMaterial(clonedMaterial);
gl.add(geom);
}
}
gl.sort();
for (int i = 0; i < gl.size(); i++) {
Material mat = gl.get(i).getMaterial();
String sortId = Integer.toHexString(mat.getSortId()).toUpperCase();
System.out.print(sortId + "\t");
System.out.println(mat);
}
Set<String> alreadySeen = new HashSet<String>();
Material current = null;
for (int i = 0; i < gl.size(); i++) {
Material mat = gl.get(i).getMaterial();
if (current == null) {
current = mat;
} else if (!current.getName().equals(mat.getName())) {
assert !alreadySeen.contains(mat.getName());
alreadySeen.add(current.getName());
current = mat;
}
}
for (int i = 0; i < materials.length; i++) {
for (int g = 0; g < 5; g++) {
int index = i * 5 + g;
Material mat1 = gl.get(index).getMaterial();
Material mat2 = materials[i];
assert mat1.getName().equals(mat2.getName()) :
mat1.getName() + " != " + mat2.getName() + " for index " + index;
}
}
}
@Test
public void testSortByMaterialDef() {
Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material particleMat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
Material unshadedMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md");
lightingMat.setName("MatLight");
particleMat.setName("MatParticle");
unshadedMat.setName("MatUnshaded");
skyMat.setName("MatSky");
testSort(skyMat, lightingMat, particleMat, unshadedMat);
}
@Test
public void testSortByTechnique() {
Material lightingMatDefault = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingPreShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingPostShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingMatPreNormalPass = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingMatGBuf = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingMatGlow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
lightingMatDefault.setName("TechDefault");
lightingMatDefault.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
lightingPostShadow.setName("TechPostShad");
lightingPostShadow.selectTechnique("PostShadow", renderManager);
lightingPreShadow.setName("TechPreShad");
lightingPreShadow.selectTechnique("PreShadow", renderManager);
lightingMatPreNormalPass.setName("TechNorm");
lightingMatPreNormalPass.selectTechnique("PreNormalPass", renderManager);
lightingMatGBuf.setName("TechGBuf");
lightingMatGBuf.selectTechnique("GBuf", renderManager);
lightingMatGlow.setName("TechGlow");
lightingMatGlow.selectTechnique("Glow", renderManager);
testSort(lightingMatGlow, lightingPreShadow, lightingMatPreNormalPass,
lightingMatDefault, lightingPostShadow, lightingMatGBuf);
}
@Test(expected = AssertionError.class)
public void testNoSortByParam() {
Material sameMat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
Material sameMat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
sameMat1.setName("MatRed");
sameMat1.setColor("Color", ColorRGBA.Red);
sameMat2.setName("MatBlue");
sameMat2.setColor("Color", ColorRGBA.Blue);
testSort(sameMat1, sameMat2);
}
private Texture createTexture(String name) {
ByteBuffer bb = BufferUtils.createByteBuffer(3);
Image image = new Image(Format.RGB8, 1, 1, bb, ColorSpace.sRGB);
Texture2D texture = new Texture2D(image);
texture.setName(name);
return texture;
}
@Test
public void testSortByTexture() {
Material texture1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
Material texture2Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
Material texture3Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
Texture tex1 = createTexture("A");
tex1.getImage().setId(1);
Texture tex2 = createTexture("B");
tex2.getImage().setId(2);
Texture tex3 = createTexture("C");
tex3.getImage().setId(3);
texture1Mat.setName("TexA");
texture1Mat.setTexture("ColorMap", tex1);
texture2Mat.setName("TexB");
texture2Mat.setTexture("ColorMap", tex2);
texture3Mat.setName("TexC");
texture3Mat.setTexture("ColorMap", tex3);
testSort(texture1Mat, texture2Mat, texture3Mat);
}
@Test
public void testSortByShaderDefines() {
Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingMatVColor = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingMatVLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingMatTC = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingMatVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material lightingMatTCVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
lightingMat.setName("DefNone");
lightingMatVColor.setName("DefVC");
lightingMatVColor.setBoolean("UseVertexColor", true);
lightingMatVLight.setName("DefVL");
lightingMatVLight.setBoolean("VertexLighting", true);
lightingMatTC.setName("DefTC");
lightingMatTC.setBoolean("SeparateTexCoord", true);
lightingMatVColorLight.setName("DefVCVL");
lightingMatVColorLight.setBoolean("UseVertexColor", true);
lightingMatVColorLight.setBoolean("VertexLighting", true);
lightingMatTCVColorLight.setName("DefVCVLTC");
lightingMatTCVColorLight.setBoolean("UseVertexColor", true);
lightingMatTCVColorLight.setBoolean("VertexLighting", true);
lightingMatTCVColorLight.setBoolean("SeparateTexCoord", true);
testSort(lightingMat, lightingMatVColor, lightingMatVLight,
lightingMatVColorLight, lightingMatTC, lightingMatTCVColorLight);
}
@Test
public void testSortByAll() {
Material matBase1 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material matBase2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
Texture texBase = createTexture("BASE");
texBase.getImage().setId(1);
Texture tex1 = createTexture("1");
tex1.getImage().setId(2);
Texture tex2 = createTexture("2");
tex2.getImage().setId(3);
matBase1.setName("BASE");
matBase1.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
matBase1.setBoolean("UseVertexColor", true);
matBase1.setTexture("DiffuseMap", texBase);
Material mat1100 = matBase1.clone();
mat1100.setName("1100");
mat1100.selectTechnique("PreShadow", renderManager);
Material mat1101 = matBase1.clone();
mat1101.setName("1101");
mat1101.selectTechnique("PreShadow", renderManager);
mat1101.setTexture("DiffuseMap", tex1);
Material mat1102 = matBase1.clone();
mat1102.setName("1102");
mat1102.selectTechnique("PreShadow", renderManager);
mat1102.setTexture("DiffuseMap", tex2);
Material mat1110 = matBase1.clone();
mat1110.setName("1110");
mat1110.selectTechnique("PreShadow", renderManager);
mat1110.setFloat("AlphaDiscardThreshold", 2f);
Material mat1120 = matBase1.clone();
mat1120.setName("1120");
mat1120.selectTechnique("PreShadow", renderManager);
mat1120.setBoolean("UseInstancing", true);
Material mat1121 = matBase1.clone();
mat1121.setName("1121");
mat1121.selectTechnique("PreShadow", renderManager);
mat1121.setBoolean("UseInstancing", true);
mat1121.setTexture("DiffuseMap", tex1);
Material mat1122 = matBase1.clone();
mat1122.setName("1122");
mat1122.selectTechnique("PreShadow", renderManager);
mat1122.setBoolean("UseInstancing", true);
mat1122.setTexture("DiffuseMap", tex2);
Material mat1140 = matBase1.clone();
mat1140.setName("1140");
mat1140.selectTechnique("PreShadow", renderManager);
mat1140.setFloat("AlphaDiscardThreshold", 2f);
mat1140.setBoolean("UseInstancing", true);
Material mat1200 = matBase1.clone();
mat1200.setName("1200");
mat1200.selectTechnique("PostShadow", renderManager);
Material mat1210 = matBase1.clone();
mat1210.setName("1210");
mat1210.selectTechnique("PostShadow", renderManager);
mat1210.setFloat("AlphaDiscardThreshold", 2f);
Material mat1220 = matBase1.clone();
mat1220.setName("1220");
mat1220.selectTechnique("PostShadow", renderManager);
mat1220.setBoolean("UseInstancing", true);
Material mat2000 = matBase2.clone();
mat2000.setName("2000");
testSort(mat1100, mat1101, mat1102, mat1110,
mat1120, mat1121, mat1122, mat1140,
mat1200, mat1210, mat1220, mat2000);
}
}

@ -0,0 +1,173 @@
/*
* Copyright (c) 2009-2016 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.scene;
import com.jme3.material.MatParamOverride;
import com.jme3.math.Matrix4f;
import com.jme3.renderer.Camera;
import com.jme3.shader.VarType;
import com.jme3.texture.Texture2D;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
public class MPOTestUtils {
private static final Camera DUMMY_CAM = new Camera(640, 480);
private static final SceneGraphVisitor VISITOR = new SceneGraphVisitor() {
@Override
public void visit(Spatial spatial) {
validateSubScene(spatial);
}
};
private static void validateSubScene(Spatial scene) {
scene.checkCulling(DUMMY_CAM);
Set<MatParamOverride> actualOverrides = new HashSet<MatParamOverride>();
for (MatParamOverride override : scene.getWorldMatParamOverrides()) {
actualOverrides.add(override);
}
Set<MatParamOverride> expectedOverrides = new HashSet<MatParamOverride>();
Spatial current = scene;
while (current != null) {
for (MatParamOverride override : current.getLocalMatParamOverrides()) {
expectedOverrides.add(override);
}
current = current.getParent();
}
assertEquals("For " + scene, expectedOverrides, actualOverrides);
}
public static void validateScene(Spatial scene) {
scene.updateGeometricState();
scene.depthFirstTraversal(VISITOR);
}
public static MatParamOverride mpoInt(String name, int value) {
return new MatParamOverride(VarType.Int, name, value);
}
public static MatParamOverride mpoBool(String name, boolean value) {
return new MatParamOverride(VarType.Boolean, name, value);
}
public static MatParamOverride mpoFloat(String name, float value) {
return new MatParamOverride(VarType.Float, name, value);
}
public static MatParamOverride mpoMatrix4Array(String name, Matrix4f[] value) {
return new MatParamOverride(VarType.Matrix4Array, name, value);
}
public static MatParamOverride mpoTexture2D(String name, Texture2D texture) {
return new MatParamOverride(VarType.Texture2D, name, texture);
}
private static int getRefreshFlags(Spatial scene) {
try {
Field refreshFlagsField = Spatial.class.getDeclaredField("refreshFlags");
refreshFlagsField.setAccessible(true);
return (Integer) refreshFlagsField.get(scene);
} catch (NoSuchFieldException ex) {
throw new AssertionError(ex);
} catch (SecurityException ex) {
throw new AssertionError(ex);
} catch (IllegalArgumentException ex) {
throw new AssertionError(ex);
} catch (IllegalAccessException ex) {
throw new AssertionError(ex);
}
}
private static void dumpSceneRF(Spatial scene, String indent, boolean last, int refreshFlagsMask) {
StringBuilder sb = new StringBuilder();
sb.append(indent);
if (last) {
if (!indent.isEmpty()) {
sb.append("└─");
} else {
sb.append(" ");
}
indent += " ";
} else {
sb.append("├─");
indent += "│ ";
}
sb.append(scene.getName());
int rf = getRefreshFlags(scene) & refreshFlagsMask;
if (rf != 0) {
sb.append("(");
if ((rf & 0x1) != 0) {
sb.append("T");
}
if ((rf & 0x2) != 0) {
sb.append("B");
}
if ((rf & 0x4) != 0) {
sb.append("L");
}
if ((rf & 0x8) != 0) {
sb.append("l");
}
if ((rf & 0x10) != 0) {
sb.append("O");
}
sb.append(")");
}
if (!scene.getLocalMatParamOverrides().isEmpty()) {
sb.append(" [MPO]");
}
System.out.println(sb);
if (scene instanceof Node) {
Node node = (Node) scene;
int childIndex = 0;
for (Spatial child : node.getChildren()) {
boolean childLast = childIndex == node.getQuantity() - 1;
dumpSceneRF(child, indent, childLast, refreshFlagsMask);
childIndex++;
}
}
}
public static void dumpSceneRF(Spatial scene, int refreshFlagsMask) {
dumpSceneRF(scene, "", true, refreshFlagsMask);
}
}

@ -0,0 +1,278 @@
/*
* Copyright (c) 2009-2016 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.scene;
import com.jme3.asset.AssetManager;
import com.jme3.export.binary.BinaryExporter;
import com.jme3.material.MatParamOverride;
import org.junit.Test;
import static com.jme3.scene.MPOTestUtils.*;
import static org.junit.Assert.*;
import com.jme3.system.TestUtil;
import java.util.ArrayList;
import java.util.List;
/**
* Validates how {@link MatParamOverride MPOs} work on the scene level.
*
* @author Kirill Vainer
*/
public class SceneMatParamOverrideTest {
private static Node createDummyScene() {
Node scene = new Node("Scene Node");
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
Node d = new Node("D");
Node e = new Node("E");
Node f = new Node("F");
Node g = new Node("G");
Node h = new Node("H");
Node j = new Node("J");
scene.attachChild(a);
scene.attachChild(b);
a.attachChild(c);
a.attachChild(d);
b.attachChild(e);
b.attachChild(f);
c.attachChild(g);
c.attachChild(h);
c.attachChild(j);
return scene;
}
@Test
public void testOverrides_Empty() {
Node n = new Node("Node");
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertTrue(n.getWorldMatParamOverrides().isEmpty());
n.updateGeometricState();
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertTrue(n.getWorldMatParamOverrides().isEmpty());
}
@Test
public void testOverrides_AddRemove() {
MatParamOverride override = mpoBool("Test", true);
Node n = new Node("Node");
n.removeMatParamOverride(override);
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertTrue(n.getWorldMatParamOverrides().isEmpty());
n.addMatParamOverride(override);
assertSame(n.getLocalMatParamOverrides().get(0), override);
assertTrue(n.getWorldMatParamOverrides().isEmpty());
n.updateGeometricState();
assertSame(n.getLocalMatParamOverrides().get(0), override);
assertSame(n.getWorldMatParamOverrides().get(0), override);
n.removeMatParamOverride(override);
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertSame(n.getWorldMatParamOverrides().get(0), override);
n.updateGeometricState();
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertTrue(n.getWorldMatParamOverrides().isEmpty());
}
@Test
public void testOverrides_Clear() {
MatParamOverride override = mpoBool("Test", true);
Node n = new Node("Node");
n.clearMatParamOverrides();
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertTrue(n.getWorldMatParamOverrides().isEmpty());
n.addMatParamOverride(override);
n.clearMatParamOverrides();
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertTrue(n.getWorldMatParamOverrides().isEmpty());
n.addMatParamOverride(override);
n.updateGeometricState();
n.clearMatParamOverrides();
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertSame(n.getWorldMatParamOverrides().get(0), override);
n.updateGeometricState();
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertTrue(n.getWorldMatParamOverrides().isEmpty());
n.addMatParamOverride(override);
n.clearMatParamOverrides();
n.updateGeometricState();
assertTrue(n.getLocalMatParamOverrides().isEmpty());
assertTrue(n.getWorldMatParamOverrides().isEmpty());
}
@Test
public void testOverrides_AddAfterAttach() {
Node scene = createDummyScene();
scene.updateGeometricState();
Node root = new Node("Root Node");
root.updateGeometricState();
root.attachChild(scene);
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
validateScene(root);
}
@Test
public void testOverrides_AddBeforeAttach() {
Node scene = createDummyScene();
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
scene.updateGeometricState();
Node root = new Node("Root Node");
root.updateGeometricState();
root.attachChild(scene);
validateScene(root);
}
@Test
public void testOverrides_RemoveBeforeAttach() {
Node scene = createDummyScene();
scene.updateGeometricState();
Node root = new Node("Root Node");
root.updateGeometricState();
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
validateScene(scene);
scene.getChild("A").clearMatParamOverrides();
validateScene(scene);
root.attachChild(scene);
validateScene(root);
}
@Test
public void testOverrides_RemoveAfterAttach() {
Node scene = createDummyScene();
scene.updateGeometricState();
Node root = new Node("Root Node");
root.updateGeometricState();
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
root.attachChild(scene);
validateScene(root);
scene.getChild("A").clearMatParamOverrides();
validateScene(root);
}
@Test
public void testOverrides_IdenticalNames() {
Node scene = createDummyScene();
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
scene.getChild("C").addMatParamOverride(mpoInt("val", 7));
validateScene(scene);
}
@Test
public void testOverrides_CloningScene_DoesntCloneMPO() {
Node originalScene = createDummyScene();
originalScene.getChild("A").addMatParamOverride(mpoInt("int", 5));
originalScene.getChild("A").addMatParamOverride(mpoBool("bool", true));
originalScene.getChild("A").addMatParamOverride(mpoFloat("float", 3.12f));
Node clonedScene = originalScene.clone(false);
validateScene(clonedScene);
validateScene(originalScene);
List<MatParamOverride> clonedOverrides = clonedScene.getChild("A").getLocalMatParamOverrides();
List<MatParamOverride> originalOverrides = originalScene.getChild("A").getLocalMatParamOverrides();
assertNotSame(clonedOverrides, originalOverrides);
assertEquals(clonedOverrides, originalOverrides);
for (int i = 0; i < clonedOverrides.size(); i++) {
assertNotSame(clonedOverrides.get(i), originalOverrides.get(i));
assertEquals(clonedOverrides.get(i), originalOverrides.get(i));
}
}
@Test
public void testOverrides_SaveAndLoad_KeepsMPOs() {
MatParamOverride override = mpoInt("val", 5);
Node scene = createDummyScene();
scene.getChild("A").addMatParamOverride(override);
AssetManager assetManager = TestUtil.createAssetManager();
Node loadedScene = BinaryExporter.saveAndLoad(assetManager, scene);
Node root = new Node("Root Node");
root.attachChild(loadedScene);
validateScene(root);
validateScene(scene);
assertNotSame(override, loadedScene.getChild("A").getLocalMatParamOverrides().get(0));
assertEquals(override, loadedScene.getChild("A").getLocalMatParamOverrides().get(0));
}
@Test
public void testEquals() {
assertEquals(mpoInt("val", 5), mpoInt("val", 5));
assertEquals(mpoBool("val", true), mpoBool("val", true));
assertNotEquals(mpoInt("val", 5), mpoInt("val", 6));
assertNotEquals(mpoInt("val1", 5), mpoInt("val2", 5));
assertNotEquals(mpoBool("val", true), mpoInt("val", 1));
}
}

@ -0,0 +1,300 @@
/*
* 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.shader;
import com.jme3.math.FastMath;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.junit.Test;
import static org.junit.Assert.*;
public class DefineListTest {
private static final List<String> DEFINE_NAMES = Arrays.asList("BOOL_VAR", "INT_VAR", "FLOAT_VAR");
private static final List<VarType> DEFINE_TYPES = Arrays.asList(VarType.Boolean, VarType.Int, VarType.Float);
private static final int NUM_DEFINES = DEFINE_NAMES.size();
private static final int BOOL_VAR = 0;
private static final int INT_VAR = 1;
private static final int FLOAT_VAR = 2;
private static final DefineList EMPTY = new DefineList(NUM_DEFINES);
@Test
public void testHashCollision() {
DefineList dl1 = new DefineList(64);
DefineList dl2 = new DefineList(64);
// Try to cause a hash collision
// (since bit #32 is aliased to bit #1 in 32-bit ints)
dl1.set(0, 123);
dl1.set(32, 0);
dl2.set(32, 0);
dl2.set(0, 123);
assert dl1.hashCode() == dl2.hashCode();
assert dl1.equals(dl2);
}
@Test
public void testGetSet() {
DefineList dl = new DefineList(NUM_DEFINES);
assertFalse(dl.getBoolean(BOOL_VAR));
assertEquals(dl.getInt(INT_VAR), 0);
assertEquals(dl.getFloat(FLOAT_VAR), 0f, 0f);
dl.set(BOOL_VAR, true);
dl.set(INT_VAR, -1);
dl.set(FLOAT_VAR, Float.NaN);
assertTrue(dl.getBoolean(BOOL_VAR));
assertEquals(dl.getInt(INT_VAR), -1);
assertTrue(Float.isNaN(dl.getFloat(FLOAT_VAR)));
}
private String generateSource(DefineList dl) {
StringBuilder sb = new StringBuilder();
dl.generateSource(sb, DEFINE_NAMES, DEFINE_TYPES);
return sb.toString();
}
@Test
public void testSourceInitial() {
DefineList dl = new DefineList(NUM_DEFINES);
assert dl.hashCode() == 0;
assert generateSource(dl).equals("");
}
@Test
public void testSourceBooleanDefine() {
DefineList dl = new DefineList(NUM_DEFINES);
dl.set(BOOL_VAR, true);
assert dl.hashCode() == 1;
assert generateSource(dl).equals("#define BOOL_VAR 1\n");
dl.set(BOOL_VAR, false);
assert dl.hashCode() == 0;
assert generateSource(dl).equals("");
}
@Test
public void testSourceIntDefine() {
DefineList dl = new DefineList(NUM_DEFINES);
int hashCodeWithInt = 1 << INT_VAR;
dl.set(INT_VAR, 123);
assert dl.hashCode() == hashCodeWithInt;
assert generateSource(dl).equals("#define INT_VAR 123\n");
dl.set(INT_VAR, 0);
assert dl.hashCode() == 0;
assert generateSource(dl).equals("");
dl.set(INT_VAR, -99);
assert dl.hashCode() == hashCodeWithInt;
assert generateSource(dl).equals("#define INT_VAR -99\n");
dl.set(INT_VAR, Integer.MAX_VALUE);
assert dl.hashCode() == hashCodeWithInt;
assert generateSource(dl).equals("#define INT_VAR 2147483647\n");
}
@Test
public void testSourceFloatDefine() {
DefineList dl = new DefineList(NUM_DEFINES);
dl.set(FLOAT_VAR, 1f);
assert dl.hashCode() == (1 << FLOAT_VAR);
assert generateSource(dl).equals("#define FLOAT_VAR 1.0\n");
dl.set(FLOAT_VAR, 0f);
assert dl.hashCode() == 0;
assert generateSource(dl).equals("");
dl.set(FLOAT_VAR, -1f);
assert generateSource(dl).equals("#define FLOAT_VAR -1.0\n");
dl.set(FLOAT_VAR, FastMath.FLT_EPSILON);
assert generateSource(dl).equals("#define FLOAT_VAR 1.1920929E-7\n");
dl.set(FLOAT_VAR, FastMath.PI);
assert generateSource(dl).equals("#define FLOAT_VAR 3.1415927\n");
try {
dl.set(FLOAT_VAR, Float.NaN);
generateSource(dl);
assert false;
} catch (IllegalArgumentException ex) { }
try {
dl.set(FLOAT_VAR, Float.POSITIVE_INFINITY);
generateSource(dl);
assert false;
} catch (IllegalArgumentException ex) { }
try {
dl.set(FLOAT_VAR, Float.NEGATIVE_INFINITY);
generateSource(dl);
assert false;
} catch (IllegalArgumentException ex) { }
}
@Test
public void testEqualsAndHashCode() {
DefineList dl1 = new DefineList(NUM_DEFINES);
DefineList dl2 = new DefineList(NUM_DEFINES);
assertTrue(dl1.hashCode() == 0);
assertEquals(dl1, dl2);
dl1.set(BOOL_VAR, true);
assertTrue(dl1.hashCode() == 1);
assertNotSame(dl1, dl2);
dl2.set(BOOL_VAR, true);
assertEquals(dl1, dl2);
dl1.set(INT_VAR, 2);
assertTrue(dl1.hashCode() == (1|2));
assertNotSame(dl1, dl2);
dl2.set(INT_VAR, 2);
assertEquals(dl1, dl2);
dl1.set(BOOL_VAR, false);
assertTrue(dl1.hashCode() == 2);
assertNotSame(dl1, dl2);
}
@Test
public void testDeepClone() {
DefineList dl1 = new DefineList(NUM_DEFINES);
DefineList dl2 = dl1.deepClone();
assertFalse(dl1 == dl2);
assertTrue(dl1.equals(dl2));
assertTrue(dl1.hashCode() == dl2.hashCode());
dl1.set(BOOL_VAR, true);
dl2 = dl1.deepClone();
assertTrue(dl1.equals(dl2));
assertTrue(dl1.hashCode() == dl2.hashCode());
dl1.set(INT_VAR, 123);
assertFalse(dl1.equals(dl2));
assertFalse(dl1.hashCode() == dl2.hashCode());
dl2 = dl1.deepClone();
assertTrue(dl1.equals(dl2));
assertTrue(dl1.hashCode() == dl2.hashCode());
}
@Test
public void testGenerateSource() {
DefineList dl = new DefineList(NUM_DEFINES);
assertEquals("", generateSource(dl));
dl.set(BOOL_VAR, true);
assertEquals("#define BOOL_VAR 1\n", generateSource(dl));
dl.set(INT_VAR, 123);
assertEquals("#define BOOL_VAR 1\n" +
"#define INT_VAR 123\n", generateSource(dl));
dl.set(BOOL_VAR, false);
assertEquals("#define INT_VAR 123\n", generateSource(dl));
dl.set(BOOL_VAR, true);
// should have predictable ordering based on defineId
assertEquals("#define BOOL_VAR 1\n" +
"#define INT_VAR 123\n", generateSource(dl));
}
private static String doLookup(HashMap<DefineList, String> map, boolean boolVal, int intVal, float floatVal) {
DefineList dl = new DefineList(NUM_DEFINES);
dl.set(BOOL_VAR, boolVal);
dl.set(INT_VAR, intVal);
dl.set(FLOAT_VAR, floatVal);
return map.get(dl);
}
@Test
public void testHashLookup() {
String STR_EMPTY = "This is an empty define list";
String STR_INT = "This define list has an int value";
String STR_BOOL = "This define list just has boolean value set";
String STR_BOOL_INT = "This define list has both a boolean and int value";
String STR_BOOL_INT_FLOAT = "This define list has a boolean, int, and float value";
HashMap<DefineList, String> map = new HashMap<DefineList, String>();
DefineList lookup = new DefineList(NUM_DEFINES);
map.put(lookup.deepClone(), STR_EMPTY);
lookup.set(BOOL_VAR, true);
map.put(lookup.deepClone(), STR_BOOL);
lookup.set(BOOL_VAR, false);
lookup.set(INT_VAR, 123);
map.put(lookup.deepClone(), STR_INT);
lookup.set(BOOL_VAR, true);
map.put(lookup.deepClone(), STR_BOOL_INT);
lookup.set(FLOAT_VAR, FastMath.PI);
map.put(lookup.deepClone(), STR_BOOL_INT_FLOAT);
assertEquals(doLookup(map, false, 0, 0f), STR_EMPTY);
assertEquals(doLookup(map, false, 123, 0f), STR_INT);
assertEquals(doLookup(map, true, 0, 0f), STR_BOOL);
assertEquals(doLookup(map, true, 123, 0f), STR_BOOL_INT);
assertEquals(doLookup(map, true, 123, FastMath.PI), STR_BOOL_INT_FLOAT);
}
}

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 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
@ -29,48 +29,50 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.effect; package com.jme3.system;
import com.jme3.renderer.Camera; import com.jme3.audio.AudioRenderer;
import java.util.Comparator; import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.nio.ByteBuffer;
@Deprecated public class MockJmeSystemDelegate extends JmeSystemDelegate {
class ParticleComparator implements Comparator<Particle> {
private Camera cam; @Override
public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException {
}
public void setCamera(Camera cam){ @Override
this.cam = cam; public void showErrorDialog(String message) {
} }
public int compare(Particle p1, Particle p2) { @Override
return 0; // unused public boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry) {
/* return false;
if (p1.life <= 0 || p2.life <= 0) }
return 0;
// if (p1.life <= 0) @Override
// return 1; public URL getPlatformAssetConfigURL() {
// else if (p2.life <= 0) return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/General.cfg");
// return -1; }
float d1 = p1.distToCam, d2 = p2.distToCam; @Override
public JmeContext newContext(AppSettings settings, JmeContext.Type contextType) {
return null;
}
if (d1 == -1){ @Override
d1 = cam.distanceToNearPlane(p1.position); public AudioRenderer newAudioRenderer(AppSettings settings) {
p1.distToCam = d1; return null;
} }
if (d2 == -1){
d2 = cam.distanceToNearPlane(p2.position); @Override
p2.distToCam = d2; public void initialize(AppSettings settings) {
} }
if (d1 < d2) @Override
return 1; public void showSoftKeyboard(boolean show) {
else if (d1 > d2)
return -1;
else
return 0;
*/
} }
}
}

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2012 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
@ -29,27 +29,27 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.terrain.heightmap; package com.jme3.system;
/** import com.jme3.asset.AssetConfig;
* A heightmap that is built off an image. import com.jme3.asset.AssetManager;
* If you want to be able to supply different Image types to import com.jme3.asset.DesktopAssetManager;
* ImageBaseHeightMapGrid, you need to implement this interface, import com.jme3.renderer.RenderManager;
* and have that class extend Abstract heightmap. import java.util.logging.Level;
* import java.util.logging.Logger;
* @author bowens
* @deprecated public class TestUtil {
*/
public interface ImageHeightmap { static {
JmeSystem.setSystemDelegate(new MockJmeSystemDelegate());
}
/** public static AssetManager createAssetManager() {
* Set the image to use for this heightmap Logger.getLogger(AssetConfig.class.getName()).setLevel(Level.OFF);
*/ return new DesktopAssetManager(true);
//public void setImage(Image image); }
/** public static RenderManager createRenderManager() {
* The BufferedImage.TYPE_ that is supported return new RenderManager(new NullRenderer());
* by this ImageHeightmap }
*/
//public int getSupportedImageType();
} }

@ -0,0 +1,6 @@
MaterialDef Test Material {
Technique Test {
VertexShader GLSL100 : test.vert
FragmentShader GLSL100 : test.frag
}
}

@ -0,0 +1,8 @@
MaterialDef Test Material {
Technique A {
}
Technique B {
VertexShader GLSL100 : test.vert
FragmentShader GLSL100 : test.frag
}
}

@ -0,0 +1,10 @@
MaterialDef Test Material {
Technique Test {
VertexShader GLSL150 : test150.vert
FragmentShader GLSL150 : test150.frag
}
Technique Test {
VertexShader GLSL100 : test.vert
FragmentShader GLSL100 : test.frag
}
}

@ -0,0 +1,34 @@
MaterialDef Simple {
MaterialParameters {
Color Color
}
Technique {
WorldParameters {
WorldViewProjectionMatrix
}
VertexShaderNodes {
ShaderNode CommonVert {
Definition : CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn
InputMappings {
worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix
modelPosition = Global.position.xyz
}
OutputMappings {
Global.position = projPosition
}
}
}
FragmentShaderNodes {
ShaderNode ColorMult {
Definition : ColorMult : Common/MatDefs/ShaderNodes/Basic/ColorMult.j3sn
InputMappings {
color1 = MatParam.Color
color2 = Global.color
}
OutputMappings {
Global.color = outColor
}
}
}
}
}

@ -504,7 +504,6 @@ public class TextureAtlas {
geom.setMesh(mesh); geom.setMesh(mesh);
Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md"); Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md");
mat.getAdditionalRenderState().setAlphaTest(true);
Texture diffuseMap = atlas.getAtlasTexture("DiffuseMap"); Texture diffuseMap = atlas.getAtlasTexture("DiffuseMap");
Texture normalMap = atlas.getAtlasTexture("NormalMap"); Texture normalMap = atlas.getAtlasTexture("NormalMap");
Texture specularMap = atlas.getAtlasTexture("SpecularMap"); Texture specularMap = atlas.getAtlasTexture("SpecularMap");

@ -6,11 +6,12 @@ import com.jme3.asset.plugins.FileLocator;
import com.jme3.material.MaterialDef; import com.jme3.material.MaterialDef;
import com.jme3.material.TechniqueDef; import com.jme3.material.TechniqueDef;
import com.jme3.material.plugins.J3MLoader; import com.jme3.material.plugins.J3MLoader;
import com.jme3.renderer.Caps;
import com.jme3.shader.DefineList; import com.jme3.shader.DefineList;
import com.jme3.shader.Shader; import com.jme3.shader.Shader;
import com.jme3.shader.ShaderKey;
import com.jme3.shader.plugins.GLSLLoader; import com.jme3.shader.plugins.GLSLLoader;
import com.jme3.system.JmeSystem; import com.jme3.system.JmeSystem;
import java.util.EnumSet;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -33,23 +34,22 @@ public class ShaderCheck {
assetManager.registerLoader(GLSLLoader.class, "vert", "frag","geom","tsctrl","tseval","glsllib"); assetManager.registerLoader(GLSLLoader.class, "vert", "frag","geom","tsctrl","tseval","glsllib");
} }
private static void checkMatDef(String matdefName){ private static void checkMatDef(String matdefName) {
MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName); MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName);
for (TechniqueDef techDef : def.getDefaultTechniques()){ EnumSet<Caps> rendererCaps = EnumSet.noneOf(Caps.class);
DefineList dl = new DefineList(); rendererCaps.add(Caps.GLSL100);
dl.addFrom(techDef.getShaderPresetDefines()); for (TechniqueDef techDef : def.getTechniqueDefs(TechniqueDef.DEFAULT_TECHNIQUE_NAME)) {
ShaderKey shaderKey = new ShaderKey(dl,techDef.getShaderProgramLanguages(),techDef.getShaderProgramNames()); DefineList defines = techDef.createDefineList();
Shader shader = techDef.getShader(assetManager, rendererCaps, defines);
Shader shader = assetManager.loadShader(shaderKey); for (Validator validator : validators) {
for (Validator validator : validators){
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
validator.validate(shader, sb); validator.validate(shader, sb);
System.out.println("==== Validator: " + validator.getName() + " " + System.out.println("==== Validator: " + validator.getName() + " "
validator.getInstalledVersion() + " ===="); + validator.getInstalledVersion() + " ====");
System.out.println(sb.toString()); System.out.println(sb.toString());
} }
} }
throw new UnsupportedOperationException();
} }
public static void main(String[] args){ public static void main(String[] args){

@ -43,6 +43,7 @@ import com.jme3.system.JmeContext.Type;
import com.jme3.util.Screenshots; import com.jme3.util.Screenshots;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints; import java.awt.RenderingHints;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp; import java.awt.image.AffineTransformOp;
@ -116,12 +117,16 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
@Override @Override
public void showErrorDialog(String message) { public void showErrorDialog(String message) {
final String msg = message; if (!GraphicsEnvironment.isHeadless()) {
EventQueue.invokeLater(new Runnable() { final String msg = message;
public void run() { EventQueue.invokeLater(new Runnable() {
ErrorDialog.showDialog(msg); public void run() {
} ErrorDialog.showDialog(msg);
}); }
});
} else {
System.err.println("[JME ERROR] " + message);
}
} }
@Override @Override
@ -129,6 +134,9 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
if (SwingUtilities.isEventDispatchThread()) { if (SwingUtilities.isEventDispatchThread()) {
throw new IllegalStateException("Cannot run from EDT"); throw new IllegalStateException("Cannot run from EDT");
} }
if (GraphicsEnvironment.isHeadless()) {
throw new IllegalStateException("Cannot show dialog in headless environment");
}
final AppSettings settings = new AppSettings(false); final AppSettings settings = new AppSettings(false);
settings.copyFrom(sourceSettings); settings.copyFrom(sourceSettings);
@ -333,27 +341,13 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
if (initialized) { if (initialized) {
return; return;
} }
initialized = true; initialized = true;
try { logger.log(Level.INFO, getBuildInfo());
if (!lowPermissions) { if (!lowPermissions) {
// can only modify logging settings if (NativeLibraryLoader.isUsingNativeBullet()) {
// if permissions are available NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
// JmeFormatter formatter = new JmeFormatter();
// Handler fileHandler = new FileHandler("jme.log");
// fileHandler.setFormatter(formatter);
// Logger.getLogger("").addHandler(fileHandler);
// Handler consoleHandler = new ConsoleHandler();
// consoleHandler.setFormatter(formatter);
// Logger.getLogger("").removeHandler(Logger.getLogger("").getHandlers()[0]);
// Logger.getLogger("").addHandler(consoleHandler);
} }
// } catch (IOException ex){
// logger.log(Level.SEVERE, "I/O Error while creating log file", ex);
} catch (SecurityException ex) {
logger.log(Level.SEVERE, "Security error in creating log file", ex);
} }
logger.log(Level.INFO, getBuildInfo());
} }
@Override @Override

@ -1,359 +0,0 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.system;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Helper class for extracting the natives (dll, so) from the jars.
* This class should only be used internally.
*
* @deprecated Use {@link NativeLibraryLoader} instead.
*/
@Deprecated
public final class Natives {
private static final Logger logger = Logger.getLogger(Natives.class.getName());
private static final byte[] buf = new byte[1024 * 100];
private static File extractionDirOverride = null;
private static File extractionDir = null;
public static void setExtractionDir(String name) {
extractionDirOverride = new File(name).getAbsoluteFile();
}
public static File getExtractionDir() {
if (extractionDirOverride != null) {
return extractionDirOverride;
}
if (extractionDir == null) {
File workingFolder = new File("").getAbsoluteFile();
if (!workingFolder.canWrite()) {
setStorageExtractionDir();
} else {
try {
File file = new File(workingFolder.getAbsolutePath() + File.separator + ".jmetestwrite");
file.createNewFile();
file.delete();
extractionDir = workingFolder;
} catch (Exception e) {
setStorageExtractionDir();
}
}
}
return extractionDir;
}
private static void setStorageExtractionDir() {
logger.log(Level.WARNING, "Working directory is not writable. Using home directory instead.");
extractionDir = new File(JmeSystem.getStorageFolder(),
"natives_" + Integer.toHexString(computeNativesHash()));
if (!extractionDir.exists()) {
extractionDir.mkdir();
}
}
private static int computeNativesHash() {
URLConnection conn = null;
try {
String classpath = System.getProperty("java.class.path");
URL url = Thread.currentThread().getContextClassLoader().getResource("com/jme3/system/Natives.class");
StringBuilder sb = new StringBuilder(url.toString());
if (sb.indexOf("jar:") == 0) {
sb.delete(0, 4);
sb.delete(sb.indexOf("!"), sb.length());
sb.delete(sb.lastIndexOf("/") + 1, sb.length());
}
try {
url = new URL(sb.toString());
} catch (MalformedURLException ex) {
throw new UnsupportedOperationException(ex);
}
conn = url.openConnection();
int hash = classpath.hashCode() ^ (int) conn.getLastModified();
return hash;
} catch (IOException ex) {
throw new UnsupportedOperationException(ex);
} finally {
if (conn != null) {
try {
conn.getInputStream().close();
conn.getOutputStream().close();
} catch (IOException ex) { }
}
}
}
public static void extractNativeLib(String sysName, String name) throws IOException {
extractNativeLib(sysName, name, false, true);
}
public static void extractNativeLib(String sysName, String name, boolean load) throws IOException {
extractNativeLib(sysName, name, load, true);
}
public static void extractNativeLib(String sysName, String name, boolean load, boolean warning) throws IOException {
String fullname;
String path;
//XXX: hack to allow specifying the extension via supplying an extension in the name (e.g. "blah.dylib")
// this is needed on osx where the openal.dylib always needs to be extracted as dylib
// and never as jnilib, even if that is the platform JNI lib suffix (OpenAL is no JNI library)
if(!name.contains(".")){
// automatic name mapping
fullname = System.mapLibraryName(name);
path = "native/" + sysName + "/" + fullname;
//XXX: Hack to extract jnilib to dylib on OSX Java 1.7+
// This assumes all jni libs for osx are stored as "jnilib" in the jar file.
// It will be extracted with the name required for the platform.
// At a later stage this should probably inverted so that dylib is the default name.
if(sysName.equals("macosx")){
path = path.replaceAll("dylib","jnilib");
}
} else{
fullname = name;
path = "native/" + sysName + "/" + fullname;
}
URL url = Thread.currentThread().getContextClassLoader().getResource(path);
// Also check for binaries that are not packed in folders by jme team, e.g. maven artifacts
if(url == null){
path = fullname;
if(sysName.equals("macosx") && !name.contains(".")){
path = path.replaceAll("dylib","jnilib");
}
url = Thread.currentThread().getContextClassLoader().getResource(path);
}
if(url == null){
if (!warning) {
logger.log(Level.WARNING, "Cannot locate native library in classpath: {0}/{1}",
new String[]{sysName, fullname});
}
// Still try loading the library without a filename, maybe it is
// accessible otherwise
try{
System.loadLibrary(name);
} catch(UnsatisfiedLinkError e){
if (!warning) {
logger.log(Level.WARNING, "Cannot load native library: {0}/{1}",
new String[]{sysName, fullname});
}
}
return;
}
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream();
File targetFile = new File(getExtractionDir(), fullname);
OutputStream out = null;
try {
if (targetFile.exists()) {
// OK, compare last modified date of this file to
// file in jar
long targetLastModified = targetFile.lastModified();
long sourceLastModified = conn.getLastModified();
// Allow ~1 second range for OSes that only support low precision
if (targetLastModified + 1000 > sourceLastModified) {
logger.log(Level.FINE, "Not copying library {0}. Latest already extracted.", fullname);
return;
}
}
out = new FileOutputStream(targetFile);
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
in = null;
out.close();
out = null;
// NOTE: On OSes that support "Date Created" property,
// this will cause the last modified date to be lower than
// date created which makes no sense
targetFile.setLastModified(conn.getLastModified());
} catch (FileNotFoundException ex) {
if (ex.getMessage().contains("used by another process")) {
return;
}
throw ex;
} finally {
if (load) {
System.load(targetFile.getAbsolutePath());
}
if(in != null){
in.close();
}
if(out != null){
out.close();
}
}
logger.log(Level.FINE, "Copied {0} to {1}", new Object[]{fullname, targetFile});
}
protected static boolean isUsingNativeBullet() {
try {
Class clazz = Class.forName("com.jme3.bullet.util.NativeMeshUtil");
return clazz != null;
} catch (ClassNotFoundException ex) {
return false;
}
}
public static void extractNativeLibs(Platform platform, AppSettings settings) throws IOException {
if (true) {
throw new UnsupportedEncodingException("Now, why would you EVER want to do that?");
}
String renderer = settings.getRenderer();
String audioRenderer = settings.getAudioRenderer();
boolean needLWJGL = false;
boolean needOAL = false;
boolean needJInput = false;
boolean needNativeBullet = isUsingNativeBullet();
if (renderer != null) {
if (renderer.startsWith("LWJGL")) {
needLWJGL = true;
}
}
if (audioRenderer != null) {
if (audioRenderer.equals("LWJGL")) {
needLWJGL = true;
needOAL = true;
}
}
needJInput = settings.useJoysticks();
String libraryPath = getExtractionDir().toString();
if (needLWJGL) {
logger.log(Level.INFO, "Extraction Directory: {0}", getExtractionDir().toString());
// LWJGL supports this feature where
// it can load libraries from this path.
System.setProperty("org.lwjgl.librarypath", libraryPath);
}
if (needJInput) {
// AND Luckily enough JInput supports the same feature.
System.setProperty("net.java.games.input.librarypath", libraryPath);
}
switch (platform) {
case Windows64:
if (needLWJGL) {
extractNativeLib("windows", "lwjgl64");
}
if (needOAL) {
extractNativeLib("windows", "OpenAL64", true, false);
}
if (needJInput) {
extractNativeLib("windows", "jinput-dx8_64");
extractNativeLib("windows", "jinput-raw_64");
}
if (needNativeBullet) {
extractNativeLib("windows", "bulletjme64", true, false);
}
break;
case Windows32:
if (needLWJGL) {
extractNativeLib("windows", "lwjgl");
}
if (needOAL) {
extractNativeLib("windows", "OpenAL32", true, false);
}
if (needJInput) {
extractNativeLib("windows", "jinput-dx8");
extractNativeLib("windows", "jinput-raw");
}
if (needNativeBullet) {
extractNativeLib("windows", "bulletjme", true, false);
}
break;
case Linux64:
if (needLWJGL) {
extractNativeLib("linux", "lwjgl64");
}
if (needJInput) {
extractNativeLib("linux", "jinput-linux64");
}
if (needOAL) {
extractNativeLib("linux", "openal64");
}
if (needNativeBullet) {
extractNativeLib("linux", "bulletjme64", true, false);
}
break;
case Linux32:
if (needLWJGL) {
extractNativeLib("linux", "lwjgl");
}
if (needJInput) {
extractNativeLib("linux", "jinput-linux");
}
if (needOAL) {
extractNativeLib("linux", "openal");
}
if (needNativeBullet) {
extractNativeLib("linux", "bulletjme", true, false);
}
break;
case MacOSX_PPC32:
case MacOSX32:
case MacOSX_PPC64:
case MacOSX64:
if (needLWJGL) {
extractNativeLib("macosx", "lwjgl");
}
if (needOAL){
extractNativeLib("macosx", "openal.dylib");
}
if (needJInput) {
extractNativeLib("macosx", "jinput-osx");
}
if (needNativeBullet) {
extractNativeLib("macosx", "bulletjme", true, false);
}
break;
}
}
}

@ -12,7 +12,9 @@ task run(dependsOn: 'build', type:JavaExec) {
jvmArgs "-Djava.awt.headless=true" jvmArgs "-Djava.awt.headless=true"
} }
systemProperty "java.util.logging.config.file", System.getProperty("java.util.logging.config.file") if (System.properties['java.util.logging.config.file'] != null) {
systemProperty "java.util.logging.config.file", System.properties['java.util.logging.config.file']
}
if( assertions == "true" ){ if( assertions == "true" ){
enableAssertions = true; enableAssertions = true;

@ -139,7 +139,8 @@ public class TestCameraMotionPath extends SimpleApplication {
rootNode.attachChild(teapot); rootNode.attachChild(teapot);
Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -1.0f, 0), 50, 1, 50)); Geometry soil = new Geometry("soil", new Box(50, 1, 50));
soil.setLocalTranslation(0, -1, 0);
soil.setMaterial(matSoil); soil.setMaterial(matSoil);
rootNode.attachChild(soil); rootNode.attachChild(soil);
DirectionalLight light = new DirectionalLight(); DirectionalLight light = new DirectionalLight();

@ -222,7 +222,8 @@ public class TestCinematic extends SimpleApplication {
matSoil.setColor("Diffuse", ColorRGBA.Green); matSoil.setColor("Diffuse", ColorRGBA.Green);
matSoil.setColor("Specular", ColorRGBA.Black); matSoil.setColor("Specular", ColorRGBA.Black);
Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -6.0f, 0), 50, 1, 50)); Geometry soil = new Geometry("soil", new Box(50, 1, 50));
soil.setLocalTranslation(0, -6, 0);
soil.setMaterial(matSoil); soil.setMaterial(matSoil);
soil.setShadowMode(ShadowMode.Receive); soil.setShadowMode(ShadowMode.Receive);
rootNode.attachChild(soil); rootNode.attachChild(soil);

@ -136,7 +136,8 @@ public class TestMotionPath extends SimpleApplication {
rootNode.attachChild(teapot); rootNode.attachChild(teapot);
Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -1.0f, 0), 50, 1, 50)); Geometry soil = new Geometry("soil", new Box(50, 1, 50));
soil.setLocalTranslation(0, -1, 0);
soil.setMaterial(matSoil); soil.setMaterial(matSoil);
rootNode.attachChild(soil); rootNode.attachChild(soil);

@ -57,7 +57,7 @@ public class TestAppStateLifeCycle extends SimpleApplication {
@Override @Override
public void simpleInitApp() { public void simpleInitApp() {
Box b = new Box(Vector3f.ZERO, 1, 1, 1); Box b = new Box(1, 1, 1);
Geometry geom = new Geometry("Box", b); Geometry geom = new Geometry("Box", b);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));

@ -56,7 +56,7 @@ public class TestBareBonesApp extends LegacyApplication {
System.out.println("Initialize"); System.out.println("Initialize");
// create a box // create a box
boxGeom = new Geometry("Box", new Box(Vector3f.ZERO, 2, 2, 2)); boxGeom = new Geometry("Box", new Box(2, 2, 2));
// load some default material // load some default material
boxGeom.setMaterial(assetManager.loadMaterial("Interface/Logo/Logo.j3m")); boxGeom.setMaterial(assetManager.loadMaterial("Interface/Logo/Logo.j3m"));

@ -1,156 +0,0 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jme3test.app;
import com.jme3.system.NativeLibraryLoader;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Try to load some natives.
*
* @author Kirill Vainer
*/
public class TestNativeLoader {
private static final File WORKING_FOLDER = new File(System.getProperty("user.dir"));
private static void tryLoadLwjgl() {
NativeLibraryLoader.loadNativeLibrary("lwjgl", true);
System.out.println("Succeeded in loading LWJGL.\n\tVersion: " +
org.lwjgl.Sys.getVersion());
}
private static void tryLoadJinput() {
NativeLibraryLoader.loadNativeLibrary("jinput", true);
NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true);
net.java.games.input.ControllerEnvironment ce =
net.java.games.input.ControllerEnvironment.getDefaultEnvironment();
if (ce.isSupported()) {
net.java.games.input.Controller[] c =
ce.getControllers();
System.out.println("Succeeded in loading JInput.\n\tVersion: " +
net.java.games.util.Version.getVersion());
}
}
private static void tryLoadOpenAL() {
NativeLibraryLoader.loadNativeLibrary("openal", true);
try {
org.lwjgl.openal.AL.create();
String renderer = org.lwjgl.openal.AL10.alGetString(org.lwjgl.openal.AL10.AL_RENDERER);
String vendor = org.lwjgl.openal.AL10.alGetString(org.lwjgl.openal.AL10.AL_VENDOR);
String version = org.lwjgl.openal.AL10.alGetString(org.lwjgl.openal.AL10.AL_VERSION);
System.out.println("Succeeded in loading OpenAL.");
System.out.println("\tVersion: " + version);
} catch (org.lwjgl.LWJGLException ex) {
throw new RuntimeException(ex);
} finally {
if (org.lwjgl.openal.AL.isCreated()) {
org.lwjgl.openal.AL.destroy();
}
}
}
private static void tryLoadOpenGL() {
org.lwjgl.opengl.Pbuffer pb = null;
try {
pb = new org.lwjgl.opengl.Pbuffer(1, 1, new org.lwjgl.opengl.PixelFormat(0, 0, 0), null);
pb.makeCurrent();
String version = org.lwjgl.opengl.GL11.glGetString(org.lwjgl.opengl.GL11.GL_VERSION);
System.out.println("Succeeded in loading OpenGL.\n\tVersion: " + version);
} catch (org.lwjgl.LWJGLException ex) {
throw new RuntimeException(ex);
} finally {
if (pb != null) {
pb.destroy();
}
}
}
private static void tryLoadBulletJme() {
if (NativeLibraryLoader.isUsingNativeBullet()) {
NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
com.jme3.bullet.PhysicsSpace physSpace = new com.jme3.bullet.PhysicsSpace();
System.out.println("Succeeded in loading BulletJme.");
} else {
System.out.println("Native bullet not included. Cannot test loading.");
}
}
private static void cleanupNativesFolder(File folder) {
for (File file : folder.listFiles()) {
String lowerCaseName = file.getName().toLowerCase();
if (lowerCaseName.contains("lwjgl") ||
lowerCaseName.contains("jinput") ||
lowerCaseName.contains("openal") ||
lowerCaseName.contains("bulletjme")) {
file.delete();
}
}
}
public static void main(String[] args) {
Logger.getLogger("").getHandlers()[0].setLevel(Level.WARNING);
Logger.getLogger(NativeLibraryLoader.class.getName()).setLevel(Level.ALL);
// Get a bit more output from LWJGL about issues.
// System.setProperty("org.lwjgl.util.Debug", "true");
// Extracting to working folder is no brainer.
// Choose some random path, then load LWJGL.
File customNativesFolder = new File("CustomNativesFolder");
customNativesFolder.mkdirs();
if (!customNativesFolder.isDirectory()) {
throw new IllegalStateException("Failed to make custom natives folder");
}
// Let's cleanup our folders first.
cleanupNativesFolder(WORKING_FOLDER);
cleanupNativesFolder(customNativesFolder);
NativeLibraryLoader.setCustomExtractionFolder(customNativesFolder.getAbsolutePath());
tryLoadLwjgl();
tryLoadOpenGL();
tryLoadOpenAL();
tryLoadJinput();
tryLoadBulletJme();
}
}

@ -50,7 +50,7 @@ public class TestReleaseDirectMemory extends SimpleApplication {
@Override @Override
public void simpleInitApp() { public void simpleInitApp() {
Box b = new Box(Vector3f.ZERO, 1, 1, 1); Box b = new Box(1, 1, 1);
Geometry geom = new Geometry("Box", b); Geometry geom = new Geometry("Box", b);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));

@ -69,7 +69,7 @@ public class TestAmbient extends SimpleApplication {
nature.play(); nature.play();
// just a blue box to mark the spot // just a blue box to mark the spot
Box box1 = new Box(Vector3f.ZERO, .5f, .5f, .5f); Box box1 = new Box(.5f, .5f, .5f);
Geometry player = new Geometry("Player", box1); Geometry player = new Geometry("Player", box1);
Material mat1 = new Material(assetManager, Material mat1 = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md"); "Common/MatDefs/Misc/Unshaded.j3md");

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

Loading…
Cancel
Save