Merge remote-tracking branch 'origin/master'
This commit is contained in:
		
						commit
						e837336116
					
				
							
								
								
									
										144
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										144
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,126 +1,11 @@ | ||||
| **/nbproject/private/ | ||||
| /.gradle/ | ||||
| /.nb-gradle/private/ | ||||
| /.nb-gradle/profiles/private/ | ||||
| /.nb-gradle/ | ||||
| /.idea/ | ||||
| /dist/ | ||||
| /build/ | ||||
| /bin/ | ||||
| /netbeans/ | ||||
| /sdk/jdks/local/ | ||||
| /jme3-core/build/ | ||||
| /jme3-core/src/main/resources/com/jme3/system/version.properties | ||||
| /jme3-plugins/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-2.82-r2704/ | ||||
| /jme3-android-native/openal-soft/ | ||||
| /jme3-android-native/OpenALSoft.zip | ||||
| /jme3-android-native/src/native/jme_decode/STBI/ | ||||
| /jme3-android-native/src/native/jme_decode/Tremor/ | ||||
| /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/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 | ||||
| @ -130,21 +15,20 @@ | ||||
| *.dylib | ||||
| *.iml | ||||
| .DS_Store | ||||
| /sdk/dist/ | ||||
| /jme3-core/src/main/resources/com/jme3/system/version.properties | ||||
| /jme3-*/build/ | ||||
| /jme3-bullet-native/bullet.zip | ||||
| /jme3-bullet-native/bullet-2.82-r2704/ | ||||
| /jme3-android-native/openal-soft/ | ||||
| /jme3-android-native/OpenALSoft.zip | ||||
| /jme3-android-native/src/native/jme_decode/STBI/ | ||||
| /jme3-android-native/src/native/jme_decode/Tremor/ | ||||
| /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/stb_image.h | ||||
| !/jme3-bullet-native/libs/native/windows/x86_64/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_64/libbulletjme.dylib | ||||
| !/jme3-bullet-native/libs/native/linux/x86/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.') { | ||||
|     classifier = 'sources' | ||||
|     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 -> 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) { | ||||
|         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); | ||||
| 		shapeBox = new Box(Vector3f.ZERO, 0.3f, 0.3f, 0.3f); | ||||
|             shapeBox = new Box(0.3f, 0.3f, 0.3f); | ||||
| 
 | ||||
| 
 | ||||
| 	//	ModelConverter.optimize(geom); | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package com.jme3.scene.plugins.blender.materials; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Map.Entry; | ||||
| @ -157,14 +158,14 @@ public final class MaterialContext implements Savable { | ||||
|         } | ||||
| 
 | ||||
|         // applying textures | ||||
|         int textureIndex = 0; | ||||
|         if (loadedTextures != null && loadedTextures.size() > 0) { | ||||
|             int textureIndex = 0; | ||||
|             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); | ||||
|             } | ||||
|             for (CombinedTexture combinedTexture : loadedTextures) { | ||||
|                 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()); | ||||
|                     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()]))); | ||||
|                         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) | ||||
|                      | ||||
|                     if(usedUserUVSet != null) { | ||||
|                     	userDefinedUVCoordinates = new HashMap<>(userDefinedUVCoordinates); | ||||
|                     	userDefinedUVCoordinates.remove(usedUserUVSet); | ||||
|                     } | ||||
|                 } 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); | ||||
|                 } | ||||
|             } | ||||
|         } 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) { | ||||
|                 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()]))); | ||||
|                     geometry.getMesh().setBuffer(uvCoordsBuffer); | ||||
|                 } 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 | ||||
|      * type. | ||||
|      *  | ||||
|      * @param geometry | ||||
|      *            the geometry the texture is created for | ||||
|      * @param geometriesOMA | ||||
|      *            the old memory address of the geometries list that the given | ||||
|      *            geometry belongs to (needed for bounding box creation) | ||||
|      * @param userDefinedUVCoordinates | ||||
|      *            the UV's defined by user (null or zero length table if none | ||||
|      *            were defined) | ||||
|      * @param blenderContext | ||||
|      *            the blender context | ||||
|      */ | ||||
|     public void flatten(Geometry geometry, Long geometriesOMA, Map<String, List<Vector2f>> userDefinedUVCoordinates, BlenderContext blenderContext) { | ||||
| 	/** | ||||
| 	 * This method flattens the texture and creates a single result of Texture2D | ||||
| 	 * type. | ||||
| 	 *  | ||||
| 	 * @param geometry | ||||
| 	 *            the geometry the texture is created for | ||||
| 	 * @param geometriesOMA | ||||
| 	 *            the old memory address of the geometries list that the given | ||||
| 	 *            geometry belongs to (needed for bounding box creation) | ||||
| 	 * @param userDefinedUVCoordinates | ||||
| 	 *            the UV's defined by user (null or zero length table if none | ||||
| 	 *            were defined) | ||||
| 	 * @param blenderContext | ||||
| 	 *            the blender context | ||||
| 	 * @return the name of the user UV coordinates used (null if the UV's were | ||||
| 	 *         generated) | ||||
| 	 */ | ||||
|     public String flatten(Geometry geometry, Long geometriesOMA, Map<String, List<Vector2f>> userDefinedUVCoordinates, BlenderContext blenderContext) { | ||||
|         Mesh mesh = geometry.getMesh(); | ||||
|         Texture previousTexture = null; | ||||
|         UVCoordinatesType masterUVCoordinatesType = null; | ||||
| @ -226,6 +228,7 @@ public class CombinedTexture { | ||||
|             } | ||||
|             resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS(); | ||||
|             resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture(); | ||||
|             masterUserUVSetName = null; | ||||
|         } | ||||
| 
 | ||||
|         // setting additional data | ||||
| @ -234,6 +237,8 @@ public class CombinedTexture { | ||||
|         // otherwise ugly lines appear between the mesh faces | ||||
|         resultTexture.setMagFilter(MagFilter.Nearest); | ||||
|         resultTexture.setMinFilter(MinFilter.NearestNoMipMaps); | ||||
|          | ||||
|         return masterUserUVSetName; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -34,7 +34,14 @@ libraries { | ||||
| //                linker.args "-static-libstdc++" | ||||
|             } else if (targetPlatform.operatingSystem.name == "windows") { | ||||
|                 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) { | ||||
|                     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 | ||||
| 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{ | ||||
| //    osx_universal { // TODO: universal binary doesn't work? | ||||
| //        architecture 'x86_64' | ||||
|  | ||||
| @ -111,7 +111,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl | ||||
|      * Material references used for hardware skinning | ||||
|      */ | ||||
|     private Set<Material> materials = new HashSet<Material>(); | ||||
| 
 | ||||
|      | ||||
|     /** | ||||
|      * Serialization only. Do not use. | ||||
|      */ | ||||
| @ -204,6 +204,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl | ||||
|      * @param skeleton the skeleton | ||||
|      */ | ||||
|     public SkeletonControl(Skeleton skeleton) { | ||||
|         if (skeleton == null) { | ||||
|             throw new IllegalArgumentException("skeleton cannot be null"); | ||||
|         } | ||||
|         this.skeleton = skeleton; | ||||
|     } | ||||
| 
 | ||||
| @ -406,7 +409,23 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl | ||||
|         // Not automatic set cloning yet | ||||
|         Set<Material> newMaterials = new HashSet<Material>(); | ||||
|         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; | ||||
|     } | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
| import com.jme3.app.state.AppState; | ||||
| import com.jme3.audio.AudioListenerState; | ||||
| import com.jme3.font.BitmapFont; | ||||
| import com.jme3.font.BitmapText; | ||||
| import com.jme3.input.FlyByCamera; | ||||
| @ -96,7 +97,7 @@ public abstract class SimpleApplication extends LegacyApplication { | ||||
|     } | ||||
| 
 | ||||
|     public SimpleApplication() { | ||||
|         this( new StatsAppState(), new FlyCamAppState(), new DebugKeysAppState() ); | ||||
|         this(new StatsAppState(), new FlyCamAppState(), new AudioListenerState(), new DebugKeysAppState()); | ||||
|     } | ||||
| 
 | ||||
|     public SimpleApplication( AppState... initialStates ) { | ||||
|  | ||||
| @ -41,9 +41,7 @@ import com.jme3.post.FilterPostProcessor; | ||||
| import com.jme3.renderer.Caps; | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.jme3.scene.plugins.OBJLoader; | ||||
| import com.jme3.shader.Shader; | ||||
| import com.jme3.shader.ShaderGenerator; | ||||
| import com.jme3.shader.ShaderKey; | ||||
| import com.jme3.texture.Texture; | ||||
| import com.jme3.texture.plugins.TGALoader; | ||||
| import java.io.IOException; | ||||
| @ -320,13 +318,6 @@ public interface AssetManager { | ||||
|      */ | ||||
|     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, | ||||
|      * and are with the extension "fnt". | ||||
|  | ||||
| @ -32,7 +32,6 @@ | ||||
| package com.jme3.asset; | ||||
| 
 | ||||
| import com.jme3.asset.cache.AssetCache; | ||||
| import com.jme3.asset.cache.SimpleAssetCache; | ||||
| import com.jme3.audio.AudioData; | ||||
| import com.jme3.audio.AudioKey; | ||||
| import com.jme3.font.BitmapFont; | ||||
| @ -42,9 +41,7 @@ import com.jme3.renderer.Caps; | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.jme3.shader.Glsl100ShaderGenerator; | ||||
| import com.jme3.shader.Glsl150ShaderGenerator; | ||||
| import com.jme3.shader.Shader; | ||||
| import com.jme3.shader.ShaderGenerator; | ||||
| import com.jme3.shader.ShaderKey; | ||||
| import com.jme3.system.JmeSystem; | ||||
| import com.jme3.texture.Texture; | ||||
| import java.io.IOException; | ||||
| @ -431,36 +428,6 @@ public class DesktopAssetManager implements AssetManager { | ||||
|         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} | ||||
|      */ | ||||
|  | ||||
| @ -123,24 +123,6 @@ public class TextureKey extends AssetKey<Texture> { | ||||
|         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() { | ||||
|         return generateMips; | ||||
|     } | ||||
| @ -149,24 +131,6 @@ public class TextureKey extends AssetKey<Texture> { | ||||
|         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. | ||||
|      *  | ||||
|  | ||||
							
								
								
									
										104
									
								
								jme3-core/src/main/java/com/jme3/audio/AudioListenerState.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								jme3-core/src/main/java/com/jme3/audio/AudioListenerState.java
									
									
									
									
									
										Normal file
									
								
							| @ -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 volatile AudioSource.Status status = AudioSource.Status.Stopped; | ||||
|     protected transient volatile int channel = -1; | ||||
|     protected Vector3f previousWorldTranslation = Vector3f.NAN; | ||||
|     protected Vector3f velocity = new Vector3f(); | ||||
|     protected boolean reverbEnabled = false; | ||||
|     protected float maxDistance = 200; // 200 meters | ||||
| @ -88,6 +89,8 @@ public class AudioNode extends Node implements AudioSource { | ||||
|     protected float innerAngle = 360; | ||||
|     protected float outerAngle = 360; | ||||
|     protected boolean positional = true; | ||||
|     protected boolean velocityFromTranslation = false; | ||||
|     protected float lastTpf; | ||||
| 
 | ||||
|     /** | ||||
|      * <code>Status</code> indicates the current status of the audio node. | ||||
| @ -702,17 +705,44 @@ public class AudioNode extends Node implements AudioSource { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void updateGeometricState(){ | ||||
|         boolean updatePos = false; | ||||
|         if ((refreshFlags & RF_TRANSFORM) != 0){ | ||||
|             updatePos = true; | ||||
|         } | ||||
|     public boolean isVelocityFromTranslation() { | ||||
|         return velocityFromTranslation; | ||||
|     } | ||||
| 
 | ||||
|     public void setVelocityFromTranslation(boolean velocityFromTranslation) { | ||||
|         this.velocityFromTranslation = velocityFromTranslation; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void updateLogicalState(float tpf) { | ||||
|         super.updateLogicalState(tpf); | ||||
|         lastTpf = tpf; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void 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); | ||||
| 
 | ||||
|             if (velocityFromTranslation) { | ||||
|                 velocity.set(currentWorldTranslation).subtractLocal(previousWorldTranslation); | ||||
|                 velocity.multLocal(1f / lastTpf); | ||||
| 
 | ||||
|                 getRenderer().updateSourceParam(this, AudioParam.Velocity); | ||||
|             } | ||||
| 
 | ||||
|             previousWorldTranslation.set(currentWorldTranslation); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @ -772,6 +802,7 @@ public class AudioNode extends Node implements AudioSource { | ||||
|         oc.write(outerAngle, "outer_angle", 360); | ||||
| 
 | ||||
|         oc.write(positional, "positional", false); | ||||
|         oc.write(velocityFromTranslation, "velocity_from_translation", false); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @ -806,6 +837,7 @@ public class AudioNode extends Node implements AudioSource { | ||||
|         outerAngle = ic.readFloat("outer_angle", 360); | ||||
| 
 | ||||
|         positional = ic.readBoolean("positional", false); | ||||
|         velocityFromTranslation = ic.readBoolean("velocity_from_translation", false); | ||||
| 
 | ||||
|         if (audioKey != null) { | ||||
|             try { | ||||
|  | ||||
| @ -36,9 +36,9 @@ import com.jme3.math.Vector3f; | ||||
| 
 | ||||
| public class Listener { | ||||
| 
 | ||||
|     private Vector3f location; | ||||
|     private Vector3f velocity; | ||||
|     private Quaternion rotation; | ||||
|     private final Vector3f location; | ||||
|     private final Vector3f velocity; | ||||
|     private final Quaternion rotation; | ||||
|     private float volume = 1; | ||||
|     private AudioRenderer renderer; | ||||
| 
 | ||||
|  | ||||
| @ -904,11 +904,12 @@ public class ALAudioRenderer implements AudioRenderer, Runnable { | ||||
|                     } else { | ||||
|                         // Buffer finished playing. | ||||
|                         if (src.isLooping()) { | ||||
|                             throw new AssertionError("Unexpected state: " +  | ||||
|                                                      "A looping sound has stopped playing"); | ||||
|                         } else { | ||||
|                             reclaimChannel = true; | ||||
|                             // When a device is disconnected, all sources | ||||
|                             // will enter the "stopped" state. | ||||
|                             logger.warning("A looping sound has stopped playing"); | ||||
|                         } | ||||
| 
 | ||||
|                         reclaimChannel = true; | ||||
|                     } | ||||
|                      | ||||
|                     if (reclaimChannel) { | ||||
|  | ||||
| @ -121,7 +121,8 @@ public class MotionPath implements Savable { | ||||
|             Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m"); | ||||
|             for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) { | ||||
|                 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); | ||||
|                 debugNode.attachChild(geo); | ||||
| 
 | ||||
|  | ||||
| @ -50,8 +50,9 @@ public abstract class ParticleMesh extends Mesh { | ||||
|     public enum Type { | ||||
|         /** | ||||
|          * 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} | ||||
|          * to render particles the usual way. | ||||
|          * Note that point based particles do not support certain features such | ||||
|          * as {@link ParticleEmitter#setRotateSpeed(float) rotation}, and | ||||
|          * {@link ParticleEmitter#setFacingVelocity(boolean) velocity following}. | ||||
|          */ | ||||
|         Point, | ||||
|          | ||||
|  | ||||
| @ -54,10 +54,10 @@ import java.util.logging.Logger; | ||||
|  */ | ||||
| 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){ | ||||
|         classRemappings.put(oldClass, newClass.getName()); | ||||
|         CLASS_REMAPPINGS.put(oldClass, newClass.getName()); | ||||
|     } | ||||
|      | ||||
|     static { | ||||
| @ -74,7 +74,7 @@ public class SavableClassUtil { | ||||
|     } | ||||
|      | ||||
|     private static String remapClass(String className) throws ClassNotFoundException { | ||||
|         String result = classRemappings.get(className); | ||||
|         String result = CLASS_REMAPPINGS.get(className); | ||||
|         if (result == null) { | ||||
|             return className; | ||||
|         } else { | ||||
|  | ||||
| @ -34,7 +34,6 @@ package com.jme3.material; | ||||
| import com.jme3.asset.TextureKey; | ||||
| import com.jme3.export.*; | ||||
| import com.jme3.math.*; | ||||
| import com.jme3.renderer.Renderer; | ||||
| import com.jme3.shader.VarType; | ||||
| import com.jme3.texture.Texture; | ||||
| import com.jme3.texture.Texture.WrapMode; | ||||
| @ -129,9 +128,6 @@ public class MatParam implements Savable, Cloneable { | ||||
|         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 | ||||
|  | ||||
							
								
								
									
										151
									
								
								jme3-core/src/main/java/com/jme3/material/MatParamOverride.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								jme3-core/src/main/java/com/jme3/material/MatParamOverride.java
									
									
									
									
									
										Normal file
									
								
							| @ -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.JmeImporter; | ||||
| import com.jme3.export.OutputCapsule; | ||||
| import com.jme3.renderer.Renderer; | ||||
| import com.jme3.shader.VarType; | ||||
| import com.jme3.texture.Texture; | ||||
| import com.jme3.texture.image.ColorSpace; | ||||
| @ -44,13 +43,11 @@ import java.io.IOException; | ||||
| public class MatParamTexture extends MatParam { | ||||
| 
 | ||||
|     private Texture texture; | ||||
|     private int unit; | ||||
|     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); | ||||
|         this.texture = texture; | ||||
|         this.unit = unit; | ||||
|         this.colorSpace = colorSpace; | ||||
|     } | ||||
| 
 | ||||
| @ -92,37 +89,18 @@ public class MatParamTexture extends MatParam { | ||||
|         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 | ||||
|     public void write(JmeExporter ex) throws IOException { | ||||
|         super.write(ex); | ||||
|         OutputCapsule oc = ex.getCapsule(this); | ||||
|         oc.write(unit, "texture_unit", -1); | ||||
|          | ||||
|         // For backwards compat | ||||
|         oc.write(texture, "texture", null); | ||||
|         oc.write(0, "texture_unit", -1); | ||||
|         oc.write(texture, "texture", null); // For backwards compatibility | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void read(JmeImporter im) throws IOException { | ||||
|         super.read(im); | ||||
|         InputCapsule ic = im.getCapsule(this); | ||||
|         unit = ic.readInt("texture_unit", -1); | ||||
|         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.RenderManager; | ||||
| import com.jme3.renderer.Renderer; | ||||
| import com.jme3.renderer.RendererException; | ||||
| import com.jme3.renderer.queue.RenderQueue.Bucket; | ||||
| 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.Uniform; | ||||
| import com.jme3.shader.UniformBindingManager; | ||||
| import com.jme3.shader.VarType; | ||||
| import com.jme3.texture.Image; | ||||
| import com.jme3.texture.Texture; | ||||
| import com.jme3.texture.image.ColorSpace; | ||||
| import com.jme3.util.ListMap; | ||||
| import com.jme3.util.TempVars; | ||||
| import java.io.IOException; | ||||
| import java.util.*; | ||||
| 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 | ||||
|     public static final int SAVABLE_VERSION = 2; | ||||
|     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 String name; | ||||
|     private MaterialDef def; | ||||
|     private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>(); | ||||
|     private Technique technique; | ||||
|     private HashMap<String, Technique> techniques = new HashMap<String, Technique>(); | ||||
|     private int nextTexUnit = 0; | ||||
|     private RenderState additionalState = null; | ||||
|     private RenderState mergedRenderState = new RenderState(); | ||||
|     private boolean transparent = false; | ||||
|     private boolean receivesShadows = false; | ||||
|     private int sortingId = -1; | ||||
|     private transient ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1); | ||||
| 
 | ||||
|     public Material(MaterialDef def) { | ||||
|         if (def == null) { | ||||
| @ -175,22 +159,29 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|      * @return The sorting ID used for sorting geometries for rendering. | ||||
|      */ | ||||
|     public int getSortId() { | ||||
|         Technique t = getActiveTechnique(); | ||||
|         if (sortingId == -1 && t != null && t.getShader() != null) { | ||||
|             int texId = -1; | ||||
|         if (sortingId == -1 && technique != null) { | ||||
|             sortingId = technique.getSortId() << 16; | ||||
|             int texturesSortId = 17; | ||||
|             for (int i = 0; i < paramValues.size(); i++) { | ||||
|                 MatParam param = paramValues.getValue(i); | ||||
|                 if (param instanceof MatParamTexture) { | ||||
|                     MatParamTexture tex = (MatParamTexture) param; | ||||
|                     if (tex.getTextureValue() != null && tex.getTextureValue().getImage() != null) { | ||||
|                         if (texId == -1) { | ||||
|                             texId = 0; | ||||
|                         } | ||||
|                         texId += tex.getTextureValue().getImage().getId() % 0xff; | ||||
|                     } | ||||
|                 if (!param.getVarType().isTextureType()) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 Texture texture = (Texture) param.getValue(); | ||||
|                 if (texture == null) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 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; | ||||
|     } | ||||
| @ -215,6 +206,8 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|                 mat.paramValues.put(entry.getKey(), entry.getValue().clone()); | ||||
|             } | ||||
| 
 | ||||
|             mat.sortingId = -1; | ||||
|              | ||||
|             return mat; | ||||
|         } catch (CloneNotSupportedException 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 | ||||
|             // uses default technique for other material, the materials | ||||
|             // are not equal. | ||||
|             String thisDefName = this.technique != null ? this.technique.getDef().getName() : "Default"; | ||||
|             String otherDefName = other.technique != null ? other.technique.getDef().getName() : "Default"; | ||||
|             String thisDefName = this.technique != null | ||||
|                     ? 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)) { | ||||
|                 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) | ||||
|      */ | ||||
|     public ListMap getParamsMap() { | ||||
|     public ListMap<String, MatParam> getParamsMap() { | ||||
|         return paramValues; | ||||
|     } | ||||
| 
 | ||||
| @ -504,16 +503,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
| 
 | ||||
|         paramValues.remove(name); | ||||
|         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; | ||||
|         } | ||||
|         if (technique != null) { | ||||
| @ -556,13 +545,13 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|                         + "Linear using texture.getImage.setColorSpace().", | ||||
|                         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 { | ||||
|             val.setTextureValue(value); | ||||
|         } | ||||
| 
 | ||||
|         if (technique != null) { | ||||
|             technique.notifyParamChanged(name, type, nextTexUnit - 1); | ||||
|             technique.notifyParamChanged(name, type, value); | ||||
|         } | ||||
| 
 | ||||
|         // need to recompute sort ID | ||||
| @ -695,277 +684,21 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|         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. | ||||
|      * <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) | ||||
|      * must be verified to be compatible with the system, for that, the | ||||
|      * <code>renderManager</code> is queried for capabilities. | ||||
|      * | ||||
|      * @param name The name of the technique to select, pass "Default" to | ||||
|      * select one of the default techniques. | ||||
|      * @param name The name of the technique to select, pass | ||||
|      * {@link TechniqueDef#DEFAULT_TECHNIQUE_NAME} to select one of the default | ||||
|      * techniques. | ||||
|      * @param renderManager The {@link RenderManager render manager} | ||||
|      * to query for capabilities. | ||||
|      * | ||||
|      * @throws IllegalArgumentException If "Default" is passed and no default | ||||
|      * techniques are available on the material definition, or if a name | ||||
|      * is passed but there's no technique by that name. | ||||
|      * @throws IllegalArgumentException If no technique exists with the given | ||||
|      * name. | ||||
|      * @throws UnsupportedOperationException If no candidate technique supports | ||||
|      * the system capabilities. | ||||
|      */ | ||||
| @ -974,49 +707,34 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|         Technique tech = techniques.get(name); | ||||
|         // When choosing technique, we choose one that | ||||
|         // supports all the caps. | ||||
|         EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps(); | ||||
|         if (tech == null) { | ||||
|             EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps(); | ||||
|             List<TechniqueDef> techDefs = def.getTechniqueDefs(name); | ||||
| 
 | ||||
|             if (name.equals("Default")) { | ||||
|                 List<TechniqueDef> techDefs = def.getDefaultTechniques(); | ||||
|                 if (techDefs == null || techDefs.isEmpty()) { | ||||
|                     throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'"); | ||||
|                 } | ||||
|             if (techDefs == null || techDefs.isEmpty()) { | ||||
|                 throw new IllegalArgumentException( | ||||
|                         String.format("The requested technique %s is not available on material %s", name, def.getName())); | ||||
|             } | ||||
| 
 | ||||
|                 TechniqueDef lastTech = null; | ||||
|                 for (TechniqueDef techDef : techDefs) { | ||||
|                     if (rendererCaps.containsAll(techDef.getRequiredCaps())) { | ||||
|                         // use the first one that supports all the caps | ||||
|                         tech = new Technique(this, techDef); | ||||
|                         techniques.put(name, tech); | ||||
|                         if(tech.getDef().getLightMode() == renderManager.getPreferredLightMode() || | ||||
|                                tech.getDef().getLightMode() == LightMode.Disable){ | ||||
|                             break; | ||||
|                         } | ||||
|             TechniqueDef lastTech = null; | ||||
|             for (TechniqueDef techDef : techDefs) { | ||||
|                 if (rendererCaps.containsAll(techDef.getRequiredCaps())) { | ||||
|                     // use the first one that supports all the caps | ||||
|                     tech = new Technique(this, techDef); | ||||
|                     techniques.put(name, tech); | ||||
|                     if (tech.getDef().getLightMode() == renderManager.getPreferredLightMode() | ||||
|                             || tech.getDef().getLightMode() == LightMode.Disable) { | ||||
|                         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"); | ||||
|                 } | ||||
| 
 | ||||
|                 tech = new Technique(this, techDef); | ||||
|                 techniques.put(name, tech); | ||||
|                 lastTech = techDef; | ||||
|             } | ||||
|             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) { | ||||
|             // attempting to switch to an already | ||||
| @ -1025,20 +743,82 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|         } | ||||
| 
 | ||||
|         technique = tech; | ||||
|         tech.makeCurrent(def.getAssetManager(), true, rendererCaps, renderManager); | ||||
|         tech.notifyTechniqueSwitched(); | ||||
| 
 | ||||
|         // shader was changed | ||||
|         sortingId = -1; | ||||
|     } | ||||
| 
 | ||||
|     private void autoSelectTechnique(RenderManager rm) { | ||||
|         if (technique == null) { | ||||
|             selectTechnique("Default", rm); | ||||
|         } else { | ||||
|             technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps(), rm); | ||||
|     private int applyOverrides(Renderer renderer, Shader shader, List<MatParamOverride> overrides, int unit) { | ||||
|         for (MatParamOverride override : overrides) { | ||||
|             VarType type = override.getVarType(); | ||||
| 
 | ||||
|             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. | ||||
|      * <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 | ||||
|      * 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) { | ||||
|         autoSelectTechnique(rm); | ||||
|     public void preload(RenderManager renderManager) { | ||||
|         if (technique == null) { | ||||
|             selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); | ||||
|         } | ||||
|         TechniqueDef techniqueDef = technique.getDef(); | ||||
|         Renderer renderer = renderManager.getRenderer(); | ||||
|         EnumSet<Caps> rendererCaps = renderer.getCaps(); | ||||
| 
 | ||||
|         Renderer r = rm.getRenderer(); | ||||
|         TechniqueDef techDef = technique.getDef(); | ||||
| 
 | ||||
|         Collection<MatParam> params = paramValues.values(); | ||||
|         for (MatParam param : params) { | ||||
|             param.apply(r, technique); | ||||
|         if (techniqueDef.isNoRender()) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         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) { | ||||
| @ -1141,80 +924,46 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|      * </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 rm The render manager requesting the rendering | ||||
|      * @param renderManager The render manager requesting the rendering | ||||
|      */ | ||||
|     public void render(Geometry geom, LightList lights, RenderManager rm) { | ||||
|         autoSelectTechnique(rm); | ||||
|         TechniqueDef techDef = technique.getDef(); | ||||
| 
 | ||||
|         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)); | ||||
|             } | ||||
|     public void render(Geometry geometry, LightList lights, RenderManager renderManager) { | ||||
|         if (technique == null) { | ||||
|             selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); | ||||
|         } | ||||
|          | ||||
|         TechniqueDef techniqueDef = technique.getDef(); | ||||
|         Renderer renderer = renderManager.getRenderer(); | ||||
|         EnumSet<Caps> rendererCaps = renderer.getCaps(); | ||||
|          | ||||
|         if (techniqueDef.isNoRender()) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Apply render state | ||||
|         updateRenderState(renderManager, renderer, techniqueDef); | ||||
| 
 | ||||
|         // update camera and world matrices | ||||
|         // NOTE: setWorldTransform should have been called already | ||||
|         // Get world overrides | ||||
|         List<MatParamOverride> overrides = geometry.getWorldMatParamOverrides(); | ||||
| 
 | ||||
|         // 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(); | ||||
| 
 | ||||
|         // send lighting information, if needed | ||||
|         switch (techDef.getLightMode()) { | ||||
|             case Disable: | ||||
|                 break; | ||||
|             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 | ||||
|         // any unset uniforms will be set to 0 | ||||
|         // 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); | ||||
|         r.setShader(shader); | ||||
| 
 | ||||
|         renderMeshFromGeometry(r, geom); | ||||
|          | ||||
|         // Delegate rendering to the technique | ||||
|         technique.render(renderManager, shader, geometry, lights); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1239,6 +988,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|         oc.write(name, "name", 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 { | ||||
|         InputCapsule ic = im.getCapsule(this); | ||||
| @ -1296,11 +1053,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { | ||||
|             MatParam param = entry.getValue(); | ||||
|             if (param instanceof MatParamTexture) { | ||||
|                 MatParamTexture texVal = (MatParamTexture) param; | ||||
| 
 | ||||
|                 if (nextTexUnit < texVal.getUnit() + 1) { | ||||
|                     nextTexUnit = texVal.getUnit() + 1; | ||||
|                 } | ||||
| 
 | ||||
|                 // the texture failed to load for this param | ||||
|                 // do not add to param values | ||||
|                 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 | ||||
|             // if value != default then set apply to true | ||||
|             additionalState.applyPolyOffset = additionalState.offsetEnabled; | ||||
|             additionalState.applyAlphaFallOff = additionalState.alphaTest; | ||||
|             additionalState.applyAlphaTest = additionalState.alphaTest; | ||||
|             additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off; | ||||
|             additionalState.applyColorWrite = !additionalState.colorWrite; | ||||
|             additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back; | ||||
|             additionalState.applyDepthTest = !additionalState.depthTest; | ||||
|             additionalState.applyDepthWrite = !additionalState.depthWrite; | ||||
|             additionalState.applyPointSprite = additionalState.pointSprite; | ||||
|             additionalState.applyStencilTest = additionalState.stencilTest; | ||||
|             additionalState.applyWireFrame = additionalState.wireframe; | ||||
|         } | ||||
|  | ||||
| @ -32,6 +32,7 @@ | ||||
| package com.jme3.material; | ||||
| 
 | ||||
| import com.jme3.asset.AssetManager; | ||||
| import com.jme3.renderer.Caps; | ||||
| import com.jme3.shader.VarType; | ||||
| import com.jme3.texture.image.ColorSpace; | ||||
| import java.util.*; | ||||
| @ -51,8 +52,7 @@ public class MaterialDef { | ||||
|     private String assetName; | ||||
|     private AssetManager assetManager; | ||||
| 
 | ||||
|     private List<TechniqueDef> defaultTechs; | ||||
|     private Map<String, TechniqueDef> techniques; | ||||
|     private Map<String, List<TechniqueDef>> techniques; | ||||
|     private Map<String, MatParam> matParams; | ||||
| 
 | ||||
|     /** | ||||
| @ -70,9 +70,8 @@ public class MaterialDef { | ||||
|     public MaterialDef(AssetManager assetManager, String name){ | ||||
|         this.assetManager = assetManager; | ||||
|         this.name = name; | ||||
|         techniques = new HashMap<String, TechniqueDef>(); | ||||
|         techniques = new HashMap<String, List<TechniqueDef>>(); | ||||
|         matParams = new HashMap<String, MatParam>(); | ||||
|         defaultTechs = new ArrayList<TechniqueDef>(); | ||||
|         logger.log(Level.FINE, "Loaded material definition: {0}", name); | ||||
|     } | ||||
| 
 | ||||
| @ -135,7 +134,7 @@ public class MaterialDef { | ||||
|      * @see 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. | ||||
|      * <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. | ||||
|      */ | ||||
|     public void addTechniqueDef(TechniqueDef technique) { | ||||
|         if (technique.getName().equals("Default")) { | ||||
|             defaultTechs.add(technique); | ||||
|         } else { | ||||
|             techniques.put(technique.getName(), technique); | ||||
|         List<TechniqueDef> list = techniques.get(technique.getName()); | ||||
|         if (list == null) { | ||||
|             list = new ArrayList<>(); | ||||
|             techniques.put(technique.getName(), list); | ||||
|         } | ||||
|         list.add(technique); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a list of all default techniques. | ||||
|      *  | ||||
|      * @return a list of all default techniques. | ||||
|      * Returns technique definitions with the given name. | ||||
|        *  | ||||
|      * @param name The name of the technique definitions to find | ||||
|        *  | ||||
|      * @return The technique definitions, or null if cannot be found. | ||||
|      */ | ||||
|     public List<TechniqueDef> getDefaultTechniques(){ | ||||
|         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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -75,12 +75,11 @@ public class RenderState implements Cloneable, Savable { | ||||
| 
 | ||||
|     /** | ||||
|      * <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 | ||||
|      * test function, the reference value given in the stencil command is | ||||
|      * the input value while the reference is the value already in the stencil | ||||
|      * buffer. | ||||
|      * <p> | ||||
|      * The reference value given in the stencil command is the input value while | ||||
|      * the reference is the value already in the stencil buffer. | ||||
|      */ | ||||
|     public enum TestFunction { | ||||
| 
 | ||||
| @ -118,7 +117,94 @@ public class RenderState implements Cloneable, Savable { | ||||
|         /** | ||||
|          * 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. | ||||
| @ -276,19 +362,16 @@ public class RenderState implements Cloneable, Savable { | ||||
|     } | ||||
| 
 | ||||
|     static { | ||||
|         ADDITIONAL.applyPointSprite = false; | ||||
|         ADDITIONAL.applyWireFrame = false; | ||||
|         ADDITIONAL.applyCullMode = false; | ||||
|         ADDITIONAL.applyDepthWrite = false; | ||||
|         ADDITIONAL.applyDepthTest = false; | ||||
|         ADDITIONAL.applyColorWrite = false; | ||||
|         ADDITIONAL.applyBlendEquation = false; | ||||
|         ADDITIONAL.applyBlendEquationAlpha = false; | ||||
|         ADDITIONAL.applyBlendMode = false; | ||||
|         ADDITIONAL.applyAlphaTest = false; | ||||
|         ADDITIONAL.applyAlphaFallOff = false; | ||||
|         ADDITIONAL.applyPolyOffset = false; | ||||
|     } | ||||
|     boolean pointSprite = false; | ||||
|     boolean applyPointSprite = true; | ||||
|     boolean wireframe = false; | ||||
|     boolean applyWireFrame = true; | ||||
|     FaceCullMode cullMode = FaceCullMode.Back; | ||||
| @ -299,12 +382,12 @@ public class RenderState implements Cloneable, Savable { | ||||
|     boolean applyDepthTest = true; | ||||
|     boolean colorWrite = true; | ||||
|     boolean applyColorWrite = true; | ||||
|     BlendEquation blendEquation = BlendEquation.Add; | ||||
|     boolean applyBlendEquation = true; | ||||
|     BlendEquationAlpha blendEquationAlpha = BlendEquationAlpha.InheritColor; | ||||
|     boolean applyBlendEquationAlpha = true; | ||||
|     BlendMode blendMode = BlendMode.Off; | ||||
|     boolean applyBlendMode = true; | ||||
|     boolean alphaTest = false; | ||||
|     boolean applyAlphaTest = true; | ||||
|     float alphaFallOff = 0; | ||||
|     boolean applyAlphaFallOff = true; | ||||
|     float offsetFactor = 0; | ||||
|     float offsetUnits = 0; | ||||
|     boolean offsetEnabled = false; | ||||
| @ -315,10 +398,7 @@ public class RenderState implements Cloneable, Savable { | ||||
|     boolean applyLineWidth = false; | ||||
|     TestFunction depthFunc = TestFunction.LessOrEqual; | ||||
|     //by default depth func will be applied anyway if depth test is applied | ||||
|     boolean applyDepthFunc = false;     | ||||
|     //by default alpha func will be applied anyway if alpha test is applied | ||||
|     TestFunction alphaFunc = TestFunction.Greater;     | ||||
|     boolean applyAlphaFunc = false; | ||||
|     boolean applyDepthFunc = false; | ||||
|     StencilOperation frontStencilStencilFailOperation = StencilOperation.Keep; | ||||
|     StencilOperation frontStencilDepthFailOperation = StencilOperation.Keep; | ||||
|     StencilOperation frontStencilDepthPassOperation = StencilOperation.Keep; | ||||
| @ -331,15 +411,13 @@ public class RenderState implements Cloneable, Savable { | ||||
| 
 | ||||
|     public void write(JmeExporter ex) throws IOException { | ||||
|         OutputCapsule oc = ex.getCapsule(this); | ||||
|         oc.write(pointSprite, "pointSprite", false); | ||||
|         oc.write(true, "pointSprite", false); | ||||
|         oc.write(wireframe, "wireframe", false); | ||||
|         oc.write(cullMode, "cullMode", FaceCullMode.Back); | ||||
|         oc.write(depthWrite, "depthWrite", true); | ||||
|         oc.write(depthTest, "depthTest", true); | ||||
|         oc.write(colorWrite, "colorWrite", true); | ||||
|         oc.write(blendMode, "blendMode", BlendMode.Off); | ||||
|         oc.write(alphaTest, "alphaTest", false); | ||||
|         oc.write(alphaFallOff, "alphaFallOff", 0); | ||||
|         oc.write(offsetEnabled, "offsetEnabled", false); | ||||
|         oc.write(offsetFactor, "offsetFactor", 0); | ||||
|         oc.write(offsetUnits, "offsetUnits", 0); | ||||
| @ -352,38 +430,34 @@ public class RenderState implements Cloneable, Savable { | ||||
|         oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep); | ||||
|         oc.write(frontStencilFunction, "frontStencilFunction", 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(alphaFunc, "alphaFunc", TestFunction.Greater); | ||||
|         oc.write(lineWidth, "lineWidth", 1); | ||||
| 
 | ||||
|         // Only "additional render state" has them set to false by default | ||||
|         oc.write(applyPointSprite, "applyPointSprite", true); | ||||
|         oc.write(applyWireFrame, "applyWireFrame", true); | ||||
|         oc.write(applyCullMode, "applyCullMode", true); | ||||
|         oc.write(applyDepthWrite, "applyDepthWrite", true); | ||||
|         oc.write(applyDepthTest, "applyDepthTest", true); | ||||
|         oc.write(applyColorWrite, "applyColorWrite", true); | ||||
|         oc.write(applyBlendEquation, "applyBlendEquation", true); | ||||
|         oc.write(applyBlendEquationAlpha, "applyBlendEquationAlpha", true); | ||||
|         oc.write(applyBlendMode, "applyBlendMode", true); | ||||
|         oc.write(applyAlphaTest, "applyAlphaTest", true); | ||||
|         oc.write(applyAlphaFallOff, "applyAlphaFallOff", true); | ||||
|         oc.write(applyPolyOffset, "applyPolyOffset", true); | ||||
|         oc.write(applyDepthFunc, "applyDepthFunc", true); | ||||
|         oc.write(applyAlphaFunc, "applyAlphaFunc", false); | ||||
|         oc.write(applyLineWidth, "applyLineWidth", true); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public void read(JmeImporter im) throws IOException { | ||||
|         InputCapsule ic = im.getCapsule(this); | ||||
|         pointSprite = ic.readBoolean("pointSprite", false); | ||||
|         wireframe = ic.readBoolean("wireframe", false); | ||||
|         cullMode = ic.readEnum("cullMode", FaceCullMode.class, FaceCullMode.Back); | ||||
|         depthWrite = ic.readBoolean("depthWrite", true); | ||||
|         depthTest = ic.readBoolean("depthTest", true); | ||||
|         colorWrite = ic.readBoolean("colorWrite", true); | ||||
|         blendMode = ic.readEnum("blendMode", BlendMode.class, BlendMode.Off); | ||||
|         alphaTest = ic.readBoolean("alphaTest", false); | ||||
|         alphaFallOff = ic.readFloat("alphaFallOff", 0); | ||||
|         offsetEnabled = ic.readBoolean("offsetEnabled", false); | ||||
|         offsetFactor = ic.readFloat("offsetFactor", 0); | ||||
|         offsetUnits = ic.readFloat("offsetUnits", 0); | ||||
| @ -396,23 +470,22 @@ public class RenderState implements Cloneable, Savable { | ||||
|         backStencilDepthPassOperation = ic.readEnum("backStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep); | ||||
|         frontStencilFunction = ic.readEnum("frontStencilFunction", 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); | ||||
|         alphaFunc = ic.readEnum("alphaFunc", TestFunction.class, TestFunction.Greater); | ||||
|         lineWidth = ic.readFloat("lineWidth", 1); | ||||
| 
 | ||||
| 
 | ||||
|         applyPointSprite = ic.readBoolean("applyPointSprite", true); | ||||
|         applyWireFrame = ic.readBoolean("applyWireFrame", true); | ||||
|         applyCullMode = ic.readBoolean("applyCullMode", true); | ||||
|         applyDepthWrite = ic.readBoolean("applyDepthWrite", true); | ||||
|         applyDepthTest = ic.readBoolean("applyDepthTest", true); | ||||
|         applyColorWrite = ic.readBoolean("applyColorWrite", true); | ||||
|         applyBlendEquation = ic.readBoolean("applyBlendEquation", true); | ||||
|         applyBlendEquationAlpha = ic.readBoolean("applyBlendEquationAlpha", true); | ||||
|         applyBlendMode = ic.readBoolean("applyBlendMode", true); | ||||
|         applyAlphaTest = ic.readBoolean("applyAlphaTest", true); | ||||
|         applyAlphaFallOff = ic.readBoolean("applyAlphaFallOff", true); | ||||
|         applyPolyOffset = ic.readBoolean("applyPolyOffset", true); | ||||
|         applyDepthFunc = ic.readBoolean("applyDepthFunc", true); | ||||
|         applyAlphaFunc = ic.readBoolean("applyAlphaFunc", false); | ||||
|         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 | ||||
|      * @param o the renderState to compate to | ||||
|      * returns true if the given renderState is equal to this one | ||||
|      * @param o the renderState to compare to | ||||
|      * @return true if the renderStates are equal | ||||
|      */ | ||||
|     @Override | ||||
| @ -446,9 +519,6 @@ public class RenderState implements Cloneable, Savable { | ||||
|             return false; | ||||
|         } | ||||
|         RenderState rs = (RenderState) o; | ||||
|         if (pointSprite != rs.pointSprite) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (wireframe != rs.wireframe) { | ||||
|             return false; | ||||
| @ -475,22 +545,18 @@ public class RenderState implements Cloneable, Savable { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (blendEquation != rs.blendEquation) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (blendEquationAlpha != rs.blendEquationAlpha) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (blendMode != rs.blendMode) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (alphaTest != rs.alphaTest) { | ||||
|             return false; | ||||
|         } | ||||
|         if (alphaTest) { | ||||
|             if (alphaFunc != rs.alphaFunc) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (alphaFallOff != rs.alphaFallOff) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (offsetEnabled != rs.offsetEnabled) { | ||||
|             return false; | ||||
| @ -544,70 +610,30 @@ public class RenderState implements Cloneable, Savable { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Enables point sprite mode. | ||||
|      * | ||||
|      * <p>When point sprite is enabled, any meshes | ||||
|      * 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 Does nothing. Point sprite is already enabled by default for | ||||
|      * all supported platforms. jME3 does not support rendering conventional | ||||
|      * point clouds. | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public void setPointSprite(boolean pointSprite) { | ||||
|         applyPointSprite = true; | ||||
|         this.pointSprite = pointSprite; | ||||
|         cachedHashCode = -1; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the alpha fall off value for alpha testing. | ||||
|      * | ||||
|      * <p>If the pixel's alpha value is greater than the | ||||
|      * <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 Does nothing. To use alpha test, set the | ||||
|      * <code>AlphaDiscardThreshold</code> material parameter. | ||||
|      * @param alphaFallOff does nothing | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public void setAlphaFallOff(float alphaFallOff) { | ||||
|         applyAlphaFallOff = true; | ||||
|         this.alphaFallOff = alphaFallOff; | ||||
|         cachedHashCode = -1; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Enable alpha testing. | ||||
|      * | ||||
|      * <p>When alpha testing is enabled, all input pixels' alpha are compared | ||||
|      * 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 Does nothing. To use alpha test, set the | ||||
|      * <code>AlphaDiscardThreshold</code> material parameter. | ||||
|      * @param alphaTest does nothing | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public void setAlphaTest(boolean alphaTest) { | ||||
|         applyAlphaTest = true; | ||||
|         this.alphaTest = alphaTest; | ||||
|         cachedHashCode = -1; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -663,6 +689,61 @@ public class RenderState implements Cloneable, Savable { | ||||
|         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. | ||||
|      * | ||||
| @ -796,24 +877,10 @@ public class RenderState implements Cloneable, Savable { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the alpha comparision function to the given TestFunction | ||||
|      * 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 | ||||
|      * @deprecated | ||||
|      */ | ||||
|     public void setAlphaFunc(TestFunction alphaFunc) {         | ||||
|         applyAlphaFunc = true; | ||||
|         this.alphaFunc = alphaFunc; | ||||
|         cachedHashCode = -1; | ||||
|     @Deprecated | ||||
|     public void setAlphaFunc(TestFunction alphaFunc) { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -822,6 +889,9 @@ public class RenderState implements Cloneable, Savable { | ||||
|      * @param lineWidth the line width. | ||||
|      */ | ||||
|     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.applyLineWidth = true; | ||||
|         cachedHashCode = -1; | ||||
| @ -988,6 +1058,24 @@ public class RenderState implements Cloneable, Savable { | ||||
|         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. | ||||
|      * | ||||
| @ -998,25 +1086,22 @@ public class RenderState implements Cloneable, Savable { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if point sprite mode is enabled | ||||
|      * | ||||
|      * @return True if point sprite mode is enabled. | ||||
|      * | ||||
|      * @see RenderState#setPointSprite(boolean) | ||||
|      * @return true | ||||
|      * @deprecated Always returns true since point sprite is always enabled. | ||||
|      * @see #setPointSprite(boolean) | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public boolean isPointSprite() { | ||||
|         return pointSprite; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if alpha test is enabled. | ||||
|      * | ||||
|      * @return True if alpha test is enabled. | ||||
|      * | ||||
|      * @see RenderState#setAlphaTest(boolean) | ||||
|      * @deprecated To use alpha test, set the <code>AlphaDiscardThreshold</code> | ||||
|      * material parameter. | ||||
|      * @return false | ||||
|      */ | ||||
|     public boolean isAlphaTest() { | ||||
|         return alphaTest; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1108,14 +1193,12 @@ public class RenderState implements Cloneable, Savable { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieve the alpha falloff value. | ||||
|      * | ||||
|      * @return the alpha falloff value. | ||||
|      * | ||||
|      * @see RenderState#setAlphaFallOff(float) | ||||
|      * @return 0 | ||||
|      * @deprecated | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public float getAlphaFallOff() { | ||||
|         return alphaFallOff; | ||||
|         return 0f; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1130,14 +1213,12 @@ public class RenderState implements Cloneable, Savable { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieve the alpha comparison function | ||||
|      * | ||||
|      * @return the alpha comparison function | ||||
|      * | ||||
|      * @see RenderState#setAlphaFunc(com.jme3.material.RenderState.TestFunction) | ||||
|      * @return {@link TestFunction#Greater}. | ||||
|      * @deprecated | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public TestFunction getAlphaFunc() { | ||||
|         return alphaFunc; | ||||
|         return TestFunction.Greater; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1150,18 +1231,19 @@ public class RenderState implements Cloneable, Savable { | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public boolean isApplyAlphaFallOff() { | ||||
|         return applyAlphaFallOff; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isApplyAlphaTest() { | ||||
|         return applyAlphaTest; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isApplyBlendMode() { | ||||
|         return applyBlendMode; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isApplyBlendEquation() { | ||||
|         return applyBlendEquation; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isApplyBlendEquationAlpha() { | ||||
|         return applyBlendEquationAlpha; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isApplyColorWrite() { | ||||
|         return applyColorWrite; | ||||
|     } | ||||
| @ -1178,9 +1260,6 @@ public class RenderState implements Cloneable, Savable { | ||||
|         return applyDepthWrite; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isApplyPointSprite() { | ||||
|         return applyPointSprite; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isApplyPolyOffset() { | ||||
|         return applyPolyOffset; | ||||
| @ -1194,9 +1273,6 @@ public class RenderState implements Cloneable, Savable { | ||||
|         return applyDepthFunc; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isApplyAlphaFunc() { | ||||
|         return applyAlphaFunc; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isApplyLineWidth() { | ||||
|         return applyLineWidth; | ||||
| @ -1208,7 +1284,6 @@ public class RenderState implements Cloneable, Savable { | ||||
|     public int contentHashCode() { | ||||
|         if (cachedHashCode == -1){ | ||||
|             int hash = 7; | ||||
|             hash = 79 * hash + (this.pointSprite ? 1 : 0); | ||||
|             hash = 79 * hash + (this.wireframe ? 1 : 0); | ||||
|             hash = 79 * hash + (this.cullMode != null ? this.cullMode.hashCode() : 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.colorWrite ? 1 : 0); | ||||
|             hash = 79 * hash + (this.blendMode != null ? this.blendMode.hashCode() : 0); | ||||
|             hash = 79 * hash + (this.alphaTest ? 1 : 0); | ||||
|             hash = 79 * hash + (this.alphaFunc != null ? this.alphaFunc.hashCode() : 0); | ||||
|             hash = 79 * hash + Float.floatToIntBits(this.alphaFallOff); | ||||
|             hash = 79 * hash + (this.blendEquation != null ? this.blendEquation.hashCode() : 0); | ||||
|             hash = 79 * hash + (this.blendEquationAlpha != null ? this.blendEquationAlpha.hashCode() : 0); | ||||
|             hash = 79 * hash + Float.floatToIntBits(this.offsetFactor); | ||||
|             hash = 79 * hash + Float.floatToIntBits(this.offsetUnits); | ||||
|             hash = 79 * hash + (this.offsetEnabled ? 1 : 0); | ||||
| @ -1263,11 +1337,6 @@ public class RenderState implements Cloneable, Savable { | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         if (additionalState.applyPointSprite) { | ||||
|             state.pointSprite = additionalState.pointSprite; | ||||
|         } else { | ||||
|             state.pointSprite = pointSprite; | ||||
|         } | ||||
|         if (additionalState.applyWireFrame) { | ||||
|             state.wireframe = additionalState.wireframe; | ||||
|         } else { | ||||
| @ -1299,27 +1368,22 @@ public class RenderState implements Cloneable, Savable { | ||||
|         } else { | ||||
|             state.colorWrite = colorWrite; | ||||
|         } | ||||
|         if (additionalState.applyBlendEquation) { | ||||
|             state.blendEquation = additionalState.blendEquation; | ||||
|         } else { | ||||
|             state.blendEquation = blendEquation; | ||||
|         } | ||||
|         if (additionalState.applyBlendEquationAlpha) { | ||||
|             state.blendEquationAlpha = additionalState.blendEquationAlpha; | ||||
|         } else { | ||||
|             state.blendEquationAlpha = blendEquationAlpha; | ||||
|         }         | ||||
|         if (additionalState.applyBlendMode) { | ||||
|             state.blendMode = additionalState.blendMode; | ||||
|         } else { | ||||
|             state.blendMode = blendMode; | ||||
|         } | ||||
|         if (additionalState.applyAlphaTest) { | ||||
|             state.alphaTest = additionalState.alphaTest; | ||||
|         } else { | ||||
|             state.alphaTest = alphaTest; | ||||
|         } | ||||
|         if (additionalState.applyAlphaFunc) { | ||||
|             state.alphaFunc = additionalState.alphaFunc; | ||||
|         } else { | ||||
|             state.alphaFunc = alphaFunc; | ||||
|         } | ||||
| 
 | ||||
|         if (additionalState.applyAlphaFallOff) { | ||||
|             state.alphaFallOff = additionalState.alphaFallOff; | ||||
|         } else { | ||||
|             state.alphaFallOff = alphaFallOff; | ||||
|         } | ||||
|         if (additionalState.applyPolyOffset) { | ||||
|             state.offsetEnabled = additionalState.offsetEnabled; | ||||
|             state.offsetFactor = additionalState.offsetFactor; | ||||
| @ -1364,16 +1428,14 @@ public class RenderState implements Cloneable, Savable { | ||||
|         state.cachedHashCode = -1; | ||||
|         return state; | ||||
|     } | ||||
|      public void set(RenderState state) { | ||||
|         pointSprite = state.pointSprite; | ||||
| 
 | ||||
|     public void set(RenderState state) { | ||||
|         wireframe = state.wireframe; | ||||
|         cullMode = state.cullMode; | ||||
|         depthWrite = state.depthWrite; | ||||
|         depthTest = state.depthTest; | ||||
|         colorWrite = state.colorWrite; | ||||
|         blendMode = state.blendMode; | ||||
|         alphaTest = state.alphaTest; | ||||
|         alphaFallOff = state.alphaFallOff; | ||||
|         offsetEnabled = state.offsetEnabled; | ||||
|         offsetFactor = state.offsetFactor; | ||||
|         offsetUnits = state.offsetUnits; | ||||
| @ -1386,30 +1448,27 @@ public class RenderState implements Cloneable, Savable { | ||||
|         backStencilDepthPassOperation = state.backStencilDepthPassOperation; | ||||
|         frontStencilFunction = state.frontStencilFunction; | ||||
|         backStencilFunction = state.backStencilFunction; | ||||
|         blendEquationAlpha = state.blendEquationAlpha; | ||||
|         blendEquation = state.blendEquation; | ||||
|         depthFunc = state.depthFunc; | ||||
|         alphaFunc = state.alphaFunc; | ||||
|         lineWidth = state.lineWidth; | ||||
| 
 | ||||
|         applyPointSprite = true; | ||||
|         applyWireFrame =  true; | ||||
|         applyCullMode =  true; | ||||
|         applyDepthWrite =  true; | ||||
|         applyDepthTest =  true; | ||||
|         applyColorWrite = true; | ||||
|         applyBlendMode =  true; | ||||
|         applyAlphaTest =  true; | ||||
|         applyAlphaFallOff =  true; | ||||
|         applyBlendEquation =  true; | ||||
|         applyBlendEquationAlpha =  true; | ||||
|         applyBlendMode = true; | ||||
|         applyPolyOffset =  true; | ||||
|         applyDepthFunc =  true; | ||||
|         applyAlphaFunc =  false; | ||||
|         applyDepthFunc = true; | ||||
|         applyLineWidth = true; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return "RenderState[\n" | ||||
|                 + "pointSprite=" + pointSprite | ||||
|                 + "\napplyPointSprite=" + applyPointSprite | ||||
|                 + "\nwireframe=" + wireframe | ||||
|                 + "\napplyWireFrame=" + applyWireFrame | ||||
|                 + "\ncullMode=" + cullMode | ||||
| @ -1421,13 +1480,11 @@ public class RenderState implements Cloneable, Savable { | ||||
|                 + "\napplyDepthTest=" + applyDepthTest | ||||
|                 + "\ncolorWrite=" + colorWrite | ||||
|                 + "\napplyColorWrite=" + applyColorWrite | ||||
|                 + "\nblendEquation=" + blendEquation | ||||
|                 + "\napplyBlendEquation=" + applyBlendEquation | ||||
|                 + "\napplyBlendEquationAlpha=" + applyBlendEquationAlpha | ||||
|                 + "\nblendMode=" + blendMode | ||||
|                 + "\napplyBlendMode=" + applyBlendMode | ||||
|                 + "\nalphaTest=" + alphaTest | ||||
|                 + "\nalphaFunc=" + alphaFunc | ||||
|                 + "\napplyAlphaTest=" + applyAlphaTest | ||||
|                 + "\nalphaFallOff=" + alphaFallOff | ||||
|                 + "\napplyAlphaFallOff=" + applyAlphaFallOff | ||||
|                 + "\noffsetEnabled=" + offsetEnabled | ||||
|                 + "\napplyPolyOffset=" + applyPolyOffset | ||||
|                 + "\noffsetFactor=" + offsetFactor | ||||
|  | ||||
| @ -31,27 +31,30 @@ | ||||
|  */ | ||||
| package com.jme3.material; | ||||
| 
 | ||||
| import com.jme3.material.logic.TechniqueDefLogic; | ||||
| 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.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.EnumSet; | ||||
| import java.util.List; | ||||
| import java.util.logging.Logger; | ||||
| 
 | ||||
| /** | ||||
|  * Represents a technique instance. | ||||
|  */ | ||||
| public class Technique /* implements Savable */ { | ||||
| public final class Technique { | ||||
| 
 | ||||
|     private static final Logger logger = Logger.getLogger(Technique.class.getName()); | ||||
|     private TechniqueDef def; | ||||
|     private Material owner; | ||||
|     private ArrayList<Uniform> worldBindUniforms; | ||||
|     private DefineList defines; | ||||
|     private Shader shader; | ||||
|     private boolean needReload = true; | ||||
|     private final TechniqueDef def; | ||||
|     private final Material owner; | ||||
|     private final DefineList paramDefines; | ||||
|     private final DefineList dynamicDefines; | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a new technique instance that implements the given | ||||
| @ -63,14 +66,8 @@ public class Technique /* implements Savable */ { | ||||
|     public Technique(Material owner, TechniqueDef def) { | ||||
|         this.owner = owner; | ||||
|         this.def = def; | ||||
|         this.worldBindUniforms = new ArrayList<Uniform>(); | ||||
|         this.defines = new DefineList(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Serialization only. Do not use. | ||||
|      */ | ||||
|     public Technique() { | ||||
|         this.paramDefines = def.createDefineList(); | ||||
|         this.dynamicDefines = def.createDefineList(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -84,158 +81,126 @@ public class Technique /* implements Savable */ { | ||||
|         return def; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the shader currently used by this technique instance. | ||||
|      * <p> | ||||
|      * 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() { | ||||
|         return shader; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a list of uniforms that implements the world parameters | ||||
|      * that were requested by the material definition. | ||||
|      *  | ||||
|      * @return a list of uniforms implementing the world parameters. | ||||
|      */ | ||||
|     public List<Uniform> getWorldBindUniforms() { | ||||
|         return worldBindUniforms; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Called by the material to tell the technique a parameter was modified. | ||||
|      * Specify <code>null</code> for value if the param is to be cleared. | ||||
|      */ | ||||
|     void notifyParamChanged(String paramName, VarType type, Object value) { | ||||
|         // Check if there's a define binding associated with this | ||||
|         // parameter. | ||||
|         String defineName = def.getShaderParamDefine(paramName); | ||||
|         if (defineName != null) { | ||||
|             // 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; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     final void notifyParamChanged(String paramName, VarType type, Object value) { | ||||
|         Integer defineId = def.getShaderParamDefineId(paramName); | ||||
| 
 | ||||
|     void updateUniformParam(String paramName, VarType type, Object value) { | ||||
|         if (paramName == null) { | ||||
|             throw new IllegalArgumentException(); | ||||
|         } | ||||
|          | ||||
|         Uniform u = shader.getUniform(paramName); | ||||
|         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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns true if the technique must be reloaded. | ||||
|      * <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. | ||||
|      */ | ||||
|     public boolean isNeedReload() { | ||||
|         return needReload; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Prepares the technique for use by loading the shader and setting | ||||
|      * the proper defines based on material parameters. | ||||
|      *  | ||||
|      * @param assetManager The asset manager to use for loading shaders. | ||||
|      */ | ||||
|     public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet<Caps> rendererCaps, RenderManager rm) { | ||||
|         if (techniqueSwitched) { | ||||
|             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 (defineId == null) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         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; | ||||
|         paramDefines.set(defineId, type, value); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Computes the define list | ||||
|      * @return the complete define list | ||||
|      * Called by the material to tell the technique that it has been made | ||||
|      * current. | ||||
|      * The technique updates dynamic defines based on the | ||||
|      * currently set material parameters. | ||||
|      */ | ||||
|     final void notifyTechniqueSwitched() { | ||||
|         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) { | ||||
|         for (MatParamOverride override : overrides) { | ||||
|             if (!override.isEnabled()) { | ||||
|                 continue; | ||||
|             } | ||||
|             Integer defineId = def.getShaderParamDefineId(override.name); | ||||
|             if (defineId != null) { | ||||
|                 if (def.getDefineIdType(defineId) == override.type) { | ||||
|                     defineList.set(defineId, override.type, override.value); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Called by the material to determine which shader to use for rendering. | ||||
|      *  | ||||
|      * 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) { | ||||
|             applyOverrides(dynamicDefines, forcedOverrides); | ||||
|         } | ||||
| 
 | ||||
|         return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Render the technique according to its {@link TechniqueDefLogic}. | ||||
|      *  | ||||
|      * @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. | ||||
|      */ | ||||
|     void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) { | ||||
|         TechniqueDefLogic logic = def.getLogic(); | ||||
|         logic.render(renderManager, shader, geometry, lights); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Get the {@link DefineList} for dynamic defines. | ||||
|      *  | ||||
|      * Dynamic defines are used to implement material parameter -> define | ||||
|      * bindings as well as {@link TechniqueDefLogic} specific functionality. | ||||
|      *  | ||||
|      * @return all dynamic defines. | ||||
|      */ | ||||
|     public DefineList getDynamicDefines() { | ||||
|         return dynamicDefines; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * @return nothing. | ||||
|      * | ||||
|      * @deprecated Preset defines are precompiled into | ||||
|      * {@link TechniqueDef#getShaderPrologue()}, whereas dynamic defines are | ||||
|      * available via {@link #getParamDefines()}. | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public DefineList getAllDefines() { | ||||
|         DefineList allDefines = new DefineList(); | ||||
|         allDefines.addFrom(def.getShaderPresetDefines()); | ||||
|         allDefines.addFrom(defines); | ||||
|         return allDefines; | ||||
|     }  | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
|      | ||||
|     /* | ||||
|     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); | ||||
|     /** | ||||
|      * Compute the sort ID. Similar to {@link Object#hashCode()} but used | ||||
|      * for sorting geometries for rendering. | ||||
|      *  | ||||
|      * @return the sort ID for this technique instance. | ||||
|      */ | ||||
|     public int getSortId() { | ||||
|         int hash = 17; | ||||
|         hash = hash * 23 + def.getSortId(); | ||||
|         hash = hash * 23 + paramDefines.hashCode(); | ||||
|         return hash; | ||||
|     } | ||||
| 
 | ||||
|     public void read(JmeImporter im) throws IOException { | ||||
|         InputCapsule ic = im.getCapsule(this); | ||||
|         def = (TechniqueDef) ic.readSavable("def", null); | ||||
|         worldBindUniforms = ic.readSavableArrayList("worldBindUniforms", null); | ||||
|         defines = (DefineList) ic.readSavable("defines", null); | ||||
|         shader = (Shader) ic.readSavable("shader", null); | ||||
|     } | ||||
|     */ | ||||
| } | ||||
|  | ||||
| @ -31,9 +31,12 @@ | ||||
|  */ | ||||
| package com.jme3.material; | ||||
| 
 | ||||
| import com.jme3.material.logic.TechniqueDefLogic; | ||||
| import com.jme3.asset.AssetManager; | ||||
| import com.jme3.export.*; | ||||
| import com.jme3.renderer.Caps; | ||||
| import com.jme3.shader.*; | ||||
| import com.jme3.shader.Shader.ShaderType; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.*; | ||||
| @ -50,6 +53,14 @@ public class TechniqueDef implements Savable { | ||||
|      */ | ||||
|     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. | ||||
|      */ | ||||
| @ -91,13 +102,19 @@ public class TechniqueDef implements Savable { | ||||
|         PostPass, | ||||
|     } | ||||
| 
 | ||||
|     private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class); | ||||
|     private final EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class); | ||||
|     private String name; | ||||
| 
 | ||||
|     private int sortId; | ||||
|      | ||||
|     private EnumMap<Shader.ShaderType,String> shaderLanguages; | ||||
|     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 List<ShaderNode> shaderNodes; | ||||
|     private ShaderGenerationInfo shaderGenerationInfo; | ||||
| @ -106,10 +123,10 @@ public class TechniqueDef implements Savable { | ||||
|     private RenderState renderState; | ||||
|     private RenderState forcedRenderState; | ||||
| 
 | ||||
|     private LightMode lightMode   = LightMode.Disable; | ||||
|     private LightMode lightMode = LightMode.Disable; | ||||
|     private ShadowMode shadowMode = ShadowMode.Disable; | ||||
|     private TechniqueDefLogic logic; | ||||
| 
 | ||||
|     private HashMap<String, String> defineParams; | ||||
|     private ArrayList<UniformBinding> worldBinds; | ||||
| 
 | ||||
|     /** | ||||
| @ -117,25 +134,38 @@ public class TechniqueDef implements Savable { | ||||
|      * <p> | ||||
|      * Used internally by the J3M/J3MD loader. | ||||
|      * | ||||
|      * @param name The name of the technique, should be set to <code>null</code> | ||||
|      * for default techniques. | ||||
|      * @param name The name of the technique | ||||
|      */ | ||||
|     public TechniqueDef(String name){ | ||||
|     public TechniqueDef(String name, int sortId){ | ||||
|         this(); | ||||
|         this.name = name == null ? "Default" : name; | ||||
|         this.sortId = sortId; | ||||
|         this.name = name; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Serialization only. Do not use. | ||||
|      */ | ||||
|     public TechniqueDef(){ | ||||
|         shaderLanguages=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); | ||||
|         shaderNames=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); | ||||
|     public TechniqueDef() { | ||||
|         shaderLanguages = 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. | ||||
|      * Default techniques have the name "Default". | ||||
|      * Default | ||||
|      * techniques have the name {@link #DEFAULT_TECHNIQUE_NAME}. | ||||
|      * | ||||
|      * @return the name of this technique | ||||
|      */ | ||||
| @ -162,7 +192,15 @@ public class TechniqueDef implements Savable { | ||||
|     public void setLightMode(LightMode lightMode) { | ||||
|         this.lightMode = lightMode; | ||||
|     } | ||||
|      | ||||
|     public void setLogic(TechniqueDefLogic logic) { | ||||
|         this.logic = logic; | ||||
|     } | ||||
| 
 | ||||
|     public TechniqueDefLogic getLogic() { | ||||
|         return logic; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Returns the shadow mode. | ||||
|      * @return the shadow mode. | ||||
| @ -224,14 +262,6 @@ public class TechniqueDef implements Savable { | ||||
|         return noRender; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @deprecated jME3 always requires shaders now | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public boolean isUsingShaders(){ | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns true if this technique uses Shader Nodes, false otherwise. | ||||
|      * | ||||
| @ -273,7 +303,188 @@ public class TechniqueDef implements Savable { | ||||
|         requiredCaps.add(fragCap); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set a string which is prepended to every shader used by this technique. | ||||
|      *  | ||||
|      * Typically this is used for preset defines. | ||||
|      *  | ||||
|      * @param shaderPrologue The prologue to append before the technique's shaders. | ||||
|      */ | ||||
|     public void setShaderPrologue(String shaderPrologue) { | ||||
|         this.shaderPrologue = shaderPrologue; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * @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. | ||||
|      * | ||||
|      * @param paramName The parameter name to look up | ||||
|      * @return The define name | ||||
|      * | ||||
|      * @see #addShaderParamDefine(java.lang.String, java.lang.String) | ||||
|      */ | ||||
|     public String getShaderParamDefine(String paramName){ | ||||
|         Integer defineId = paramToDefineId.get(paramName); | ||||
|         if (defineId != null) { | ||||
|             return defineNames.get(defineId); | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 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. | ||||
|      * <p> | ||||
|      * Any time the material parameter on the parent material is altered, | ||||
|      * the appropriate define on the technique will be modified as well. | ||||
|      * When set, the material parameter will be mapped to an integer define,  | ||||
|      * typically <code>1</code> if it is set, unless it is an integer or a float, | ||||
|      * in which case it will converted into an integer. | ||||
|      * | ||||
|      * @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 | ||||
|      */ | ||||
|     public void addShaderParamDefine(String paramName, VarType paramType, String defineName){ | ||||
|         int defineId = defineNames.size(); | ||||
|          | ||||
|         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."); | ||||
|         } | ||||
|          | ||||
|         defineNames.add(defineName); | ||||
|         defineTypes.add(defineType); | ||||
|         return defineId; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the names of all defines declared on this technique definition. | ||||
|      * | ||||
|      * The defines are returned in order of declaration. | ||||
|      * | ||||
|      * @return the names of all defines declared. | ||||
|      */ | ||||
|     public String[] getDefineNames() { | ||||
|         return defineNames.toArray(new String[0]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the types of all defines declared on this technique definition. | ||||
|      * | ||||
|      * The types are returned in order of declaration. | ||||
|      * | ||||
|      * @return the types of all defines declared. | ||||
|      */ | ||||
|     public VarType[] getDefineTypes() { | ||||
|         return defineTypes.toArray(new VarType[0]); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 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. | ||||
|      * | ||||
| @ -301,71 +512,6 @@ public class TechniqueDef implements Savable { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the define name which the given material parameter influences. | ||||
|      * | ||||
|      * @param paramName The parameter name to look up | ||||
|      * @return The define name | ||||
|      * | ||||
|      * @see #addShaderParamDefine(java.lang.String, java.lang.String) | ||||
|      */ | ||||
|     public String getShaderParamDefine(String paramName){ | ||||
|         if (defineParams == null) { | ||||
|             return null; | ||||
|         } | ||||
|         return defineParams.get(paramName); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Adds a define linked to a material parameter. | ||||
|      * <p> | ||||
|      * Any time the material parameter on the parent material is altered, | ||||
|      * the appropriate define on the technique will be modified as well. | ||||
|      * See the method | ||||
|      * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) } | ||||
|      * on the exact details of how the material parameter changes the define. | ||||
|      * | ||||
|      * @param paramName The name of the material parameter to link to. | ||||
|      * @param defineName The name of the define parameter, e.g. USE_LIGHTING | ||||
|      */ | ||||
|     public void addShaderParamDefine(String paramName, String defineName){ | ||||
|         if (defineParams == null) { | ||||
|             defineParams = new HashMap<String, String>(); | ||||
|         } | ||||
|         defineParams.put(paramName, defineName); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the {@link DefineList} for the preset defines. | ||||
|      * | ||||
|      * @return the {@link DefineList} for the preset defines. | ||||
|      * | ||||
|      * @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object) | ||||
|      */ | ||||
|     public DefineList getShaderPresetDefines() { | ||||
|         return presetDefines; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Adds a preset define. | ||||
|      * <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 | ||||
|      * @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 | ||||
|      */ | ||||
|     public void addShaderPresetDefine(String defineName, VarType type, Object value){ | ||||
|         if (presetDefines == null) { | ||||
|             presetDefines = new DefineList(); | ||||
|         } | ||||
|         presetDefines.set(defineName, type, value); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the name of the fragment shader used by the technique, or null | ||||
|      * if no fragment shader is specified. | ||||
| @ -467,7 +613,7 @@ public class TechniqueDef implements Savable { | ||||
|         oc.write(shaderLanguages.get(Shader.ShaderType.TessellationControl), "tsctrlLanguage", 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(shadowMode, "shadowMode", ShadowMode.Disable); | ||||
|         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.TessellationControl,ic.readString("tsctrlName", 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); | ||||
|         shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable); | ||||
|         renderState = (RenderState) ic.readSavable("renderState", null); | ||||
| @ -547,9 +693,14 @@ public class TechniqueDef implements Savable { | ||||
|         this.shaderGenerationInfo = shaderGenerationInfo; | ||||
|     } | ||||
| 
 | ||||
|     //todo: make toString return something usefull | ||||
|     @Override | ||||
|     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, | ||||
|      * the plane state of the bounding volume must be saved and restored, e.g: | ||||
|      * <code>BoundingVolume bv;<br/> | ||||
|      * Camera c;<br/> | ||||
|      * int planeState = bv.getPlaneState();<br/> | ||||
|      * bv.setPlaneState(0);<br/> | ||||
|      * c.contains(bv);<br/> | ||||
|      * bv.setPlaneState(plateState);<br/> | ||||
|      * <code>BoundingVolume bv;<br> | ||||
|      * Camera c;<br> | ||||
|      * int planeState = bv.getPlaneState();<br> | ||||
|      * bv.setPlaneState(0);<br> | ||||
|      * c.contains(bv);<br> | ||||
|      * bv.setPlaneState(plateState);<br> | ||||
|      * </code> | ||||
|      * | ||||
|      * @param bound the bound to check for culling | ||||
|  | ||||
| @ -32,51 +32,34 @@ | ||||
| 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}. | ||||
|  * <p> | ||||
|  * For example, maximum texture sizes or number of samples. | ||||
|  *  | ||||
|  * | ||||
|  * @author Kirill Vainer | ||||
|  */ | ||||
| public enum Limits { | ||||
|     /** | ||||
|      * Maximum number of vertex texture units, or number of textures | ||||
|      * that can be used in the vertex shader. | ||||
|      * Maximum number of vertex texture units, or number of textures that can be | ||||
|      * used in the vertex shader. | ||||
|      */ | ||||
|     VertexTextureUnits, | ||||
|      | ||||
|     /** | ||||
|      * Maximum number of fragment texture units, or number of textures | ||||
|      * that can be used in the fragment shader. | ||||
|      * Maximum number of fragment texture units, or number of textures that can | ||||
|      * be used in the fragment shader. | ||||
|      */ | ||||
|     FragmentTextureUnits, | ||||
|      | ||||
|     FragmentUniforms, | ||||
|      | ||||
|     VertexAttributes, | ||||
|      | ||||
|     FrameBufferSamples, | ||||
|      | ||||
|     FrameBufferAttachments, | ||||
|      | ||||
|     FrameBufferMrtAttachments, | ||||
|      | ||||
|     RenderBufferSize, | ||||
|      | ||||
|     TextureSize, | ||||
|      | ||||
|     CubemapSize, | ||||
|      | ||||
|     VertexCount, | ||||
|      | ||||
|     TriangleCount, | ||||
|      | ||||
|     ColorTextureSamples, | ||||
|      | ||||
|     DepthTextureSamples, | ||||
| 
 | ||||
|     FragmentUniformVectors, | ||||
|     VertexUniformVectors, | ||||
|      | ||||
|     VertexAttributes, | ||||
|     FrameBufferSamples, | ||||
|     FrameBufferAttachments, | ||||
|     FrameBufferMrtAttachments, | ||||
|     RenderBufferSize, | ||||
|     TextureSize, | ||||
|     CubemapSize, | ||||
|     ColorTextureSamples, | ||||
|     DepthTextureSamples, | ||||
|     TextureAnisotropy, | ||||
| } | ||||
|  | ||||
| @ -55,16 +55,6 @@ public class RenderContext { | ||||
|      */ | ||||
|     public boolean depthTestEnabled = false; | ||||
| 
 | ||||
|     /** | ||||
|      * @see RenderState#setAlphaFallOff(float)  | ||||
|      */ | ||||
|     public float alphaTestFallOff = 0f; | ||||
| 
 | ||||
|     /** | ||||
|      * @see RenderState#setAlphaTest(boolean)  | ||||
|      */ | ||||
|     public boolean alphaTestEnabled = false; | ||||
| 
 | ||||
|     /** | ||||
|      * @see RenderState#setDepthWrite(boolean)  | ||||
|      */ | ||||
| @ -110,16 +100,21 @@ public class RenderContext { | ||||
|      */ | ||||
|     public RenderState.BlendMode blendMode = RenderState.BlendMode.Off; | ||||
| 
 | ||||
|     /** | ||||
|      * @see RenderState#setBlendEquation(com.jme3.material.RenderState.BlendEquation)  | ||||
|      */ | ||||
|     public RenderState.BlendEquation blendEquation = RenderState.BlendEquation.Add; | ||||
|      | ||||
|     /** | ||||
|      * @see RenderState#setBlendEquationAlpha(com.jme3.material.RenderState.BlendEquationAlpha)  | ||||
|      */ | ||||
|     public RenderState.BlendEquationAlpha blendEquationAlpha = RenderState.BlendEquationAlpha.InheritColor; | ||||
| 
 | ||||
|     /** | ||||
|      * @see RenderState#setWireframe(boolean)  | ||||
|      */ | ||||
|     public boolean wireframe = false; | ||||
| 
 | ||||
|     /** | ||||
|      * @see RenderState#setPointSprite(boolean)  | ||||
|      */ | ||||
|     public boolean pointSprite = false; | ||||
| 
 | ||||
|     /** | ||||
|      * @see Renderer#setShader(com.jme3.shader.Shader)  | ||||
|      */ | ||||
| @ -261,7 +256,6 @@ public class RenderContext { | ||||
|     public void reset(){ | ||||
|         cullMode = RenderState.FaceCullMode.Off; | ||||
|         depthTestEnabled = false; | ||||
|         alphaTestFallOff = 0f; | ||||
|         depthWriteEnabled = false; | ||||
|         colorWriteEnabled = false; | ||||
|         clipRectEnabled = false; | ||||
| @ -270,6 +264,8 @@ public class RenderContext { | ||||
|         polyOffsetUnits = 0; | ||||
|         pointSize = 1; | ||||
|         blendMode = RenderState.BlendMode.Off; | ||||
|         blendEquation = RenderState.BlendEquation.Add; | ||||
|         blendEquationAlpha = RenderState.BlendEquationAlpha.InheritColor; | ||||
|         wireframe = false; | ||||
|         boundShaderProgram = 0; | ||||
|         boundShader = null; | ||||
|  | ||||
| @ -34,8 +34,13 @@ package com.jme3.renderer; | ||||
| import com.jme3.light.DefaultLightFilter; | ||||
| import com.jme3.light.LightFilter; | ||||
| import com.jme3.light.LightList; | ||||
| import com.jme3.material.*; | ||||
| import com.jme3.math.Matrix4f; | ||||
| import com.jme3.material.MatParamOverride; | ||||
| import com.jme3.material.Material; | ||||
| import com.jme3.material.MaterialDef; | ||||
| import com.jme3.material.RenderState; | ||||
| import com.jme3.material.Technique; | ||||
| import com.jme3.material.TechniqueDef; | ||||
| import com.jme3.math.*; | ||||
| import com.jme3.post.SceneProcessor; | ||||
| import com.jme3.profile.AppProfiler; | ||||
| 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.ShadowMode; | ||||
| import com.jme3.scene.*; | ||||
| import com.jme3.shader.Uniform; | ||||
| import com.jme3.shader.Shader; | ||||
| import com.jme3.shader.UniformBinding; | ||||
| import com.jme3.shader.UniformBindingManager; | ||||
| import com.jme3.system.NullRenderer; | ||||
| import com.jme3.system.Timer; | ||||
| import com.jme3.util.SafeArrayList; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| @ -70,25 +74,26 @@ import java.util.logging.Logger; | ||||
| public class RenderManager { | ||||
| 
 | ||||
|     private static final Logger logger = Logger.getLogger(RenderManager.class.getName()); | ||||
|     private Renderer renderer; | ||||
|     private UniformBindingManager uniformBindingManager = new UniformBindingManager(); | ||||
|     private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>(); | ||||
|     private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>(); | ||||
|     private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>(); | ||||
|     private final Renderer renderer; | ||||
|     private final UniformBindingManager uniformBindingManager = new UniformBindingManager(); | ||||
|     private final ArrayList<ViewPort> preViewPorts = new ArrayList<>(); | ||||
|     private final ArrayList<ViewPort> viewPorts = new ArrayList<>(); | ||||
|     private final ArrayList<ViewPort> postViewPorts = new ArrayList<>(); | ||||
|     private Camera prevCam = null; | ||||
|     private Material forcedMaterial = null; | ||||
|     private String forcedTechnique = null; | ||||
|     private RenderState forcedRenderState = null; | ||||
|     private final List<MatParamOverride> forcedOverrides = new ArrayList<>(); | ||||
|     private int viewX, viewY, viewWidth, viewHeight; | ||||
|     private Matrix4f orthoMatrix = new Matrix4f(); | ||||
|     private LightList filteredLightList = new LightList(null); | ||||
|     private String tmpTech; | ||||
|     private final Matrix4f orthoMatrix = new Matrix4f(); | ||||
|     private final LightList filteredLightList = new LightList(null); | ||||
|     private boolean handleTranlucentBucket = true; | ||||
|     private AppProfiler prof; | ||||
|     private LightFilter lightFilter = new DefaultLightFilter(); | ||||
|     private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass; | ||||
|     private int singlePassLightBatchSize = 1; | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Create a high-level rendering interface over the | ||||
|      * low-level rendering interface. | ||||
| @ -423,6 +428,44 @@ public class RenderManager { | ||||
|         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.  | ||||
|      * <p> | ||||
| @ -480,8 +523,8 @@ public class RenderManager { | ||||
|      * Updates the given list of uniforms with {@link UniformBinding uniform bindings} | ||||
|      * based on the current world state. | ||||
|      */ | ||||
|     public void updateUniformBindings(List<Uniform> params) { | ||||
|         uniformBindingManager.updateUniformBindings(params); | ||||
|     public void updateUniformBindings(Shader shader) { | ||||
|         uniformBindingManager.updateUniformBindings(shader); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -508,45 +551,54 @@ public class RenderManager { | ||||
|      * for rendering the material, and the material's own render state is ignored. | ||||
|      * 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 RenderState | ||||
|      * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)  | ||||
|      * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)  | ||||
|      */ | ||||
|     public void renderGeometry(Geometry g) { | ||||
|         if (g.isIgnoreTransform()) { | ||||
|     public void renderGeometry(Geometry geom) { | ||||
|         if (geom.isIgnoreTransform()) { | ||||
|             setWorldMatrix(Matrix4f.IDENTITY); | ||||
|         } else { | ||||
|             setWorldMatrix(g.getWorldMatrix()); | ||||
|             setWorldMatrix(geom.getWorldMatrix()); | ||||
|         } | ||||
|          | ||||
|         // Perform light filtering if we have a light filter. | ||||
|         LightList lightList = g.getWorldLightList(); | ||||
|         LightList lightList = geom.getWorldLightList(); | ||||
|          | ||||
|         if (lightFilter != null) { | ||||
|             filteredLightList.clear(); | ||||
|             lightFilter.filterLights(g, filteredLightList); | ||||
|             lightFilter.filterLights(geom, filteredLightList); | ||||
|             lightList = filteredLightList; | ||||
|         } | ||||
| 
 | ||||
|         Material material = geom.getMaterial(); | ||||
| 
 | ||||
|         //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 | ||||
|         //else the geom is not rendered | ||||
|         if (forcedTechnique != null) { | ||||
|             if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) { | ||||
|                 tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default"; | ||||
|                 g.getMaterial().selectTechnique(forcedTechnique, this); | ||||
|             MaterialDef matDef = material.getMaterialDef(); | ||||
|             if (matDef.getTechniqueDefs(forcedTechnique) != null) { | ||||
| 
 | ||||
|                 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 | ||||
|                 RenderState tmpRs = forcedRenderState; | ||||
|                 if (g.getMaterial().getActiveTechnique().getDef().getForcedRenderState() != null) { | ||||
|                 if (geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState() != null) { | ||||
|                     //forcing forced technique renderState | ||||
|                     forcedRenderState = g.getMaterial().getActiveTechnique().getDef().getForcedRenderState(); | ||||
|                     forcedRenderState = geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState(); | ||||
|                 } | ||||
|                 // use geometry's material | ||||
|                 g.getMaterial().render(g, lightList, this); | ||||
|                 g.getMaterial().selectTechnique(tmpTech, this); | ||||
|                 material.render(geom, lightList, this); | ||||
|                 material.selectTechnique(previousTechniqueName, this); | ||||
| 
 | ||||
|                 //restoring forcedRenderState | ||||
|                 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 | ||||
|             } else if (forcedMaterial != null) { | ||||
|                 // use forced material | ||||
|                 forcedMaterial.render(g, lightList, this); | ||||
|                 forcedMaterial.render(geom, lightList, this); | ||||
|             } | ||||
|         } else if (forcedMaterial != null) { | ||||
|             // use forced material | ||||
|             forcedMaterial.render(g, lightList, this); | ||||
|             forcedMaterial.render(geom, lightList, this); | ||||
|         } else { | ||||
|             g.getMaterial().render(g, lightList, this); | ||||
|             material.render(geom, lightList, this); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -612,7 +664,9 @@ public class RenderManager { | ||||
| 
 | ||||
|             gm.getMaterial().preload(this); | ||||
|             Mesh mesh = gm.getMesh(); | ||||
|             if (mesh != null) { | ||||
|             if (mesh != null | ||||
|                     && mesh.getVertexCount() != 0 | ||||
|                     && mesh.getTriangleCount() != 0) { | ||||
|                 for (VertexBuffer vb : mesh.getBufferList().getArray()) { | ||||
|                     if (vb.getData() != null && vb.getUsage() != VertexBuffer.Usage.CpuOnly) { | ||||
|                         renderer.updateBufferData(vb); | ||||
| @ -637,8 +691,10 @@ public class RenderManager { | ||||
|      * <p> | ||||
|      * In addition to enqueuing the visible geometries, this method | ||||
|      * also scenes which cast or receive shadows, by putting them into the | ||||
|      * RenderQueue's {@link RenderQueue#renderShadowQueue(GeometryList, RenderManager, Camera, boolean) shadow queue}. | ||||
|      * Each Spatial which has its {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode} | ||||
|      * RenderQueue's  | ||||
|      * {@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 | ||||
|      * this process does not check for frustum culling on any  | ||||
|      * {@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> | ||||
|      * <li>If any objects remained in the render queue, they are removed | ||||
|      * 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> | ||||
|      * </ul> | ||||
|      *  | ||||
|  | ||||
| @ -43,6 +43,7 @@ import com.jme3.texture.Image; | ||||
| import com.jme3.texture.Texture; | ||||
| import com.jme3.util.NativeObject; | ||||
| import java.nio.ByteBuffer; | ||||
| import java.util.EnumMap; | ||||
| import java.util.EnumSet; | ||||
| 
 | ||||
| /** | ||||
| @ -66,6 +67,13 @@ public interface Renderer { | ||||
|      */ | ||||
|     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 | ||||
|      * 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)  | ||||
|      */ | ||||
|     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. | ||||
|      * <p> | ||||
|  | ||||
| @ -45,142 +45,149 @@ import java.nio.ShortBuffer; | ||||
|  */ | ||||
| public interface GL { | ||||
| 
 | ||||
| 	public static final int GL_ALPHA = 0x1906; | ||||
| 	public static final int GL_ALWAYS = 0x207; | ||||
| 	public static final int GL_ARRAY_BUFFER = 0x8892; | ||||
| 	public static final int GL_BACK = 0x405; | ||||
| 	public static final int GL_BLEND = 0xBE2; | ||||
| 	public static final int GL_BYTE = 0x1400; | ||||
| 	public static final int GL_CLAMP_TO_EDGE = 0x812F; | ||||
| 	public static final int GL_COLOR_BUFFER_BIT = 0x4000; | ||||
| 	public static final int GL_COMPILE_STATUS = 0x8B81; | ||||
| 	public static final int GL_CULL_FACE = 0xB44; | ||||
| 	public static final int GL_DECR = 0x1E03; | ||||
| 	public static final int GL_DECR_WRAP = 0x8508; | ||||
| 	public static final int GL_DEPTH_BUFFER_BIT = 0x100; | ||||
| 	public static final int GL_DEPTH_COMPONENT = 0x1902; | ||||
| 	public static final int GL_DEPTH_COMPONENT16 = 0x81A5; | ||||
| 	public static final int GL_DEPTH_TEST = 0xB71; | ||||
| 	public static final int GL_DOUBLE = 0x140A; | ||||
| 	public static final int GL_DST_COLOR = 0x306; | ||||
| 	public static final int GL_DYNAMIC_DRAW = 0x88E8; | ||||
| 	public static final int GL_ELEMENT_ARRAY_BUFFER = 0x8893; | ||||
| 	public static final int GL_EQUAL = 0x202; | ||||
| 	public static final int GL_EXTENSIONS = 0x1F03; | ||||
| 	public static final int GL_FALSE = 0x0; | ||||
| 	public static final int GL_FLOAT = 0x1406; | ||||
| 	public static final int GL_FRAGMENT_SHADER = 0x8B30; | ||||
| 	public static final int GL_FRONT = 0x404; | ||||
| 	public static final int GL_FRONT_AND_BACK = 0x408; | ||||
| 	public static final int GL_GEQUAL = 0x206; | ||||
| 	public static final int GL_GREATER = 0x204; | ||||
|         public static final int GL_GREEN = 0x1904; | ||||
| 	public static final int GL_INCR = 0x1E02; | ||||
| 	public static final int GL_INCR_WRAP = 0x8507; | ||||
| 	public static final int GL_INFO_LOG_LENGTH = 0x8B84; | ||||
| 	public static final int GL_INT = 0x1404; | ||||
|         public static final int GL_INVALID_ENUM = 0x500; | ||||
|         public static final int GL_INVALID_VALUE = 0x501; | ||||
|         public static final int GL_INVALID_OPERATION = 0x502; | ||||
| 	public static final int GL_INVERT = 0x150A; | ||||
| 	public static final int GL_KEEP = 0x1E00; | ||||
| 	public static final int GL_LEQUAL = 0x203; | ||||
| 	public static final int GL_LESS = 0x201; | ||||
| 	public static final int GL_LINEAR = 0x2601; | ||||
| 	public static final int GL_LINEAR_MIPMAP_LINEAR = 0x2703; | ||||
| 	public static final int GL_LINEAR_MIPMAP_NEAREST = 0x2701; | ||||
| 	public static final int GL_LINES = 0x1; | ||||
| 	public static final int GL_LINE_LOOP = 0x2; | ||||
| 	public static final int GL_LINE_STRIP = 0x3; | ||||
| 	public static final int GL_LINK_STATUS = 0x8B82; | ||||
| 	public static final int GL_LUMINANCE = 0x1909; | ||||
| 	public static final int GL_LUMINANCE_ALPHA = 0x190A; | ||||
| 	public static final int GL_MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C; | ||||
| 	public static final int GL_MAX_TEXTURE_IMAGE_UNITS = 0x8872; | ||||
| 	public static final int GL_MAX_TEXTURE_SIZE = 0xD33; | ||||
| 	public static final int GL_MAX_VERTEX_ATTRIBS = 0x8869; | ||||
| 	public static final int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C; | ||||
| 	public static final int GL_MAX_VERTEX_UNIFORM_COMPONENTS  = 0x8B4A; | ||||
| 	public static final int GL_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB; | ||||
| 	public static final int GL_MIRRORED_REPEAT = 0x8370; | ||||
| 	public static final int GL_NEAREST = 0x2600; | ||||
| 	public static final int GL_NEAREST_MIPMAP_LINEAR = 0x2702; | ||||
| 	public static final int GL_NEAREST_MIPMAP_NEAREST = 0x2700; | ||||
| 	public static final int GL_NEVER = 0x200; | ||||
|         public static final int GL_NO_ERROR = 0x0; | ||||
| 	public static final int GL_NONE = 0x0; | ||||
| 	public static final int GL_NOTEQUAL = 0x205; | ||||
| 	public static final int GL_ONE = 0x1; | ||||
| 	public static final int GL_ONE_MINUS_DST_COLOR = 0x307; | ||||
| 	public static final int GL_ONE_MINUS_SRC_ALPHA = 0x303; | ||||
| 	public static final int GL_ONE_MINUS_SRC_COLOR = 0x301; | ||||
|         public static final int GL_OUT_OF_MEMORY = 0x505; | ||||
| 	public static final int GL_POINTS = 0x0; | ||||
| 	public static final int GL_POLYGON_OFFSET_FILL = 0x8037; | ||||
|         public static final int GL_RED = 0x1903; | ||||
|         public static final int GL_RENDERER = 0x1F01; | ||||
| 	public static final int GL_REPEAT = 0x2901; | ||||
| 	public static final int GL_REPLACE = 0x1E01; | ||||
| 	public static final int GL_RGB = 0x1907; | ||||
|         public static final int GL_RGB565 = 0x8D62; | ||||
|         public static final int GL_RGB5_A1 = 0x8057; | ||||
| 	public static final int GL_RGBA = 0x1908; | ||||
|         public static final int GL_RGBA4 = 0x8056; | ||||
| 	public static final int GL_SCISSOR_TEST = 0xC11; | ||||
| 	public static final int GL_SHADING_LANGUAGE_VERSION = 0x8B8C; | ||||
| 	public static final int GL_SHORT = 0x1402; | ||||
| 	public static final int GL_SRC_ALPHA = 0x302; | ||||
| 	public static final int GL_SRC_COLOR = 0x300; | ||||
| 	public static final int GL_STATIC_DRAW = 0x88E4; | ||||
| 	public static final int GL_STENCIL_BUFFER_BIT = 0x400; | ||||
| 	public static final int GL_STENCIL_TEST = 0xB90; | ||||
| 	public static final int GL_STREAM_DRAW = 0x88E0; | ||||
|         public static final int GL_STREAM_READ = 0x88E1; | ||||
| 	public static final int GL_TEXTURE = 0x1702; | ||||
| 	public static final int GL_TEXTURE0 = 0x84C0; | ||||
|         public static final int GL_TEXTURE1 = 0x84C1; | ||||
|         public static final int GL_TEXTURE2 = 0x84C2; | ||||
|         public static final int GL_TEXTURE3 = 0x84C3; | ||||
|         public static final int GL_TEXTURE4 = 0x84C4; | ||||
|         public static final int GL_TEXTURE5 = 0x84C5; | ||||
|         public static final int GL_TEXTURE6 = 0x84C6; | ||||
|         public static final int GL_TEXTURE7 = 0x84C7; | ||||
|         public static final int GL_TEXTURE8 = 0x84C8; | ||||
|         public static final int GL_TEXTURE9 = 0x84C9; | ||||
|         public static final int GL_TEXTURE10 = 0x84CA; | ||||
|         public static final int GL_TEXTURE11 = 0x84CB; | ||||
|         public static final int GL_TEXTURE12 = 0x84CC; | ||||
|         public static final int GL_TEXTURE13 = 0x84CD; | ||||
|         public static final int GL_TEXTURE14 = 0x84CE; | ||||
|         public static final int GL_TEXTURE15 = 0x84CF; | ||||
| 	public static final int GL_TEXTURE_2D = 0xDE1; | ||||
| 	public static final int GL_TEXTURE_CUBE_MAP = 0x8513; | ||||
| 	public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515; | ||||
|         public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516; | ||||
|         public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517; | ||||
|         public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518; | ||||
|         public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519; | ||||
|         public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A; | ||||
|         public static final int GL_TEXTURE_BASE_LEVEL = 0x813C; | ||||
| 	public static final int GL_TEXTURE_MAG_FILTER = 0x2800; | ||||
| 	public static final int GL_TEXTURE_MAX_LEVEL = 0x813D; | ||||
| 	public static final int GL_TEXTURE_MIN_FILTER = 0x2801; | ||||
| 	public static final int GL_TEXTURE_WRAP_S = 0x2802; | ||||
| 	public static final int GL_TEXTURE_WRAP_T = 0x2803; | ||||
| 	public static final int GL_TRIANGLES = 0x4; | ||||
| 	public static final int GL_TRIANGLE_FAN = 0x6; | ||||
| 	public static final int GL_TRIANGLE_STRIP = 0x5; | ||||
| 	public static final int GL_TRUE = 0x1; | ||||
| 	public static final int GL_UNPACK_ALIGNMENT = 0xCF5; | ||||
| 	public static final int GL_UNSIGNED_BYTE = 0x1401; | ||||
| 	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 static final int GL_ALPHA = 0x1906; | ||||
|     public static final int GL_ALWAYS = 0x207; | ||||
|     public static final int GL_ARRAY_BUFFER = 0x8892; | ||||
|     public static final int GL_BACK = 0x405; | ||||
|     public static final int GL_BLEND = 0xBE2; | ||||
|     public static final int GL_BYTE = 0x1400; | ||||
|     public static final int GL_CLAMP_TO_EDGE = 0x812F; | ||||
|     public static final int GL_COLOR_BUFFER_BIT = 0x4000; | ||||
|     public static final int GL_COMPILE_STATUS = 0x8B81; | ||||
|     public static final int GL_CULL_FACE = 0xB44; | ||||
|     public static final int GL_DECR = 0x1E03; | ||||
|     public static final int GL_DECR_WRAP = 0x8508; | ||||
|     public static final int GL_DEPTH_BUFFER_BIT = 0x100; | ||||
|     public static final int GL_DEPTH_COMPONENT = 0x1902; | ||||
|     public static final int GL_DEPTH_COMPONENT16 = 0x81A5; | ||||
|     public static final int GL_DEPTH_TEST = 0xB71; | ||||
|     public static final int GL_DOUBLE = 0x140A; | ||||
|     public static final int GL_DST_COLOR = 0x306; | ||||
|     public static final int GL_DYNAMIC_DRAW = 0x88E8; | ||||
|     public static final int GL_ELEMENT_ARRAY_BUFFER = 0x8893; | ||||
|     public static final int GL_EQUAL = 0x202; | ||||
|     public static final int GL_EXTENSIONS = 0x1F03; | ||||
|     public static final int GL_FALSE = 0x0; | ||||
|     public static final int GL_FLOAT = 0x1406; | ||||
|     public static final int GL_FRAGMENT_SHADER = 0x8B30; | ||||
|     public static final int GL_FRONT = 0x404; | ||||
|     public static final int GL_FUNC_ADD = 0x8006; | ||||
|     public static final int GL_FUNC_SUBTRACT = 0x800A; | ||||
|     public static final int GL_FUNC_REVERSE_SUBTRACT = 0x800B; | ||||
|     public static final int GL_FRONT_AND_BACK = 0x408; | ||||
|     public static final int GL_GEQUAL = 0x206; | ||||
|     public static final int GL_GREATER = 0x204; | ||||
|     public static final int GL_GREEN = 0x1904; | ||||
|     public static final int GL_INCR = 0x1E02; | ||||
|     public static final int GL_INCR_WRAP = 0x8507; | ||||
|     public static final int GL_INFO_LOG_LENGTH = 0x8B84; | ||||
|     public static final int GL_INT = 0x1404; | ||||
|     public static final int GL_INVALID_ENUM = 0x500; | ||||
|     public static final int GL_INVALID_VALUE = 0x501; | ||||
|     public static final int GL_INVALID_OPERATION = 0x502; | ||||
|     public static final int GL_INVERT = 0x150A; | ||||
|     public static final int GL_KEEP = 0x1E00; | ||||
|     public static final int GL_LEQUAL = 0x203; | ||||
|     public static final int GL_LESS = 0x201; | ||||
|     public static final int GL_LINEAR = 0x2601; | ||||
|     public static final int GL_LINEAR_MIPMAP_LINEAR = 0x2703; | ||||
|     public static final int GL_LINEAR_MIPMAP_NEAREST = 0x2701; | ||||
|     public static final int GL_LINES = 0x1; | ||||
|     public static final int GL_LINE_LOOP = 0x2; | ||||
|     public static final int GL_LINE_STRIP = 0x3; | ||||
|     public static final int GL_LINK_STATUS = 0x8B82; | ||||
|     public static final int GL_LUMINANCE = 0x1909; | ||||
|     public static final int GL_LUMINANCE_ALPHA = 0x190A; | ||||
|     public static final int GL_MAX = 0x8008; | ||||
|     public static final int GL_MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C; | ||||
|     public static final int GL_MAX_FRAGMENT_UNIFORM_COMPONENTS = 0x8B49; | ||||
|     public static final int GL_MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD; | ||||
|     public static final int GL_MAX_TEXTURE_IMAGE_UNITS = 0x8872; | ||||
|     public static final int GL_MAX_TEXTURE_SIZE = 0xD33; | ||||
|     public static final int GL_MAX_VERTEX_ATTRIBS = 0x8869; | ||||
|     public static final int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C; | ||||
|     public static final int GL_MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A; | ||||
|     public static final int GL_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB; | ||||
|     public static final int GL_MIRRORED_REPEAT = 0x8370; | ||||
|     public static final int GL_MIN = 0x8007; | ||||
|     public static final int GL_NEAREST = 0x2600; | ||||
|     public static final int GL_NEAREST_MIPMAP_LINEAR = 0x2702; | ||||
|     public static final int GL_NEAREST_MIPMAP_NEAREST = 0x2700; | ||||
|     public static final int GL_NEVER = 0x200; | ||||
|     public static final int GL_NO_ERROR = 0x0; | ||||
|     public static final int GL_NONE = 0x0; | ||||
|     public static final int GL_NOTEQUAL = 0x205; | ||||
|     public static final int GL_ONE = 0x1; | ||||
|     public static final int GL_ONE_MINUS_DST_COLOR = 0x307; | ||||
|     public static final int GL_ONE_MINUS_SRC_ALPHA = 0x303; | ||||
|     public static final int GL_ONE_MINUS_SRC_COLOR = 0x301; | ||||
|     public static final int GL_OUT_OF_MEMORY = 0x505; | ||||
|     public static final int GL_POINTS = 0x0; | ||||
|     public static final int GL_POLYGON_OFFSET_FILL = 0x8037; | ||||
|     public static final int GL_RED = 0x1903; | ||||
|     public static final int GL_RENDERER = 0x1F01; | ||||
|     public static final int GL_REPEAT = 0x2901; | ||||
|     public static final int GL_REPLACE = 0x1E01; | ||||
|     public static final int GL_RGB = 0x1907; | ||||
|     public static final int GL_RGB565 = 0x8D62; | ||||
|     public static final int GL_RGB5_A1 = 0x8057; | ||||
|     public static final int GL_RGBA = 0x1908; | ||||
|     public static final int GL_RGBA4 = 0x8056; | ||||
|     public static final int GL_SCISSOR_TEST = 0xC11; | ||||
|     public static final int GL_SHADING_LANGUAGE_VERSION = 0x8B8C; | ||||
|     public static final int GL_SHORT = 0x1402; | ||||
|     public static final int GL_SRC_ALPHA = 0x302; | ||||
|     public static final int GL_SRC_COLOR = 0x300; | ||||
|     public static final int GL_STATIC_DRAW = 0x88E4; | ||||
|     public static final int GL_STENCIL_BUFFER_BIT = 0x400; | ||||
|     public static final int GL_STENCIL_TEST = 0xB90; | ||||
|     public static final int GL_STREAM_DRAW = 0x88E0; | ||||
|     public static final int GL_STREAM_READ = 0x88E1; | ||||
|     public static final int GL_TEXTURE = 0x1702; | ||||
|     public static final int GL_TEXTURE0 = 0x84C0; | ||||
|     public static final int GL_TEXTURE1 = 0x84C1; | ||||
|     public static final int GL_TEXTURE2 = 0x84C2; | ||||
|     public static final int GL_TEXTURE3 = 0x84C3; | ||||
|     public static final int GL_TEXTURE4 = 0x84C4; | ||||
|     public static final int GL_TEXTURE5 = 0x84C5; | ||||
|     public static final int GL_TEXTURE6 = 0x84C6; | ||||
|     public static final int GL_TEXTURE7 = 0x84C7; | ||||
|     public static final int GL_TEXTURE8 = 0x84C8; | ||||
|     public static final int GL_TEXTURE9 = 0x84C9; | ||||
|     public static final int GL_TEXTURE10 = 0x84CA; | ||||
|     public static final int GL_TEXTURE11 = 0x84CB; | ||||
|     public static final int GL_TEXTURE12 = 0x84CC; | ||||
|     public static final int GL_TEXTURE13 = 0x84CD; | ||||
|     public static final int GL_TEXTURE14 = 0x84CE; | ||||
|     public static final int GL_TEXTURE15 = 0x84CF; | ||||
|     public static final int GL_TEXTURE_2D = 0xDE1; | ||||
|     public static final int GL_TEXTURE_CUBE_MAP = 0x8513; | ||||
|     public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515; | ||||
|     public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516; | ||||
|     public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517; | ||||
|     public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518; | ||||
|     public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519; | ||||
|     public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A; | ||||
|     public static final int GL_TEXTURE_BASE_LEVEL = 0x813C; | ||||
|     public static final int GL_TEXTURE_MAG_FILTER = 0x2800; | ||||
|     public static final int GL_TEXTURE_MAX_LEVEL = 0x813D; | ||||
|     public static final int GL_TEXTURE_MIN_FILTER = 0x2801; | ||||
|     public static final int GL_TEXTURE_WRAP_S = 0x2802; | ||||
|     public static final int GL_TEXTURE_WRAP_T = 0x2803; | ||||
|     public static final int GL_TRIANGLES = 0x4; | ||||
|     public static final int GL_TRIANGLE_FAN = 0x6; | ||||
|     public static final int GL_TRIANGLE_STRIP = 0x5; | ||||
|     public static final int GL_TRUE = 0x1; | ||||
|     public static final int GL_UNPACK_ALIGNMENT = 0xCF5; | ||||
|     public static final int GL_UNSIGNED_BYTE = 0x1401; | ||||
|     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(); | ||||
|          | ||||
| @ -188,6 +195,7 @@ public interface GL { | ||||
| 	public void glAttachShader(int program, int shader); | ||||
| 	public void glBindBuffer(int target, int buffer); | ||||
| 	public void glBindTexture(int target, int texture); | ||||
| 	public void glBlendEquationSeparate(int colorMode, int alphaMode); | ||||
| 	public void glBlendFunc(int sfactor, int dfactor); | ||||
|         public void glBufferData(int target, long data_size, 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); | ||||
|         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(); | ||||
|         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 int vpX, vpY, vpW, vpH; | ||||
|     private int clipX, clipY, clipW, clipH; | ||||
|     private int defaultAnisotropicFilter = 1; | ||||
|     private boolean linearizeSrgbImages; | ||||
|     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)); | ||||
| 
 | ||||
| //        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)) { | ||||
|             limits.put(Limits.FragmentUniformVectors, getInteger(GL.GL_MAX_FRAGMENT_UNIFORM_VECTORS)); | ||||
|             limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_VECTORS)); | ||||
|         } 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.VertexAttributes, getInteger(GL.GL_MAX_VERTEX_ATTRIBS)); | ||||
|         limits.put(Limits.TextureSize, getInteger(GL.GL_MAX_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("\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()); | ||||
|         } | ||||
| 
 | ||||
| @ -522,7 +530,6 @@ public final class GLRenderer implements Renderer { | ||||
|             gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE); | ||||
|             if (!caps.contains(Caps.CoreProfile)) { | ||||
|                 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) { | ||||
|         if (caps.contains(Caps.Multisample)) { | ||||
|             if (value) { | ||||
| @ -735,6 +750,19 @@ public final class GLRenderer implements Renderer { | ||||
|                         throw new UnsupportedOperationException("Unrecognized blend mode: " | ||||
|                                 + 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(); | ||||
| @ -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) { | ||||
|         switch (stencilOp) { | ||||
|             case Keep: | ||||
| @ -964,12 +1027,12 @@ public final class GLRenderer implements Renderer { | ||||
|                 gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE); | ||||
|                 break; | ||||
|             case Matrix3: | ||||
|                 fb = (FloatBuffer) uniform.getValue(); | ||||
|                 fb = uniform.getMultiData(); | ||||
|                 assert fb.remaining() == 9; | ||||
|                 gl.glUniformMatrix3(loc, false, fb); | ||||
|                 break; | ||||
|             case Matrix4: | ||||
|                 fb = (FloatBuffer) uniform.getValue(); | ||||
|                 fb = uniform.getMultiData(); | ||||
|                 assert fb.remaining() == 16; | ||||
|                 gl.glUniformMatrix4(loc, false, fb); | ||||
|                 break; | ||||
| @ -978,23 +1041,23 @@ public final class GLRenderer implements Renderer { | ||||
|                 gl.glUniform1(loc, ib); | ||||
|                 break; | ||||
|             case FloatArray: | ||||
|                 fb = (FloatBuffer) uniform.getValue(); | ||||
|                 fb = uniform.getMultiData(); | ||||
|                 gl.glUniform1(loc, fb); | ||||
|                 break; | ||||
|             case Vector2Array: | ||||
|                 fb = (FloatBuffer) uniform.getValue(); | ||||
|                 fb = uniform.getMultiData(); | ||||
|                 gl.glUniform2(loc, fb); | ||||
|                 break; | ||||
|             case Vector3Array: | ||||
|                 fb = (FloatBuffer) uniform.getValue(); | ||||
|                 fb = uniform.getMultiData(); | ||||
|                 gl.glUniform3(loc, fb); | ||||
|                 break; | ||||
|             case Vector4Array: | ||||
|                 fb = (FloatBuffer) uniform.getValue(); | ||||
|                 fb = uniform.getMultiData(); | ||||
|                 gl.glUniform4(loc, fb); | ||||
|                 break; | ||||
|             case Matrix4Array: | ||||
|                 fb = (FloatBuffer) uniform.getValue(); | ||||
|                 fb = uniform.getMultiData(); | ||||
|                 gl.glUniformMatrix4(loc, false, fb); | ||||
|                 break; | ||||
|             case Int: | ||||
| @ -1872,13 +1935,18 @@ public final class GLRenderer implements Renderer { | ||||
|             gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, convertMinFilter(tex.getMinFilter(), haveMips)); | ||||
|             curState.minFilter = tex.getMinFilter(); | ||||
|         } | ||||
| 
 | ||||
|         int desiredAnisoFilter = tex.getAnisotropicFilter() == 0 | ||||
|                 ? defaultAnisotropicFilter | ||||
|                 : tex.getAnisotropicFilter(); | ||||
| 
 | ||||
|         if (caps.contains(Caps.TextureFilterAnisotropic) | ||||
|                 && curState.anisoFilter != tex.getAnisotropicFilter()) { | ||||
|                 && curState.anisoFilter != desiredAnisoFilter) { | ||||
|             bindTextureAndUnit(target, image, unit); | ||||
|             gl.glTexParameterf(target, | ||||
|                     GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT, | ||||
|                     tex.getAnisotropicFilter()); | ||||
|             curState.anisoFilter = tex.getAnisotropicFilter(); | ||||
|                     desiredAnisoFilter); | ||||
|             curState.anisoFilter = desiredAnisoFilter; | ||||
|         } | ||||
| 
 | ||||
|         switch (tex.getType()) { | ||||
| @ -2689,12 +2757,15 @@ public final class GLRenderer implements Renderer { | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|         } | ||||
| 
 | ||||
|         //this is kept for backward compatibility. | ||||
|         if (mesh.getLineWidth() != -1 && context.lineWidth != mesh.getLineWidth()) { | ||||
|         if (count > 1 && !caps.contains(Caps.MeshInstancing)) { | ||||
|             throw new RendererException("Mesh instancing is not supported by the video hardware"); | ||||
|         } | ||||
| 
 | ||||
|         if (mesh.getLineWidth() != 1f && context.lineWidth != mesh.getLineWidth()) { | ||||
|             gl.glLineWidth(mesh.getLineWidth()); | ||||
|             context.lineWidth = mesh.getLineWidth(); | ||||
|         } | ||||
|  | ||||
| @ -99,6 +99,16 @@ public class GeometryList implements Iterable<Geometry>{ | ||||
|         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. | ||||
|      * | ||||
|  | ||||
| @ -69,11 +69,12 @@ public class OpaqueComparator implements GeometryComparator { | ||||
|         return spat.queueDistance; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int compare(Geometry o1, Geometry o2) { | ||||
|         Material m1 = o1.getMaterial(); | ||||
|         Material m2 = o2.getMaterial(); | ||||
| 
 | ||||
|         int compareResult = m2.getSortId() - m1.getSortId(); | ||||
|          | ||||
|         int compareResult = Integer.compare(m1.getSortId(), m2.getSortId()); | ||||
|         if (compareResult == 0){ | ||||
|             // use the same shader. | ||||
|             // sort front-to-back then. | ||||
|  | ||||
| @ -175,7 +175,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable { | ||||
|     private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>(); | ||||
|     private VertexBuffer[] lodLevels; | ||||
|     private float pointSize = 1; | ||||
|     private float lineWidth = -1; | ||||
|     private float lineWidth = 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) | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public float getPointSize() { | ||||
|         return pointSize; | ||||
|         return 1.0f; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set the size of points for meshes of mode {@link Mode#Points}. | ||||
|      * The point size is specified as on-screen pixels, the default | ||||
|      * 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>. | ||||
|      * @deprecated Does nothing, since the size of {@link Mode#Points points} is | ||||
|      * determined via the vertex shader's <code>gl_PointSize</code> output. | ||||
|      * | ||||
|      * @param pointSize The size of points | ||||
|      * @param pointSize ignored | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public void setPointSize(float pointSize) { | ||||
|         this.pointSize = pointSize; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -634,6 +632,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable { | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public void setLineWidth(float lineWidth) { | ||||
|         if (lineWidth < 1f) { | ||||
|             throw new IllegalArgumentException("lineWidth must be greater than or equal to 1.0"); | ||||
|         } | ||||
|         this.lineWidth = lineWidth; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -75,7 +75,6 @@ public class Node extends Spatial { | ||||
|      * requiresUpdate() method. | ||||
|      */ | ||||
|     private SafeArrayList<Spatial> updateList = null; | ||||
| 
 | ||||
|     /** | ||||
|      * False if the update list requires rebuilding.  This is Node.class | ||||
|      * 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) { | ||||
|         super(name); | ||||
| 
 | ||||
|         // For backwards compatibility, only clear the "requires | ||||
|         // update" flag if we are not a subclass of Node. | ||||
|         // 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 | ||||
|     protected void updateWorldBound(){ | ||||
|         super.updateWorldBound(); | ||||
| 
 | ||||
|         // for a node, the world bound is a combination of all it's children | ||||
|         // bounds | ||||
|         BoundingVolume resultBound = null; | ||||
| @ -239,19 +248,19 @@ public class Node extends Spatial { | ||||
|             // This branch has no geometric state that requires updates. | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if ((refreshFlags & RF_LIGHTLIST) != 0){ | ||||
|             updateWorldLightList(); | ||||
|         } | ||||
| 
 | ||||
|         if ((refreshFlags & RF_TRANSFORM) != 0){ | ||||
|             // combine with parent transforms- same for all spatial | ||||
|             // subclasses. | ||||
|             updateWorldTransforms(); | ||||
|         } | ||||
|         if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) { | ||||
|             updateMatParamOverrides(); | ||||
|         } | ||||
| 
 | ||||
|         refreshFlags &= ~RF_CHILD_LIGHTLIST; | ||||
| 
 | ||||
|         if (!children.isEmpty()) { | ||||
|             // the important part- make sure child geometric state is refreshed | ||||
|             // first before updating own world bound. This saves | ||||
| @ -287,7 +296,6 @@ public class Node extends Spatial { | ||||
| 
 | ||||
|         return count; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * <code>getVertexCount</code> returns the number of vertices contained | ||||
|      * in all sub-branches of this node that contain geometry. | ||||
| @ -321,7 +329,6 @@ public class Node extends Spatial { | ||||
|     public int attachChild(Spatial child) { | ||||
|         return attachChildAt(child, children.size()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * <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); | ||||
|             children.add(index, child); | ||||
| 
 | ||||
|             // XXX: Not entirely correct? Forces bound update up the | ||||
|             // tree stemming from the attached child. Also forces | ||||
|             // transform update down the tree- | ||||
|             child.setTransformRefresh(); | ||||
|             child.setLightListRefresh(); | ||||
|             child.setMatParamOverrideRefresh(); | ||||
|             if (logger.isLoggable(Level.FINE)) { | ||||
|                 logger.log(Level.FINE,"Child ({0}) attached to this node ({1})", | ||||
|                         new Object[]{child.getName(), getName()}); | ||||
|             } | ||||
| 
 | ||||
|             invalidateUpdateList(); | ||||
|         } | ||||
| 
 | ||||
|         return children.size(); | ||||
|     } | ||||
| 
 | ||||
| @ -433,7 +438,8 @@ public class Node extends Spatial { | ||||
|             child.setTransformRefresh(); | ||||
|             // lights are also inherited from parent | ||||
|             child.setLightListRefresh(); | ||||
| 
 | ||||
|             child.setMatParamOverrideRefresh(); | ||||
|              | ||||
|             invalidateUpdateList(); | ||||
|         } | ||||
|         return child; | ||||
| @ -519,7 +525,6 @@ public class Node extends Spatial { | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * determines if the provided Spatial is contained in the children list of | ||||
|      * this node. | ||||
| @ -567,39 +572,32 @@ public class Node extends Spatial { | ||||
| 
 | ||||
|     public int collideWith(Collidable other, CollisionResults results){ | ||||
|         int total = 0; | ||||
| 
 | ||||
|         // 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. | ||||
|         // 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 | ||||
|         think it needs to be implemented a better way anyway. | ||||
| 
 | ||||
|         First, it causes issues for anyone doing collideWith() with BoundingVolumes | ||||
|         and expecting it to trickle down to the children.  For example, children | ||||
|         with BoundingSphere bounding volumes and collideWith(BoundingSphere).  Doing | ||||
|         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 | ||||
|         case is tricky and the first sign that this is the wrong approach.) | ||||
| 
 | ||||
|         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 | ||||
|         idea of calculating a full collision just to see if the two shapes collide at all | ||||
|         is very wasteful. | ||||
| 
 | ||||
|         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% | ||||
|         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. | ||||
| 
 | ||||
|         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 | ||||
|         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 | ||||
|         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. | ||||
| 
 | ||||
|         if (children.size() > 4) | ||||
|         { | ||||
|           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. | ||||
|         nodeClone.updateList = null; | ||||
|         nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone() | ||||
| 
 | ||||
|         return nodeClone; | ||||
|     } | ||||
| 
 | ||||
| @ -732,7 +729,6 @@ public class Node extends Spatial { | ||||
|         // cloning this list is fine. | ||||
|         this.updateList = cloner.clone(updateList); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void write(JmeExporter e) throws IOException { | ||||
|         super.write(e); | ||||
| @ -744,7 +740,6 @@ public class Node extends Spatial { | ||||
|         // XXX: Load children before loading itself!! | ||||
|         // This prevents empty children list if controls query | ||||
|         // it in Control.setSpatial(). | ||||
| 
 | ||||
|         children = new SafeArrayList( Spatial.class, | ||||
|                                       e.getCapsule(this).readSavableArrayList("children", null) ); | ||||
| 
 | ||||
| @ -754,7 +749,6 @@ public class Node extends Spatial { | ||||
|                 child.parent = this; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         super.read(e); | ||||
|     } | ||||
| 
 | ||||
| @ -775,7 +769,6 @@ public class Node extends Spatial { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void depthFirstTraversal(SceneGraphVisitor visitor) { | ||||
|         for (Spatial child : children.getArray()) { | ||||
| @ -783,7 +776,6 @@ public class Node extends Spatial { | ||||
|         } | ||||
|         visitor.visit(this); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) { | ||||
|         queue.addAll(children); | ||||
|  | ||||
| @ -38,6 +38,7 @@ import com.jme3.collision.Collidable; | ||||
| import com.jme3.export.*; | ||||
| import com.jme3.light.Light; | ||||
| import com.jme3.light.LightList; | ||||
| import com.jme3.material.MatParamOverride; | ||||
| import com.jme3.material.Material; | ||||
| import com.jme3.math.*; | ||||
| 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 | ||||
|                                RF_BOUND = 0x02, | ||||
|                                RF_LIGHTLIST = 0x04, // changes in light lists | ||||
|                                RF_CHILD_LIGHTLIST = 0x08; // some child need geometry update | ||||
| 
 | ||||
|                                RF_LIGHTLIST = 0x04, // changes in light lists  | ||||
|                                RF_CHILD_LIGHTLIST = 0x08, // some child need geometry update | ||||
|                                RF_MATPARAM_OVERRIDE = 0x10; | ||||
|      | ||||
|     protected CullHint cullHint = CullHint.Inherit; | ||||
|     protected BatchHint batchHint = BatchHint.Inherit; | ||||
|     /** | ||||
| @ -135,7 +137,11 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|      */ | ||||
|     protected LightList localLights; | ||||
|     protected transient LightList worldLights; | ||||
|     /** | ||||
| 
 | ||||
|     protected ArrayList<MatParamOverride> localOverrides; | ||||
|     protected ArrayList<MatParamOverride> worldOverrides; | ||||
| 
 | ||||
|     /**  | ||||
|      * This spatial's name. | ||||
|      */ | ||||
|     protected String name; | ||||
| @ -195,13 +201,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|      */ | ||||
|     protected Spatial(String name) { | ||||
|         this.name = name; | ||||
| 
 | ||||
|         localTransform = new Transform(); | ||||
|         worldTransform = new Transform(); | ||||
| 
 | ||||
|         localLights = new LightList(this); | ||||
|         worldLights = new LightList(this); | ||||
| 
 | ||||
|         localOverrides = new ArrayList<>(); | ||||
|         worldOverrides = new ArrayList<>(); | ||||
|         refreshFlags |= RF_BOUND; | ||||
|     } | ||||
| 
 | ||||
| @ -222,7 +229,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|     boolean requiresUpdates() { | ||||
|         return requiresUpdates | !controls.isEmpty(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Subclasses can call this with true to denote that they require | ||||
|      * 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() { | ||||
|         refreshFlags |= RF_LIGHTLIST; | ||||
| 
 | ||||
|         // Make sure next updateGeometricState() visits this branch | ||||
|         // to update lights. | ||||
|         Spatial p = parent; | ||||
|         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) { | ||||
|                 // The parent already has this flag, | ||||
|                 // so must all ancestors. | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             p.refreshFlags |= RF_CHILD_LIGHTLIST; | ||||
|             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 | ||||
|      * a refresh is required. | ||||
| @ -318,7 +322,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|             p = p.parent; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * (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; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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 | ||||
|      * Spatial. | ||||
| @ -525,10 +551,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|         TempVars vars = TempVars.get(); | ||||
| 
 | ||||
|         Vector3f compVecA = vars.vect4; | ||||
| 
 | ||||
|         compVecA.set(position).subtractLocal(worldTranslation); | ||||
|         getLocalRotation().lookAt(compVecA, upVector); | ||||
| 
 | ||||
|         if ( getParent() != null ) { | ||||
|             Quaternion rot=vars.quat1; | ||||
|             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); | ||||
|             refreshFlags &= ~RF_LIGHTLIST; | ||||
|         } else { | ||||
|             if ((parent.refreshFlags & RF_LIGHTLIST) == 0) { | ||||
|                 worldLights.update(localLights, parent.worldLights); | ||||
|                 refreshFlags &= ~RF_LIGHTLIST; | ||||
|             } else { | ||||
|                 assert false; | ||||
|             } | ||||
|             assert (parent.refreshFlags & RF_LIGHTLIST) == 0; | ||||
|             worldLights.update(localLights, parent.worldLights); | ||||
|             refreshFlags &= ~RF_LIGHTLIST; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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(). | ||||
|      * In most cases should not be subclassed. | ||||
| @ -696,7 +768,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|         controls.add(control); | ||||
|         control.setSpatial(this); | ||||
|         boolean after = requiresUpdates(); | ||||
| 
 | ||||
|         // If the requirement to be updated has changed | ||||
|         // then we need to let the parent node know so it | ||||
|         // can rebuild its update list. | ||||
| @ -720,7 +791,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|             } | ||||
|         } | ||||
|         boolean after = requiresUpdates(); | ||||
| 
 | ||||
|         // If the requirement to be updated has changed | ||||
|         // then we need to let the parent node know so it | ||||
|         // can rebuild its update list. | ||||
| @ -746,14 +816,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|         } | ||||
| 
 | ||||
|         boolean after = requiresUpdates(); | ||||
| 
 | ||||
|         // If the requirement to be updated has changed | ||||
|         // then we need to let the parent node know so it | ||||
|         // can rebuild its update list. | ||||
|         if( parent != null && before != after ) { | ||||
|             parent.invalidateUpdateList(); | ||||
|         } | ||||
| 
 | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
| @ -838,7 +906,10 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|         if ((refreshFlags & RF_BOUND) != 0) { | ||||
|             updateWorldBound(); | ||||
|         } | ||||
| 
 | ||||
|         if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) { | ||||
|             updateMatParamOverrides(); | ||||
|         } | ||||
|          | ||||
|         assert refreshFlags == 0; | ||||
|     } | ||||
| 
 | ||||
| @ -1292,6 +1363,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|         // the transforms and stuff get refreshed. | ||||
|         clone.setTransformRefresh(); | ||||
|         clone.setLightListRefresh(); | ||||
|         clone.setMatParamOverrideRefresh(); | ||||
| 
 | ||||
|         return clone; | ||||
|     } | ||||
| @ -1312,6 +1384,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|             clone.localLights.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. | ||||
|             // This node already has the refresh flags | ||||
|             // 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.setTransformRefresh(); | ||||
|             clone.setLightListRefresh(); | ||||
|             clone.setMatParamOverrideRefresh(); | ||||
| 
 | ||||
|             clone.controls = new SafeArrayList<Control>(Control.class); | ||||
|             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. | ||||
|         clone.setTransformRefresh(); | ||||
|         clone.setLightListRefresh(); | ||||
|         clone.setMatParamOverrideRefresh(); | ||||
| 
 | ||||
|         return clone; | ||||
|     } | ||||
| @ -1419,6 +1500,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab | ||||
|         this.localLights = cloner.clone(localLights); | ||||
|         this.worldTransform = cloner.clone(worldTransform); | ||||
|         this.localTransform = cloner.clone(localTransform); | ||||
|         this.worldOverrides = cloner.clone(worldOverrides); | ||||
|         this.localOverrides = cloner.clone(localOverrides); | ||||
|         this.controls = cloner.clone(controls); | ||||
| 
 | ||||
|         // 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(localTransform, "transform", Transform.IDENTITY); | ||||
|         capsule.write(localLights, "lights", null); | ||||
|         capsule.writeSavableArrayList(localOverrides, "overrides", null); | ||||
| 
 | ||||
|         // Shallow clone the controls array to convert its type. | ||||
|         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.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 | ||||
|         //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. | ||||
|  | ||||
| @ -108,8 +108,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { | ||||
|          * Do not use. | ||||
|          */ | ||||
|         @Deprecated | ||||
|         MiscAttrib, | ||||
| 
 | ||||
|         Reserved0, | ||||
|         /** | ||||
|          * Specifies the index buffer, must contain integer data | ||||
|          * (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."); | ||||
| 
 | ||||
|         this.usage = usage; | ||||
|         this.setUpdateNeeded(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -101,14 +101,6 @@ public class WireBox extends Mesh { | ||||
|         ); | ||||
|         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. | ||||
|  | ||||
| @ -1,286 +1,179 @@ | ||||
| /* | ||||
|  * 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.export.*; | ||||
| import com.jme3.material.MatParam; | ||||
| import com.jme3.material.TechniqueDef; | ||||
| import com.jme3.util.ListMap; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.Map; | ||||
| import java.util.TreeMap; | ||||
| 
 | ||||
| public final class DefineList implements Savable, Cloneable { | ||||
| 
 | ||||
|     private static final String ONE = "1"; | ||||
|      | ||||
|     private TreeMap<String, String> defines = new TreeMap<String, String>(); | ||||
|     private String compiled = null; | ||||
|     private int cachedHashCode = 0; | ||||
| 
 | ||||
|     public void write(JmeExporter ex) throws IOException{ | ||||
|         OutputCapsule oc = ex.getCapsule(this); | ||||
| 
 | ||||
|         String[] keys = new String[defines.size()]; | ||||
|         String[] vals = new String[defines.size()]; | ||||
| 
 | ||||
|         int i = 0; | ||||
|         for (Map.Entry<String, String> define : defines.entrySet()){ | ||||
|             keys[i] = define.getKey(); | ||||
|             vals[i] = define.getValue(); | ||||
|             i++; | ||||
|         } | ||||
| 
 | ||||
|         oc.write(keys, "keys", null); | ||||
|         oc.write(vals, "vals", null); | ||||
|     } | ||||
| 
 | ||||
|     public void read(JmeImporter im) throws IOException{ | ||||
|         InputCapsule ic = im.getCapsule(this); | ||||
| 
 | ||||
|         String[] keys = ic.readStringArray("keys", null); | ||||
|         String[] vals = ic.readStringArray("vals", null); | ||||
|         for (int i = 0; i < keys.length; i++){ | ||||
|             defines.put(keys[i], vals[i]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void clear() { | ||||
|         defines.clear(); | ||||
|         compiled = ""; | ||||
|         cachedHashCode = 0; | ||||
|     } | ||||
| 
 | ||||
|     public String get(String key){ | ||||
|         return defines.get(key); | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public DefineList clone() { | ||||
|         try { | ||||
|             DefineList clone = (DefineList) super.clone(); | ||||
|             clone.cachedHashCode = 0; | ||||
|             clone.compiled = null; | ||||
|             clone.defines = (TreeMap<String, String>) defines.clone(); | ||||
|             return clone; | ||||
|         } catch (CloneNotSupportedException ex) { | ||||
|             throw new AssertionError(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public boolean set(String key, VarType type, Object val){     | ||||
|         if (val == null){ | ||||
|             defines.remove(key); | ||||
|             compiled = null; | ||||
|             cachedHashCode = 0; | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         switch (type){ | ||||
|             case Boolean: | ||||
|                 if (((Boolean) val).booleanValue()) { | ||||
|                     // same literal, != will work | ||||
|                     if (defines.put(key, ONE) != ONE) { | ||||
|                         compiled = null; | ||||
|                         cachedHashCode = 0; | ||||
|                         return true; | ||||
|                     } | ||||
|                 } else if (defines.containsKey(key)) { | ||||
|                     defines.remove(key); | ||||
|                     compiled = null; | ||||
|                     cachedHashCode = 0; | ||||
|                     return true; | ||||
|                 } | ||||
|                  | ||||
|                 break; | ||||
|             case Float: | ||||
|             case Int: | ||||
|                 String newValue = val.toString(); | ||||
|                 String original = defines.put(key, newValue); | ||||
|                 if (!val.equals(original)) { | ||||
|                     compiled = null; | ||||
|                     cachedHashCode = 0; | ||||
|                     return true;             | ||||
|                 } | ||||
|                 break; | ||||
|             default: | ||||
|                 // same literal, != will work | ||||
|                 if (defines.put(key, ONE) != ONE) {   | ||||
|                     compiled = null; | ||||
|                     cachedHashCode = 0; | ||||
|                     return true;             | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|          | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     public boolean remove(String key){    | ||||
|         if (defines.remove(key) != null) { | ||||
|             compiled = null; | ||||
|             cachedHashCode = 0; | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     public void addFrom(DefineList other){     | ||||
|         if (other == null) { | ||||
|             return; | ||||
|         } | ||||
|         compiled = null; | ||||
|         cachedHashCode = 0; | ||||
|         defines.putAll(other.defines); | ||||
|     } | ||||
| 
 | ||||
|     public String getCompiled(){ | ||||
|         if (compiled == null){ | ||||
|             StringBuilder sb = new StringBuilder(); | ||||
|             for (Map.Entry<String, String> entry : defines.entrySet()){ | ||||
|                 sb.append("#define ").append(entry.getKey()).append(" "); | ||||
|                 sb.append(entry.getValue()).append('\n'); | ||||
|             } | ||||
|             compiled = sb.toString(); | ||||
|         } | ||||
|         return compiled; | ||||
|     } | ||||
| 
 | ||||
|     @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(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| /* | ||||
|  * 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 java.util.Arrays; | ||||
| import java.util.List; | ||||
| 
 | ||||
| /** | ||||
|  * The new define list. | ||||
|  *  | ||||
|  * @author Kirill Vainer | ||||
|  */ | ||||
| public final class DefineList { | ||||
| 
 | ||||
|     public static final int MAX_DEFINES = 64; | ||||
| 
 | ||||
|     private long hash; | ||||
|     private final int[] vals; | ||||
| 
 | ||||
|     public DefineList(int numValues) { | ||||
|         if (numValues < 0 || numValues > MAX_DEFINES) { | ||||
|             throw new IllegalArgumentException("numValues must be between 0 and 64"); | ||||
|         } | ||||
|         vals = new int[numValues]; | ||||
|     } | ||||
|      | ||||
|     private DefineList(DefineList original) { | ||||
|         this.hash = original.hash; | ||||
|         this.vals = new int[original.vals.length]; | ||||
|         System.arraycopy(original.vals, 0, vals, 0, vals.length); | ||||
|     } | ||||
| 
 | ||||
|     public void set(int id, int val) { | ||||
|         assert 0 <= id && id < 64; | ||||
|         if (val != 0) { | ||||
|             hash |=  (1L << id); | ||||
|         } else { | ||||
|             hash &= ~(1L << id); | ||||
|         } | ||||
|         vals[id] = val; | ||||
|     } | ||||
|      | ||||
|     public void set(int id, float val) { | ||||
|         set(id, Float.floatToIntBits(val)); | ||||
|     } | ||||
|      | ||||
|     public void set(int id, boolean val) { | ||||
|         set(id, val ? 1 : 0); | ||||
|     } | ||||
| 
 | ||||
|     public void set(int id, VarType type, Object value) { | ||||
|         if (value == null) { | ||||
|             set(id, 0); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         switch (type) { | ||||
|             case Int: | ||||
|                 set(id, (Integer) value); | ||||
|                 break; | ||||
|             case Float: | ||||
|                 set(id, (Float) value); | ||||
|                 break; | ||||
|             case Boolean: | ||||
|                 set(id, ((Boolean) value)); | ||||
|                 break; | ||||
|             default: | ||||
|                 set(id, 1); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void setAll(DefineList other) { | ||||
|         for (int i = 0; i < other.vals.length; i++) { | ||||
|             if (other.vals[i] != 0) { | ||||
|                 vals[i] = other.vals[i]; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public void clear() { | ||||
|         hash = 0; | ||||
|         Arrays.fill(vals, 0); | ||||
|     } | ||||
| 
 | ||||
|     public boolean getBoolean(int id) { | ||||
|         return vals[id] != 0; | ||||
|     } | ||||
|      | ||||
|     public float getFloat(int id) { | ||||
|         return Float.intBitsToFloat(vals[id]); | ||||
|     } | ||||
|      | ||||
|     public int getInt(int id) { | ||||
|         return vals[id]; | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public int hashCode() { | ||||
|         return (int)((hash >> 32) ^ hash); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean equals(Object other) { | ||||
|          DefineList o = (DefineList) other; | ||||
|          if (hash == o.hash) { | ||||
|              for (int i = 0; i < vals.length; i++) { | ||||
|                   if (vals[i] != o.vals[i]) return false; | ||||
|              } | ||||
|              return true; | ||||
|          } | ||||
|          return false; | ||||
|     } | ||||
| 
 | ||||
|     public DefineList deepClone() { | ||||
|          return new DefineList(this); | ||||
|     } | ||||
|      | ||||
|     public void generateSource(StringBuilder sb, List<String> defineNames, List<VarType> defineTypes) { | ||||
|         for (int i = 0; i < vals.length; i++) { | ||||
|             if (vals[i] != 0) { | ||||
|                 String defineName = defineNames.get(i); | ||||
|                  | ||||
|                 sb.append("#define "); | ||||
|                 sb.append(defineName); | ||||
|                 sb.append(" "); | ||||
|                  | ||||
|                 if (defineTypes != null && defineTypes.get(i) == VarType.Float) { | ||||
|                     float val = Float.intBitsToFloat(vals[i]); | ||||
|                     if (Float.isInfinite(val) || Float.isNaN(val)) { | ||||
|                         throw new IllegalArgumentException( | ||||
|                                 "GLSL does not support NaN " | ||||
|                                 + "or Infinite float literals"); | ||||
|                     } | ||||
|                     sb.append(val); | ||||
|                 } else { | ||||
|                     sb.append(vals[i]); | ||||
|                 } | ||||
|                  | ||||
|                 sb.append("\n"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public String generateSource(List<String> defineNames, List<VarType> defineTypes) { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         generateSource(sb, defineNames, defineTypes); | ||||
|         return sb.toString(); | ||||
|     } | ||||
| } | ||||
| @ -45,44 +45,63 @@ public final class Shader extends NativeObject { | ||||
|     /** | ||||
|      * A list of all shader sources currently attached. | ||||
|      */ | ||||
|     private ArrayList<ShaderSource> shaderSourceList; | ||||
|     private final ArrayList<ShaderSource> shaderSourceList; | ||||
| 
 | ||||
|     /** | ||||
|      * 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. | ||||
|      */ | ||||
|     private IntMap<Attribute> attribs; | ||||
|     private final IntMap<Attribute> attribs; | ||||
| 
 | ||||
|     /** | ||||
|      * Type of shader. The shader will control the pipeline of it's type. | ||||
|      */ | ||||
|     public static enum ShaderType { | ||||
| 
 | ||||
|         /** | ||||
|          * Control fragment rasterization. (e.g color of pixel). | ||||
|          */ | ||||
|         Fragment, | ||||
| 
 | ||||
|         Fragment("frag"), | ||||
|         /** | ||||
|          * 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) | ||||
|          */ | ||||
|         Geometry("geom"), | ||||
|         /** | ||||
|          * Controls tesselation factor (e.g how often a input patch should be | ||||
|          * subdivided) | ||||
|          */ | ||||
|         TessellationControl("tsctrl"), | ||||
|         /** | ||||
|          * Controls tesselation transform (e.g similar to the vertex shader, but | ||||
|          * required to mix inputs manual) | ||||
|          */ | ||||
|         TessellationEvaluation("tseval"); | ||||
| 
 | ||||
|         /** | ||||
|          * Control geometry assembly. (e.g compile a triangle list from input data) | ||||
|          */ | ||||
|         Geometry, | ||||
|         /** | ||||
|          * Controls tesselation factor (e.g how often a input patch should be subdivided) | ||||
|          */ | ||||
|         TessellationControl, | ||||
|         /** | ||||
|          * Controls tesselation transform (e.g similar to the vertex shader, but required to mix inputs manual) | ||||
|          */ | ||||
|         TessellationEvaluation; | ||||
|         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 | ||||
|      * after this constructor for the shader to be usable. | ||||
|      */ | ||||
|     public Shader(){ | ||||
|         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){ | ||||
|             shaderSourceList.add( (ShaderSource)source.createDestructableClone() ); | ||||
|         } | ||||
|          | ||||
|         uniforms = null; | ||||
|         boundUniforms = null; | ||||
|         attribs = null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -248,6 +265,18 @@ public final class Shader extends NativeObject { | ||||
|         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){ | ||||
|         assert name.startsWith("m_") || name.startsWith("g_"); | ||||
|         Uniform uniform = uniforms.get(name); | ||||
| @ -277,6 +306,10 @@ public final class Shader extends NativeObject { | ||||
|     public ListMap<String, Uniform> getUniformMap(){ | ||||
|         return uniforms; | ||||
|     } | ||||
|      | ||||
|     public ArrayList<Uniform> getBoundUniforms() { | ||||
|         return boundUniforms; | ||||
|     } | ||||
| 
 | ||||
|     public Collection<ShaderSource> getSources(){ | ||||
|         return shaderSourceList; | ||||
|  | ||||
| @ -57,9 +57,9 @@ public abstract class ShaderGenerator { | ||||
|      */ | ||||
|     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 | ||||
| @ -70,8 +70,8 @@ public abstract class ShaderGenerator { | ||||
|         this.assetManager = assetManager;         | ||||
|     } | ||||
|      | ||||
|     public void initialize(Technique technique){ | ||||
|         this.technique = technique; | ||||
|     public void initialize(TechniqueDef techniqueDef){ | ||||
|         this.techniqueDef = techniqueDef; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
| @ -79,24 +79,29 @@ public abstract class ShaderGenerator { | ||||
|      * | ||||
|      * @return a Shader program | ||||
|      */ | ||||
|     public Shader generateShader() { | ||||
|         if(technique == null){ | ||||
|             throw new UnsupportedOperationException("The shaderGenerator was not properly initialized, call initialize(Technique) before any generation"); | ||||
|     public Shader generateShader(String definesSourceCode) { | ||||
|         if (techniqueDef == null) { | ||||
|             throw new UnsupportedOperationException("The shaderGenerator was not " | ||||
|                     + "properly initialized, call " | ||||
|                     + "initialize(TechniqueDef) before any generation"); | ||||
|         } | ||||
| 
 | ||||
|         DefineList defines = technique.getAllDefines(); | ||||
|         TechniqueDef def = technique.getDef(); | ||||
|         ShaderGenerationInfo info = def.getShaderGenerationInfo(); | ||||
| 
 | ||||
|         String vertexSource = buildShader(def.getShaderNodes(), info, ShaderType.Vertex); | ||||
|         String fragmentSource = buildShader(def.getShaderNodes(), info, ShaderType.Fragment); | ||||
|         String techniqueName = techniqueDef.getName(); | ||||
|         ShaderGenerationInfo info = techniqueDef.getShaderGenerationInfo(); | ||||
| 
 | ||||
|         Shader shader = new Shader(); | ||||
|         shader.initialize(); | ||||
|         shader.addSource(Shader.ShaderType.Vertex, technique.getDef().getName() + ".vert", vertexSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Vertex)); | ||||
|         shader.addSource(Shader.ShaderType.Fragment, technique.getDef().getName() + ".frag", fragmentSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Fragment)); | ||||
|         for (ShaderType type : ShaderType.values()) { | ||||
|             String extension = type.getExtension(); | ||||
|             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; | ||||
|     } | ||||
| 
 | ||||
| @ -109,6 +114,14 @@ public abstract class ShaderGenerator { | ||||
|      * @return the code of the generated vertex shader | ||||
|      */ | ||||
|     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; | ||||
| 
 | ||||
|         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; | ||||
| 
 | ||||
|     @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 | ||||
|     public String toString(){ | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
| @ -102,6 +126,10 @@ public class Uniform extends ShaderVariable { | ||||
|     public Object getValue(){ | ||||
|         return value; | ||||
|     } | ||||
|      | ||||
|     public FloatBuffer getMultiData() { | ||||
|         return multiData; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isSetByCurrentMaterial() { | ||||
|         return setByCurrentMaterial; | ||||
| @ -111,21 +139,6 @@ public class Uniform extends ShaderVariable { | ||||
|         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(){ | ||||
|         updateNeeded = true; | ||||
| 
 | ||||
| @ -182,27 +195,43 @@ public class Uniform extends ShaderVariable { | ||||
|         } | ||||
| 
 | ||||
|         if (value == null) { | ||||
|             throw new NullPointerException(); | ||||
|             throw new IllegalArgumentException("for uniform " + name + ": value cannot be null"); | ||||
|         } | ||||
| 
 | ||||
|         setByCurrentMaterial = true; | ||||
| 
 | ||||
|         switch (type){ | ||||
|             case Matrix3: | ||||
|                 if (value.equals(this.value)) { | ||||
|                     return; | ||||
|                 } | ||||
|                 Matrix3f m3 = (Matrix3f) value; | ||||
|                 if (multiData == null) { | ||||
|                     multiData = BufferUtils.createFloatBuffer(9); | ||||
|                 } | ||||
|                 m3.fillFloatBuffer(multiData, true); | ||||
|                 multiData.clear(); | ||||
|                 if (this.value == null) { | ||||
|                     this.value = new Matrix3f(m3); | ||||
|                 } else { | ||||
|                     ((Matrix3f)this.value).set(m3); | ||||
|                 } | ||||
|                 break; | ||||
|             case Matrix4: | ||||
|                 if (value.equals(this.value)) { | ||||
|                     return; | ||||
|                 } | ||||
|                 Matrix4f m4 = (Matrix4f) value; | ||||
|                 if (multiData == null) { | ||||
|                     multiData = BufferUtils.createFloatBuffer(16); | ||||
|                 } | ||||
|                 m4.fillFloatBuffer(multiData, true); | ||||
|                 multiData.clear(); | ||||
|                 if (this.value == null) { | ||||
|                     this.value = new Matrix4f(m4); | ||||
|                 } else { | ||||
|                     ((Matrix4f)this.value).copy(m4); | ||||
|                 } | ||||
|                 break; | ||||
|             case IntArray: | ||||
|                 int[] ia = (int[]) value; | ||||
| @ -283,11 +312,32 @@ public class Uniform extends ShaderVariable { | ||||
|                 } | ||||
|                 multiData.clear(); | ||||
|                 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 | ||||
|             case Int: | ||||
|             case Float: | ||||
|             case Boolean: | ||||
|                 if (this.value != null && this.value.equals(value)) { | ||||
|                 if (value.equals(this.value)) { | ||||
|                     return; | ||||
|                 } | ||||
|                 this.value = value; | ||||
| @ -297,39 +347,38 @@ public class Uniform extends ShaderVariable { | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         if (multiData != null) { | ||||
|             this.value = multiData; | ||||
|         } | ||||
| //        if (multiData != null) { | ||||
| //            this.value = multiData; | ||||
| //        } | ||||
| 
 | ||||
|         varType = type; | ||||
|         updateNeeded = true; | ||||
|     } | ||||
| 
 | ||||
|     public void setVector4Length(int length){ | ||||
|         if (location == -1) | ||||
|         if (location == -1) { | ||||
|             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; | ||||
|         updateNeeded = true; | ||||
|         setByCurrentMaterial = true; | ||||
|     } | ||||
| 
 | ||||
|     public void setVector4InArray(float x, float y, float z, float w, int index){ | ||||
|         if (location == -1) | ||||
|         if (location == -1) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (varType != null && varType != VarType.Vector4Array) | ||||
|             throw new IllegalArgumentException("Expected a "+varType.name()+" value!"); | ||||
|         if (varType != null && varType != VarType.Vector4Array) { | ||||
|             throw new IllegalArgumentException("Expected a " + varType.name() + " value!"); | ||||
|         } | ||||
| 
 | ||||
|         FloatBuffer fb = (FloatBuffer) value; | ||||
|         fb.position(index * 4); | ||||
|         fb.put(x).put(y).put(z).put(w); | ||||
|         fb.rewind(); | ||||
|         multiData.position(index * 4); | ||||
|         multiData.put(x).put(y).put(z).put(w); | ||||
|         multiData.rewind(); | ||||
|         updateNeeded = true; | ||||
|         setByCurrentMaterial = true; | ||||
|     } | ||||
|  | ||||
| @ -36,7 +36,7 @@ import com.jme3.math.*; | ||||
| import com.jme3.renderer.Camera; | ||||
| import com.jme3.renderer.RenderManager; | ||||
| import com.jme3.system.Timer; | ||||
| import java.util.List; | ||||
| import java.util.ArrayList; | ||||
| 
 | ||||
| /** | ||||
|  * <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} | ||||
|      * 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++) { | ||||
|             Uniform u = params.get(i); | ||||
|             switch (u.getBinding()) { | ||||
|  | ||||
| @ -330,12 +330,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable | ||||
|     public void initialize(RenderManager rm, ViewPort vp) { | ||||
|         renderManager = rm; | ||||
|         viewPort = vp; | ||||
|         //checking for caps to chosse the appropriate post material technique | ||||
|         if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) { | ||||
|             postTechniqueName = "PostShadow15"; | ||||
|         } else { | ||||
|             postTechniqueName = "PostShadow"; | ||||
|         } | ||||
|         postTechniqueName = "PostShadow"; | ||||
|         if(zFarOverride>0 && frustumCam == null){ | ||||
|             initFrustumCam(); | ||||
|         } | ||||
| @ -587,7 +582,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable | ||||
|         for (int i = 0; i < l.size(); i++) { | ||||
|             Material mat = l.get(i).getMaterial(); | ||||
|             //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)) { | ||||
|                     matCache.add(mat); | ||||
|                 } | ||||
|  | ||||
| @ -355,12 +355,7 @@ public class PssmShadowRenderer implements SceneProcessor { | ||||
|     public void initialize(RenderManager rm, ViewPort vp) { | ||||
|         renderManager = rm; | ||||
|         viewPort = vp; | ||||
|         //checking for caps to chosse the appropriate post material technique | ||||
|         if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) { | ||||
|             postTechniqueName = "PostShadow15"; | ||||
|         } else { | ||||
|             postTechniqueName = "PostShadow"; | ||||
|         } | ||||
|         postTechniqueName = "PostShadow"; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isInitialized() { | ||||
| @ -533,7 +528,7 @@ public class PssmShadowRenderer implements SceneProcessor { | ||||
|         for (int i = 0; i < l.size(); i++) { | ||||
|             Material mat = l.get(i).getMaterial(); | ||||
|             //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)) { | ||||
|                     matCache.add(mat); | ||||
|                 } | ||||
|  | ||||
| @ -128,12 +128,13 @@ public class NullContext implements JmeContext, Runnable { | ||||
|     public void run(){ | ||||
|         initInThread(); | ||||
| 
 | ||||
|         while (!needClose.get()){ | ||||
|         do { | ||||
|             listener.update(); | ||||
| 
 | ||||
|             if (frameRate > 0) | ||||
|             if (frameRate > 0) { | ||||
|                 sync(frameRate); | ||||
|         } | ||||
|             } | ||||
|         } while (!needClose.get()); | ||||
| 
 | ||||
|         deinitInThread(); | ||||
| 
 | ||||
|  | ||||
| @ -39,6 +39,7 @@ import com.jme3.material.RenderState; | ||||
| import com.jme3.math.ColorRGBA; | ||||
| import com.jme3.math.Matrix4f; | ||||
| import com.jme3.renderer.Caps; | ||||
| import com.jme3.renderer.Limits; | ||||
| import com.jme3.renderer.Renderer; | ||||
| import com.jme3.renderer.Statistics; | ||||
| import com.jme3.scene.Mesh; | ||||
| @ -48,15 +49,25 @@ import com.jme3.shader.Shader.ShaderSource; | ||||
| import com.jme3.texture.FrameBuffer; | ||||
| import com.jme3.texture.Image; | ||||
| import com.jme3.texture.Texture; | ||||
| import java.util.EnumMap; | ||||
| 
 | ||||
| public class NullRenderer implements Renderer { | ||||
| 
 | ||||
|     private static final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class); | ||||
|     private static final Statistics stats = new Statistics(); | ||||
|     private final EnumSet<Caps> caps = EnumSet.allOf(Caps.class); | ||||
|     private final EnumMap<Limits, Integer> limits = new EnumMap<>(Limits.class); | ||||
|     private final Statistics stats = new Statistics(); | ||||
| 
 | ||||
|     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() { | ||||
|         return caps; | ||||
|     } | ||||
| @ -164,4 +175,7 @@ public class NullRenderer implements Renderer { | ||||
|     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; | ||||
|         magFilter = null; | ||||
|         minFilter = null; | ||||
|         anisoFilter = 0; | ||||
|         anisoFilter = 1; | ||||
|          | ||||
|         // The default in OpenGL is OFF, so we avoid setting this per texture | ||||
|         // if its not used. | ||||
|  | ||||
| @ -207,10 +207,10 @@ public class Cloner { | ||||
| 
 | ||||
|         // Check the index to see if we already have it | ||||
|         Object clone = index.get(object); | ||||
|         if( clone != null ) { | ||||
|         if( clone != null || index.containsKey(object) ) { | ||||
|             if( log.isLoggable(Level.FINER) ) { | ||||
|                 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); | ||||
|         } | ||||
|  | ||||
| @ -114,10 +114,10 @@ MaterialDef Phong Lighting { | ||||
|         //For instancing | ||||
|         Boolean UseInstancing | ||||
| 
 | ||||
|         Boolean BackfaceShadows: false | ||||
|         Boolean BackfaceShadows : false | ||||
|     } | ||||
| 
 | ||||
|  Technique { | ||||
|     Technique { | ||||
|         LightMode SinglePass | ||||
|          | ||||
|         VertexShader GLSL100:   Common/MatDefs/Light/SPLighting.vert | ||||
| @ -149,7 +149,7 @@ MaterialDef Phong Lighting { | ||||
|             SEPARATE_TEXCOORD : SeparateTexCoord | ||||
|             DISCARD_ALPHA : AlphaDiscardThreshold | ||||
|             USE_REFLECTION : EnvMap | ||||
|             SPHERE_MAP : SphereMap   | ||||
|             SPHERE_MAP : EnvMapAsSphereMap   | ||||
|             NUM_BONES : NumberOfBones                         | ||||
|             INSTANCING : UseInstancing | ||||
|         } | ||||
| @ -188,7 +188,7 @@ MaterialDef Phong Lighting { | ||||
|             SEPARATE_TEXCOORD : SeparateTexCoord | ||||
|             DISCARD_ALPHA : AlphaDiscardThreshold | ||||
|             USE_REFLECTION : EnvMap | ||||
|             SPHERE_MAP : SphereMap   | ||||
|             SPHERE_MAP : EnvMapAsSphereMap   | ||||
|             NUM_BONES : NumberOfBones                         | ||||
|             INSTANCING : UseInstancing | ||||
|         } | ||||
| @ -209,7 +209,6 @@ MaterialDef Phong Lighting { | ||||
|         } | ||||
| 
 | ||||
|         Defines { | ||||
|             COLOR_MAP : ColorMap | ||||
|             DISCARD_ALPHA : AlphaDiscardThreshold | ||||
|             NUM_BONES : NumberOfBones | ||||
|             INSTANCING : UseInstancing | ||||
| @ -218,7 +217,7 @@ MaterialDef Phong Lighting { | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     Technique PostShadow15{ | ||||
|     Technique PostShadow { | ||||
|         VertexShader GLSL150:   Common/MatDefs/Shadow/PostShadow.vert | ||||
|         FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag | ||||
| 
 | ||||
| @ -234,8 +233,7 @@ MaterialDef Phong Lighting { | ||||
|             HARDWARE_SHADOWS : HardwareShadows | ||||
|             FILTER_MODE : FilterMode | ||||
|             PCFEDGE : PCFEdge | ||||
|             DISCARD_ALPHA : AlphaDiscardThreshold            | ||||
|             COLOR_MAP : ColorMap | ||||
|             DISCARD_ALPHA : AlphaDiscardThreshold | ||||
|             SHADOWMAP_SIZE : ShadowMapSize | ||||
|             FADE : FadeInfo | ||||
|             PSSM : Splits | ||||
| @ -268,8 +266,7 @@ MaterialDef Phong Lighting { | ||||
|             HARDWARE_SHADOWS : HardwareShadows | ||||
|             FILTER_MODE : FilterMode | ||||
|             PCFEDGE : PCFEdge | ||||
|             DISCARD_ALPHA : AlphaDiscardThreshold            | ||||
|             COLOR_MAP : ColorMap | ||||
|             DISCARD_ALPHA : AlphaDiscardThreshold | ||||
|             SHADOWMAP_SIZE : ShadowMapSize | ||||
|             FADE : FadeInfo | ||||
|             PSSM : Splits | ||||
| @ -343,10 +340,6 @@ MaterialDef Phong Lighting { | ||||
|         Defines { | ||||
|             VERTEX_COLOR : UseVertexColor | ||||
|             MATERIAL_COLORS : UseMaterialColors | ||||
|             V_TANGENT : VTangent | ||||
|             MINNAERT  : Minnaert | ||||
|             WARDISO   : WardIso | ||||
| 
 | ||||
|             DIFFUSEMAP : DiffuseMap | ||||
|             NORMALMAP : NormalMap | ||||
|             SPECULARMAP : SpecularMap | ||||
|  | ||||
| @ -148,7 +148,7 @@ MaterialDef Unshaded { | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     Technique PostShadow15{ | ||||
|     Technique PostShadow { | ||||
|         VertexShader GLSL150:   Common/MatDefs/Shadow/PostShadow.vert | ||||
|         FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag | ||||
| 
 | ||||
| @ -181,7 +181,7 @@ MaterialDef Unshaded { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Technique PostShadow{ | ||||
|     Technique PostShadow { | ||||
|         VertexShader GLSL100:   Common/MatDefs/Shadow/PostShadow.vert | ||||
|         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.blender.BlenderModelLoader : blend | ||||
| 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.SceneWithAnimationLoader : fba | ||||
| LOADER com.jme3.scene.plugins.fbx.FbxLoader : fbx | ||||
|  | ||||
| @ -31,8 +31,12 @@ | ||||
|  */ | ||||
| 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.material.*; | ||||
| import com.jme3.material.RenderState.BlendEquation; | ||||
| import com.jme3.material.RenderState.BlendMode; | ||||
| import com.jme3.material.RenderState.FaceCullMode; | ||||
| 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.Vector2f; | ||||
| import com.jme3.math.Vector3f; | ||||
| import com.jme3.shader.DefineList; | ||||
| import com.jme3.shader.Shader; | ||||
| import com.jme3.shader.VarType; | ||||
| import com.jme3.texture.Texture; | ||||
| @ -73,15 +78,16 @@ public class J3MLoader implements AssetLoader { | ||||
|     private Material material; | ||||
|     private TechniqueDef technique; | ||||
|     private RenderState renderState; | ||||
|     private ArrayList<String> presetDefines = new ArrayList<String>(); | ||||
| 
 | ||||
|     private EnumMap<Shader.ShaderType, String> shaderLanguage; | ||||
|     private EnumMap<Shader.ShaderType, String> shaderName; | ||||
|     private EnumMap<Shader.ShaderType, String> shaderLanguages; | ||||
|     private EnumMap<Shader.ShaderType, String> shaderNames; | ||||
| 
 | ||||
|     private static final String whitespacePattern = "\\p{javaWhitespace}+"; | ||||
| 
 | ||||
|     public J3MLoader() { | ||||
|         shaderLanguage = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); | ||||
|         shaderName = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class); | ||||
|         shaderLanguages = new EnumMap<>(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) { | ||||
|         shaderName.put(shaderType, name); | ||||
|         shaderLanguage.put(shaderType, language); | ||||
|         shaderNames.put(shaderType, name); | ||||
|         shaderLanguages.put(shaderType, language); | ||||
|     } | ||||
| 
 | ||||
|     // LightMode <MODE> | ||||
| @ -443,9 +449,12 @@ public class J3MLoader implements AssetLoader { | ||||
|             renderState.setDepthTest(parseBoolean(split[1])); | ||||
|         }else if (split[0].equals("Blend")){ | ||||
|             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")){ | ||||
|             renderState.setAlphaTest(true); | ||||
|             renderState.setAlphaFallOff(Float.parseFloat(split[1])); | ||||
|             // Ignore for backwards compatbility | ||||
|         }else if (split[0].equals("PolyOffset")){ | ||||
|             float factor = Float.parseFloat(split[1]); | ||||
|             float units = Float.parseFloat(split[2]); | ||||
| @ -453,7 +462,7 @@ public class J3MLoader implements AssetLoader { | ||||
|         }else if (split[0].equals("ColorWrite")){ | ||||
|             renderState.setColorWrite(parseBoolean(split[1])); | ||||
|         }else if (split[0].equals("PointSprite")){ | ||||
|             renderState.setPointSprite(parseBoolean(split[1])); | ||||
|             // Ignore for backwards compatbility | ||||
|         }else if (split[0].equals("DepthFunc")){ | ||||
|             renderState.setDepthFunc(RenderState.TestFunction.valueOf(split[1])); | ||||
|         }else if (split[0].equals("AlphaFunc")){ | ||||
| @ -495,10 +504,22 @@ public class J3MLoader implements AssetLoader { | ||||
|     private void readDefine(String statement) throws IOException{ | ||||
|         String[] split = statement.split(":"); | ||||
|         if (split.length == 1){ | ||||
|             // add preset define | ||||
|             technique.addShaderPresetDefine(split[0].trim(), VarType.Boolean, true); | ||||
|             String defineName = split[0].trim(); | ||||
|             presetDefines.add(defineName); | ||||
|         }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{ | ||||
|             throw new IOException("Define syntax incorrect"); | ||||
|         } | ||||
| @ -560,37 +581,80 @@ public class J3MLoader implements AssetLoader { | ||||
|         } | ||||
|         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{ | ||||
|         isUseNodes = false; | ||||
|         String[] split = techStat.getLine().split(whitespacePattern); | ||||
| 
 | ||||
|         String name; | ||||
|         if (split.length == 1) { | ||||
|             technique = new TechniqueDef(null); | ||||
|             name = TechniqueDef.DEFAULT_TECHNIQUE_NAME; | ||||
|         } else if (split.length == 2) { | ||||
|             String techName = split[1]; | ||||
|             technique = new TechniqueDef(techName); | ||||
|             name = split[1]; | ||||
|         } else { | ||||
|             throw new IOException("Technique statement syntax incorrect"); | ||||
|         } | ||||
| 
 | ||||
|         String techniqueUniqueName = materialDef.getAssetName() + "@" + name; | ||||
|         technique = new TechniqueDef(name, techniqueUniqueName.hashCode()); | ||||
| 
 | ||||
|         for (Statement statement : techStat.getContents()){ | ||||
|             readTechniqueStatement(statement); | ||||
|         } | ||||
| 
 | ||||
|         if(isUseNodes){ | ||||
|             nodesLoaderDelegate.computeConditions(); | ||||
|              | ||||
|             //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"); | ||||
|         }else if (shaderNames.containsKey(Shader.ShaderType.Vertex) && shaderNames.containsKey(Shader.ShaderType.Fragment)) { | ||||
|             technique.setShaderFile(shaderNames, shaderLanguages); | ||||
|         } else { | ||||
|             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; | ||||
|         } | ||||
| 
 | ||||
|         if (shaderName.containsKey(Shader.ShaderType.Vertex) && shaderName.containsKey(Shader.ShaderType.Fragment)) { | ||||
|             technique.setShaderFile(shaderName, shaderLanguage); | ||||
|          | ||||
|         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); | ||||
|         technique = null; | ||||
|         shaderLanguage.clear(); | ||||
|         shaderName.clear(); | ||||
|         shaderLanguages.clear(); | ||||
|         shaderNames.clear(); | ||||
|         presetDefines.clear(); | ||||
|     } | ||||
| 
 | ||||
|     private void loadFromRoot(List<Statement> roots) throws IOException{ | ||||
| @ -711,7 +775,7 @@ public class J3MLoader implements AssetLoader { | ||||
| 
 | ||||
|     protected void initNodesLoader() { | ||||
|         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 (nodesLoaderDelegate == null) { | ||||
|                     nodesLoaderDelegate = new ShaderNodeLoaderDelegate(); | ||||
|  | ||||
| @ -44,6 +44,7 @@ import com.jme3.shader.ShaderNodeDefinition; | ||||
| import com.jme3.shader.ShaderNodeVariable; | ||||
| import com.jme3.shader.ShaderUtils; | ||||
| import com.jme3.shader.UniformBinding; | ||||
| import com.jme3.shader.VarType; | ||||
| import com.jme3.shader.VariableMapping; | ||||
| import com.jme3.util.blockparser.Statement; | ||||
| import java.io.IOException; | ||||
| @ -583,7 +584,7 @@ public class ShaderNodeLoaderDelegate { | ||||
|                     //multiplicity is not an int attempting to find for a material parameter. | ||||
|                     MatParam mp = findMatParam(multiplicity); | ||||
|                     if (mp != null) { | ||||
|                         addDefine(multiplicity); | ||||
|                         addDefine(multiplicity, VarType.Int); | ||||
|                         multiplicity = multiplicity.toUpperCase(); | ||||
|                     } else { | ||||
|                         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 | ||||
|      */ | ||||
|     public void addDefine(String paramName) { | ||||
|     public void addDefine(String paramName, VarType paramType) { | ||||
|         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) { | ||||
|             MatParam param = findMatParam(string); | ||||
|             if (param != null) { | ||||
|                 addDefine(param.getName()); | ||||
|                 addDefine(param.getName(), param.getVarType()); | ||||
|             } else { | ||||
|                 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){ | ||||
|             material.setTransparent(true); | ||||
|             material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); | ||||
|             material.getAdditionalRenderState().setAlphaTest(true); | ||||
|             material.getAdditionalRenderState().setAlphaFallOff(0.01f); | ||||
|             material.setFloat("AlphaDiscardThreshold", 0.01f); | ||||
|         } | ||||
|          | ||||
|         matList.put(matName, material); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (c) 2009-2012 jMonkeyEngine | ||||
|  * Copyright (c) 2009-2015 jMonkeyEngine | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * 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 | ||||
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| package com.jme3.animation; | ||||
| package com.jme3.asset; | ||||
| 
 | ||||
| /** | ||||
|  * @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack) | ||||
|  */ | ||||
| @Deprecated | ||||
| public final class BoneAnimation extends Animation { | ||||
| import com.jme3.asset.plugins.ClasspathLocator; | ||||
| import com.jme3.shader.plugins.GLSLLoader; | ||||
| import com.jme3.system.JmeSystem; | ||||
| import com.jme3.system.MockJmeSystemDelegate; | ||||
| import org.junit.Test; | ||||
| 
 | ||||
|     @Deprecated | ||||
|     public BoneAnimation(String name, float length) { | ||||
|         super(name, length); | ||||
| public class LoadShaderSourceTest { | ||||
| 
 | ||||
|     @Test | ||||
|     public void testLoadShaderSource() { | ||||
|         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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										171
									
								
								jme3-core/src/test/java/com/jme3/material/MaterialTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								jme3-core/src/test/java/com/jme3/material/MaterialTest.java
									
									
									
									
									
										Normal file
									
								
							| @ -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.Material; | ||||
| import com.jme3.material.MaterialDef; | ||||
| import com.jme3.renderer.Caps; | ||||
| import com.jme3.shader.VarType; | ||||
| import com.jme3.texture.Texture; | ||||
| import java.io.IOException; | ||||
| import java.util.EnumSet; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | ||||
| @ -18,6 +21,7 @@ import org.mockito.runners.MockitoJUnitRunner; | ||||
| 
 | ||||
| import static org.mockito.Matchers.any; | ||||
| import static org.mockito.Mockito.verify; | ||||
| import static org.junit.Assert.*; | ||||
| import static org.mockito.Mockito.when; | ||||
| 
 | ||||
| /** | ||||
| @ -51,6 +55,30 @@ public class J3MLoaderTest { | ||||
|         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 | ||||
|     public void oldStyleTextureParameters_shouldBeSupported() throws Exception { | ||||
|         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) { | ||||
|         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); | ||||
|         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 static org.junit.Assert.assertEquals; | ||||
| import org.junit.Ignore; | ||||
| 
 | ||||
| /** | ||||
|  * Verifies that algorithms in {@link FastMath} are working correctly. | ||||
|  *  | ||||
| @ -56,4 +59,39 @@ public class FastMathTest { | ||||
|             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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										173
									
								
								jme3-core/src/test/java/com/jme3/scene/MPOTestUtils.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								jme3-core/src/test/java/com/jme3/scene/MPOTestUtils.java
									
									
									
									
									
										Normal file
									
								
							| @ -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)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										300
									
								
								jme3-core/src/test/java/com/jme3/shader/DefineListTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								jme3-core/src/test/java/com/jme3/shader/DefineListTest.java
									
									
									
									
									
										Normal file
									
								
							| @ -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. | ||||
|  * | ||||
|  * 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 | ||||
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| package com.jme3.effect; | ||||
| package com.jme3.system; | ||||
| 
 | ||||
| import com.jme3.renderer.Camera; | ||||
| import java.util.Comparator; | ||||
| import com.jme3.audio.AudioRenderer; | ||||
| import java.io.IOException; | ||||
| import java.io.OutputStream; | ||||
| import java.net.URL; | ||||
| import java.nio.ByteBuffer; | ||||
| 
 | ||||
| @Deprecated | ||||
| class ParticleComparator implements Comparator<Particle> { | ||||
| public class MockJmeSystemDelegate extends JmeSystemDelegate { | ||||
| 
 | ||||
|     private Camera cam; | ||||
| 
 | ||||
|     public void setCamera(Camera cam){ | ||||
|         this.cam = cam; | ||||
|     @Override | ||||
|     public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException { | ||||
|     } | ||||
| 
 | ||||
|     public int compare(Particle p1, Particle p2) { | ||||
|         return 0; // unused | ||||
|         /* | ||||
|         if (p1.life <= 0 || p2.life <= 0) | ||||
|             return 0; | ||||
| 
 | ||||
| //        if (p1.life <= 0) | ||||
| //            return 1; | ||||
| //        else if (p2.life <= 0) | ||||
| //            return -1; | ||||
| 
 | ||||
|         float d1 = p1.distToCam, d2 = p2.distToCam; | ||||
| 
 | ||||
|         if (d1 == -1){ | ||||
|             d1 = cam.distanceToNearPlane(p1.position); | ||||
|             p1.distToCam = d1; | ||||
|         } | ||||
|         if (d2 == -1){ | ||||
|             d2 = cam.distanceToNearPlane(p2.position); | ||||
|             p2.distToCam = d2; | ||||
|         } | ||||
| 
 | ||||
|         if (d1 < d2) | ||||
|             return 1; | ||||
|         else if (d1 > d2) | ||||
|             return -1; | ||||
|         else | ||||
|             return 0; | ||||
|         */ | ||||
|     @Override | ||||
|     public void showErrorDialog(String message) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public URL getPlatformAssetConfigURL() { | ||||
|         return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/General.cfg"); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public JmeContext newContext(AppSettings settings, JmeContext.Type contextType) { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public AudioRenderer newAudioRenderer(AppSettings settings) { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void initialize(AppSettings settings) { | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void showSoftKeyboard(boolean show) { | ||||
|     } | ||||
|      | ||||
| } | ||||
| @ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (c) 2009-2012 jMonkeyEngine | ||||
|  * Copyright (c) 2009-2015 jMonkeyEngine | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * 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 | ||||
|  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  */ | ||||
| package com.jme3.terrain.heightmap; | ||||
| package com.jme3.system; | ||||
| 
 | ||||
| /** | ||||
|  * A heightmap that is built off an image. | ||||
|  * If you want to be able to supply different Image types to  | ||||
|  * ImageBaseHeightMapGrid, you need to implement this interface, | ||||
|  * and have that class extend Abstract heightmap. | ||||
|  *  | ||||
|  * @author bowens | ||||
|  * @deprecated | ||||
|  */ | ||||
| public interface ImageHeightmap { | ||||
| import com.jme3.asset.AssetConfig; | ||||
| import com.jme3.asset.AssetManager; | ||||
| import com.jme3.asset.DesktopAssetManager; | ||||
| import com.jme3.renderer.RenderManager; | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.Logger; | ||||
| 
 | ||||
| public class TestUtil { | ||||
|      | ||||
|     /** | ||||
|      * Set the image to use for this heightmap | ||||
|      */ | ||||
|     //public void setImage(Image image); | ||||
|     static { | ||||
|         JmeSystem.setSystemDelegate(new MockJmeSystemDelegate()); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * The BufferedImage.TYPE_ that is supported | ||||
|      * by this ImageHeightmap | ||||
|      */ | ||||
|     //public int getSupportedImageType(); | ||||
|     public static AssetManager createAssetManager() { | ||||
|         Logger.getLogger(AssetConfig.class.getName()).setLevel(Level.OFF); | ||||
|         return new DesktopAssetManager(true); | ||||
|     } | ||||
|      | ||||
|     public static RenderManager createRenderManager() { | ||||
|         return new RenderManager(new NullRenderer()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										6
									
								
								jme3-core/src/test/resources/no-default-technique.j3md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								jme3-core/src/test/resources/no-default-technique.j3md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| MaterialDef Test Material { | ||||
|     Technique Test { | ||||
|         VertexShader GLSL100 : test.vert | ||||
|         FragmentShader GLSL100 : test.frag | ||||
|     } | ||||
| } | ||||
							
								
								
									
										8
									
								
								jme3-core/src/test/resources/no-shader-specified.j3md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								jme3-core/src/test/resources/no-shader-specified.j3md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| MaterialDef Test Material { | ||||
|     Technique A { | ||||
|     } | ||||
|     Technique B { | ||||
|         VertexShader GLSL100 : test.vert | ||||
|         FragmentShader GLSL100 : test.frag | ||||
|     } | ||||
| } | ||||
							
								
								
									
										10
									
								
								jme3-core/src/test/resources/same-name-technique.j3md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								jme3-core/src/test/resources/same-name-technique.j3md
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								jme3-core/src/test/resources/testMatDef.j3md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								jme3-core/src/test/resources/testMatDef.j3md
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
| 
 | ||||
|         Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md"); | ||||
|         mat.getAdditionalRenderState().setAlphaTest(true); | ||||
|         Texture diffuseMap = atlas.getAtlasTexture("DiffuseMap"); | ||||
|         Texture normalMap = atlas.getAtlasTexture("NormalMap"); | ||||
|         Texture specularMap = atlas.getAtlasTexture("SpecularMap"); | ||||
|  | ||||
| @ -6,11 +6,12 @@ import com.jme3.asset.plugins.FileLocator; | ||||
| import com.jme3.material.MaterialDef; | ||||
| import com.jme3.material.TechniqueDef; | ||||
| import com.jme3.material.plugins.J3MLoader; | ||||
| import com.jme3.renderer.Caps; | ||||
| import com.jme3.shader.DefineList; | ||||
| import com.jme3.shader.Shader; | ||||
| import com.jme3.shader.ShaderKey; | ||||
| import com.jme3.shader.plugins.GLSLLoader; | ||||
| import com.jme3.system.JmeSystem; | ||||
| import java.util.EnumSet; | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.Logger; | ||||
| 
 | ||||
| @ -33,23 +34,22 @@ public class ShaderCheck { | ||||
|         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); | ||||
|         for (TechniqueDef techDef : def.getDefaultTechniques()){ | ||||
|             DefineList dl = new DefineList(); | ||||
|             dl.addFrom(techDef.getShaderPresetDefines()); | ||||
|             ShaderKey shaderKey = new ShaderKey(dl,techDef.getShaderProgramLanguages(),techDef.getShaderProgramNames()); | ||||
| 
 | ||||
|             Shader shader = assetManager.loadShader(shaderKey); | ||||
| 
 | ||||
|             for (Validator validator : validators){ | ||||
|         EnumSet<Caps> rendererCaps = EnumSet.noneOf(Caps.class); | ||||
|         rendererCaps.add(Caps.GLSL100); | ||||
|         for (TechniqueDef techDef : def.getTechniqueDefs(TechniqueDef.DEFAULT_TECHNIQUE_NAME)) { | ||||
|             DefineList defines = techDef.createDefineList(); | ||||
|             Shader shader = techDef.getShader(assetManager, rendererCaps, defines); | ||||
|             for (Validator validator : validators) { | ||||
|                 StringBuilder sb = new StringBuilder(); | ||||
|                 validator.validate(shader, sb); | ||||
|                 System.out.println("==== Validator: " + validator.getName() + " " +  | ||||
|                                         validator.getInstalledVersion() + " ===="); | ||||
|                 System.out.println("==== Validator: " + validator.getName() + " " | ||||
|                         + validator.getInstalledVersion() + " ===="); | ||||
|                 System.out.println(sb.toString()); | ||||
|             } | ||||
|         } | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
|            | ||||
|     public static void main(String[] args){ | ||||
|  | ||||
| @ -43,6 +43,7 @@ import com.jme3.system.JmeContext.Type; | ||||
| import com.jme3.util.Screenshots; | ||||
| import java.awt.EventQueue; | ||||
| import java.awt.Graphics2D; | ||||
| import java.awt.GraphicsEnvironment; | ||||
| import java.awt.RenderingHints; | ||||
| import java.awt.geom.AffineTransform; | ||||
| import java.awt.image.AffineTransformOp; | ||||
| @ -116,12 +117,16 @@ public class JmeDesktopSystem extends JmeSystemDelegate { | ||||
| 
 | ||||
|     @Override | ||||
|     public void showErrorDialog(String message) { | ||||
|         final String msg = message; | ||||
|         EventQueue.invokeLater(new Runnable() { | ||||
|             public void run() { | ||||
|                 ErrorDialog.showDialog(msg); | ||||
|             } | ||||
|         }); | ||||
|         if (!GraphicsEnvironment.isHeadless()) { | ||||
|             final String msg = message; | ||||
|             EventQueue.invokeLater(new Runnable() { | ||||
|                 public void run() { | ||||
|                     ErrorDialog.showDialog(msg); | ||||
|                 } | ||||
|             }); | ||||
|         } else { | ||||
|             System.err.println("[JME ERROR] " + message); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @ -129,6 +134,9 @@ public class JmeDesktopSystem extends JmeSystemDelegate { | ||||
|         if (SwingUtilities.isEventDispatchThread()) { | ||||
|             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); | ||||
|         settings.copyFrom(sourceSettings); | ||||
| @ -333,27 +341,13 @@ public class JmeDesktopSystem extends JmeSystemDelegate { | ||||
|         if (initialized) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         initialized = true; | ||||
|         try { | ||||
|             if (!lowPermissions) { | ||||
|                 // can only modify logging settings | ||||
|                 // if permissions are available | ||||
| //                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()); | ||||
|         if (!lowPermissions) { | ||||
|             if (NativeLibraryLoader.isUsingNativeBullet()) { | ||||
|                 NativeLibraryLoader.loadNativeLibrary("bulletjme", true); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @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" | ||||
|     } | ||||
| 
 | ||||
|     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" ){ | ||||
|         enableAssertions = true; | ||||
|  | ||||
| @ -139,7 +139,8 @@ public class TestCameraMotionPath extends SimpleApplication { | ||||
| 
 | ||||
| 
 | ||||
|         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); | ||||
|         rootNode.attachChild(soil); | ||||
|         DirectionalLight light = new DirectionalLight(); | ||||
|  | ||||
| @ -222,7 +222,8 @@ public class TestCinematic extends SimpleApplication { | ||||
|         matSoil.setColor("Diffuse", ColorRGBA.Green); | ||||
|         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.setShadowMode(ShadowMode.Receive); | ||||
|         rootNode.attachChild(soil); | ||||
|  | ||||
| @ -136,7 +136,8 @@ public class TestMotionPath extends SimpleApplication { | ||||
| 
 | ||||
| 
 | ||||
|         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); | ||||
| 
 | ||||
|         rootNode.attachChild(soil); | ||||
|  | ||||
| @ -57,7 +57,7 @@ public class TestAppStateLifeCycle extends SimpleApplication { | ||||
| 
 | ||||
|     @Override | ||||
|     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); | ||||
|         Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); | ||||
|         mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); | ||||
|  | ||||
| @ -56,7 +56,7 @@ public class TestBareBonesApp extends LegacyApplication { | ||||
|         System.out.println("Initialize"); | ||||
| 
 | ||||
|         // 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 | ||||
|         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 | ||||
|     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); | ||||
|         Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); | ||||
|         mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); | ||||
|  | ||||
| @ -69,7 +69,7 @@ public class TestAmbient extends SimpleApplication { | ||||
|     nature.play(); | ||||
|      | ||||
|     // 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); | ||||
|     Material mat1 = new Material(assetManager, | ||||
|             "Common/MatDefs/Misc/Unshaded.j3md"); | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user