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/
|
/.gradle/
|
||||||
/.nb-gradle/private/
|
/.nb-gradle/
|
||||||
/.nb-gradle/profiles/private/
|
|
||||||
/.idea/
|
/.idea/
|
||||||
/dist/
|
/dist/
|
||||||
/build/
|
/build/
|
||||||
|
/bin/
|
||||||
/netbeans/
|
/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
|
/.classpath
|
||||||
/.project
|
/.project
|
||||||
/.settings
|
/.settings
|
||||||
@ -130,21 +15,20 @@
|
|||||||
*.dylib
|
*.dylib
|
||||||
*.iml
|
*.iml
|
||||||
.DS_Store
|
.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_64/bulletjme.dll
|
||||||
!/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll
|
!/jme3-bullet-native/libs/native/windows/x86/bulletjme.dll
|
||||||
!/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib
|
!/jme3-bullet-native/libs/native/osx/x86/libbulletjme.dylib
|
||||||
!/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib
|
!/jme3-bullet-native/libs/native/osx/x86_64/libbulletjme.dylib
|
||||||
!/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so
|
!/jme3-bullet-native/libs/native/linux/x86/libbulletjme.so
|
||||||
!/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so
|
!/jme3-bullet-native/libs/native/linux/x86_64/libbulletjme.so
|
||||||
/.nb-gradle/
|
|
||||||
/sdk/ant-jme/nbproject/private/
|
|
||||||
/sdk/nbi/stub/ext/engine/nbproject/private/
|
|
||||||
/sdk/nbi/stub/ext/components/products/jdk/nbproject/private/
|
|
||||||
/sdk/nbi/stub/ext/components/products/blender/nbproject/private/
|
|
||||||
/sdk/nbi/stub/ext/components/products/helloworld/nbproject/private/
|
|
||||||
/sdk/BasicGameTemplate/nbproject/private/
|
|
||||||
/sdk/nbi/stub/ext/components/products/jdk/build/
|
|
||||||
/sdk/nbi/stub/ext/components/products/jdk/dist/
|
|
||||||
/sdk/jme3-dark-laf/nbproject/private/
|
|
||||||
jme3-lwjgl3/build/
|
|
||||||
|
@ -51,6 +51,12 @@ javadoc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
testLogging {
|
||||||
|
exceptionFormat = 'full'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
task sourcesJar(type: Jar, dependsOn: classes, description: 'Creates a jar from the source files.') {
|
task sourcesJar(type: Jar, dependsOn: classes, description: 'Creates a jar from the source files.') {
|
||||||
classifier = 'sources'
|
classifier = 'sources'
|
||||||
from sourceSets*.allSource
|
from sourceSets*.allSource
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
package com.jme3.asset;
|
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.texture.Image;
|
|
||||||
import com.jme3.texture.Image.Format;
|
|
||||||
import com.jme3.texture.image.ImageRaster;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>AndroidImageInfo</code> is set in a jME3 image via the {@link Image#setEfficentData(java.lang.Object) }
|
|
||||||
* method to retrieve a {@link Bitmap} when it is needed by the renderer.
|
|
||||||
* User code may extend <code>AndroidImageInfo</code> and provide their own implementation of the
|
|
||||||
* {@link AndroidImageInfo#loadBitmap()} method to acquire a bitmap by their own means.
|
|
||||||
*
|
|
||||||
* @author Kirill Vainer
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class AndroidImageInfo extends ImageRaster {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(AndroidImageInfo.class.getName());
|
|
||||||
|
|
||||||
protected AssetInfo assetInfo;
|
|
||||||
protected Bitmap bitmap;
|
|
||||||
protected Format format;
|
|
||||||
|
|
||||||
public AndroidImageInfo(AssetInfo assetInfo) {
|
|
||||||
this.assetInfo = assetInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap getBitmap(){
|
|
||||||
if (bitmap == null || bitmap.isRecycled()){
|
|
||||||
try {
|
|
||||||
loadBitmap();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// If called first inside AssetManager, the error will propagate
|
|
||||||
// correctly. Assuming that if the first calls succeeds
|
|
||||||
// then subsequent calls will as well.
|
|
||||||
throw new AssetLoadException("Failed to load image " + assetInfo.getKey(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifyBitmapUploaded() {
|
|
||||||
// Default function is to recycle the bitmap.
|
|
||||||
if (bitmap != null && !bitmap.isRecycled()) {
|
|
||||||
bitmap.recycle();
|
|
||||||
bitmap = null;
|
|
||||||
logger.log(Level.FINE, "Bitmap was deleted. ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Format getFormat(){
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getWidth() {
|
|
||||||
return getBitmap().getWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHeight() {
|
|
||||||
return getBitmap().getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPixel(int x, int y, ColorRGBA color) {
|
|
||||||
getBitmap().setPixel(x, y, color.asIntARGB());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ColorRGBA getPixel(int x, int y, ColorRGBA store) {
|
|
||||||
if (store == null) {
|
|
||||||
store = new ColorRGBA();
|
|
||||||
}
|
|
||||||
store.fromIntARGB(getBitmap().getPixel(x, y));
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the bitmap directly from the asset info, possibly updating
|
|
||||||
* or creating the image object.
|
|
||||||
*/
|
|
||||||
protected void loadBitmap() throws IOException{
|
|
||||||
InputStream in = null;
|
|
||||||
try {
|
|
||||||
in = assetInfo.openStream();
|
|
||||||
bitmap = BitmapFactory.decodeStream(in);
|
|
||||||
if (bitmap == null) {
|
|
||||||
throw new IOException("Failed to load image: " + assetInfo.getKey().getName());
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (in != null) {
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (bitmap.getConfig()) {
|
|
||||||
case ALPHA_8:
|
|
||||||
format = Image.Format.Alpha8;
|
|
||||||
break;
|
|
||||||
case ARGB_8888:
|
|
||||||
format = Image.Format.RGBA8;
|
|
||||||
break;
|
|
||||||
case RGB_565:
|
|
||||||
format = Image.Format.RGB565;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// This should still work as long
|
|
||||||
// as renderer doesn't check format
|
|
||||||
// but just loads bitmap directly.
|
|
||||||
format = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureKey texKey = (TextureKey) assetInfo.getKey();
|
|
||||||
if (texKey.isFlipY()) {
|
|
||||||
// Flip the image, then delete the old one.
|
|
||||||
Matrix flipMat = new Matrix();
|
|
||||||
flipMat.preScale(1.0f, -1.0f);
|
|
||||||
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), flipMat, false);
|
|
||||||
bitmap.recycle();
|
|
||||||
bitmap = newBitmap;
|
|
||||||
|
|
||||||
if (bitmap == null) {
|
|
||||||
throw new IOException("Failed to flip image: " + texKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,533 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
package com.jme3.audio.android;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.AssetFileDescriptor;
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.media.MediaPlayer;
|
|
||||||
import android.media.SoundPool;
|
|
||||||
import com.jme3.asset.AssetKey;
|
|
||||||
import com.jme3.audio.*;
|
|
||||||
import com.jme3.audio.AudioSource.Status;
|
|
||||||
import com.jme3.audio.openal.ALAudioRenderer;
|
|
||||||
import com.jme3.math.FastMath;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is the android implementation for {@link AudioRenderer}
|
|
||||||
*
|
|
||||||
* @author larynx
|
|
||||||
* @author plan_rich
|
|
||||||
*
|
|
||||||
* @deprecated No longer supported due to too many limitations.
|
|
||||||
* Please use the generic {@link ALAudioRenderer} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class AndroidMediaPlayerAudioRenderer implements AudioRenderer,
|
|
||||||
SoundPool.OnLoadCompleteListener, MediaPlayer.OnCompletionListener {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(AndroidMediaPlayerAudioRenderer.class.getName());
|
|
||||||
private final static int MAX_NUM_CHANNELS = 16;
|
|
||||||
private final HashMap<AudioSource, MediaPlayer> musicPlaying = new HashMap<AudioSource, MediaPlayer>();
|
|
||||||
private SoundPool soundPool = null;
|
|
||||||
private final Vector3f listenerPosition = new Vector3f();
|
|
||||||
// For temp use
|
|
||||||
private final Vector3f distanceVector = new Vector3f();
|
|
||||||
private final AssetManager assetManager;
|
|
||||||
private HashMap<Integer, AudioSource> soundpoolStillLoading = new HashMap<Integer, AudioSource>();
|
|
||||||
private Listener listener;
|
|
||||||
private boolean audioDisabled = false;
|
|
||||||
private final AudioManager manager;
|
|
||||||
|
|
||||||
public AndroidMediaPlayerAudioRenderer(Activity context) {
|
|
||||||
manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
|
||||||
context.setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
|
||||||
assetManager = context.getAssets();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize() {
|
|
||||||
soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC,
|
|
||||||
0);
|
|
||||||
soundPool.setOnLoadCompleteListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateSourceParam(AudioSource src, AudioParam param) {
|
|
||||||
if (audioDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src.getChannel() < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (param) {
|
|
||||||
case Position:
|
|
||||||
if (!src.isPositional()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3f pos = src.getPosition();
|
|
||||||
break;
|
|
||||||
case Velocity:
|
|
||||||
if (!src.isPositional()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3f vel = src.getVelocity();
|
|
||||||
break;
|
|
||||||
case MaxDistance:
|
|
||||||
if (!src.isPositional()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RefDistance:
|
|
||||||
if (!src.isPositional()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ReverbFilter:
|
|
||||||
if (!src.isPositional() || !src.isReverbEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ReverbEnabled:
|
|
||||||
if (!src.isPositional()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src.isReverbEnabled()) {
|
|
||||||
updateSourceParam(src, AudioParam.ReverbFilter);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IsPositional:
|
|
||||||
break;
|
|
||||||
case Direction:
|
|
||||||
if (!src.isDirectional()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3f dir = src.getDirection();
|
|
||||||
break;
|
|
||||||
case InnerAngle:
|
|
||||||
if (!src.isDirectional()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case OuterAngle:
|
|
||||||
if (!src.isDirectional()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IsDirectional:
|
|
||||||
if (src.isDirectional()) {
|
|
||||||
updateSourceParam(src, AudioParam.Direction);
|
|
||||||
updateSourceParam(src, AudioParam.InnerAngle);
|
|
||||||
updateSourceParam(src, AudioParam.OuterAngle);
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DryFilter:
|
|
||||||
if (src.getDryFilter() != null) {
|
|
||||||
Filter f = src.getDryFilter();
|
|
||||||
if (f.isUpdateNeeded()) {
|
|
||||||
// updateFilter(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Looping:
|
|
||||||
if (src.isLooping()) {
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Volume:
|
|
||||||
MediaPlayer mp = musicPlaying.get(src);
|
|
||||||
if (mp != null) {
|
|
||||||
mp.setVolume(src.getVolume(), src.getVolume());
|
|
||||||
} else {
|
|
||||||
soundPool.setVolume(src.getChannel(), src.getVolume(),
|
|
||||||
src.getVolume());
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Pitch:
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateListenerParam(Listener listener, ListenerParam param) {
|
|
||||||
if (audioDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (param) {
|
|
||||||
case Position:
|
|
||||||
listenerPosition.set(listener.getLocation());
|
|
||||||
break;
|
|
||||||
case Rotation:
|
|
||||||
Vector3f dir = listener.getDirection();
|
|
||||||
Vector3f up = listener.getUp();
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Velocity:
|
|
||||||
Vector3f vel = listener.getVelocity();
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Volume:
|
|
||||||
// alListenerf(AL_GAIN, listener.getVolume());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(float tpf) {
|
|
||||||
float distance;
|
|
||||||
float volume;
|
|
||||||
|
|
||||||
// Loop over all mediaplayers
|
|
||||||
for (AudioSource src : musicPlaying.keySet()) {
|
|
||||||
|
|
||||||
MediaPlayer mp = musicPlaying.get(src);
|
|
||||||
|
|
||||||
// Calc the distance to the listener
|
|
||||||
distanceVector.set(listenerPosition);
|
|
||||||
distanceVector.subtractLocal(src.getPosition());
|
|
||||||
distance = FastMath.abs(distanceVector.length());
|
|
||||||
|
|
||||||
if (distance < src.getRefDistance()) {
|
|
||||||
distance = src.getRefDistance();
|
|
||||||
}
|
|
||||||
if (distance > src.getMaxDistance()) {
|
|
||||||
distance = src.getMaxDistance();
|
|
||||||
}
|
|
||||||
volume = src.getRefDistance() / distance;
|
|
||||||
|
|
||||||
AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
|
|
||||||
|
|
||||||
if (FastMath.abs(audioData.getCurrentVolume() - volume) > FastMath.FLT_EPSILON) {
|
|
||||||
// Left / Right channel get the same volume by now, only
|
|
||||||
// positional
|
|
||||||
mp.setVolume(volume, volume);
|
|
||||||
|
|
||||||
audioData.setCurrentVolume(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setListener(Listener listener) {
|
|
||||||
if (audioDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.listener != null) {
|
|
||||||
// previous listener no longer associated with current
|
|
||||||
// renderer
|
|
||||||
this.listener.setRenderer(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.listener = listener;
|
|
||||||
this.listener.setRenderer(this);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cleanup() {
|
|
||||||
// Cleanup sound pool
|
|
||||||
if (soundPool != null) {
|
|
||||||
soundPool.release();
|
|
||||||
soundPool = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup media player
|
|
||||||
for (AudioSource src : musicPlaying.keySet()) {
|
|
||||||
MediaPlayer mp = musicPlaying.get(src);
|
|
||||||
{
|
|
||||||
mp.stop();
|
|
||||||
mp.release();
|
|
||||||
src.setStatus(Status.Stopped);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
musicPlaying.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCompletion(MediaPlayer mp) {
|
|
||||||
if (mp.isPlaying()) {
|
|
||||||
mp.seekTo(0);
|
|
||||||
mp.stop();
|
|
||||||
}
|
|
||||||
// XXX: This has bad performance -> maybe change overall structure of
|
|
||||||
// mediaplayer in this audiorenderer?
|
|
||||||
for (AudioSource src : musicPlaying.keySet()) {
|
|
||||||
if (musicPlaying.get(src) == mp) {
|
|
||||||
src.setStatus(Status.Stopped);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays using the {@link SoundPool} of Android. Due to hard limitation of
|
|
||||||
* the SoundPool: After playing more instances of the sound you only have
|
|
||||||
* the channel of the last played instance.
|
|
||||||
*
|
|
||||||
* It is not possible to get information about the state of the soundpool of
|
|
||||||
* a specific streamid, so removing is not possilbe -> noone knows when
|
|
||||||
* sound finished.
|
|
||||||
*/
|
|
||||||
public void playSourceInstance(AudioSource src) {
|
|
||||||
if (audioDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
|
|
||||||
|
|
||||||
if (!(audioData.getAssetKey() instanceof AudioKey)) {
|
|
||||||
throw new IllegalArgumentException("Asset is not a AudioKey");
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioKey assetKey = (AudioKey) audioData.getAssetKey();
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
if (audioData.getId() < 0) { // found something to load
|
|
||||||
int soundId = soundPool.load(
|
|
||||||
assetManager.openFd(assetKey.getName()), 1);
|
|
||||||
audioData.setId(soundId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int channel = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
|
|
||||||
|
|
||||||
if (channel == 0) {
|
|
||||||
soundpoolStillLoading.put(audioData.getId(), src);
|
|
||||||
} else {
|
|
||||||
if (src.getStatus() != Status.Stopped) {
|
|
||||||
soundPool.stop(channel);
|
|
||||||
src.setStatus(Status.Stopped);
|
|
||||||
}
|
|
||||||
src.setChannel(channel); // receive a channel at the last
|
|
||||||
setSourceParams(src);
|
|
||||||
// playing at least
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.log(Level.SEVERE,
|
|
||||||
"Failed to load sound " + assetKey.getName(), e);
|
|
||||||
audioData.setId(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
|
|
||||||
AudioSource src = soundpoolStillLoading.remove(sampleId);
|
|
||||||
|
|
||||||
if (src == null) {
|
|
||||||
logger.warning("Something went terribly wrong! onLoadComplete"
|
|
||||||
+ " had sampleId which was not in the HashMap of loading items");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioData audioData = src.getAudioData();
|
|
||||||
|
|
||||||
// load was successfull
|
|
||||||
if (status == 0) {
|
|
||||||
int channelIndex;
|
|
||||||
channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
|
|
||||||
src.setChannel(channelIndex);
|
|
||||||
setSourceParams(src);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void playSource(AudioSource src) {
|
|
||||||
if (audioDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
|
|
||||||
|
|
||||||
MediaPlayer mp = musicPlaying.get(src);
|
|
||||||
if (mp == null) {
|
|
||||||
mp = new MediaPlayer();
|
|
||||||
mp.setOnCompletionListener(this);
|
|
||||||
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (src.getStatus() == Status.Stopped) {
|
|
||||||
mp.reset();
|
|
||||||
AssetKey<?> key = audioData.getAssetKey();
|
|
||||||
|
|
||||||
AssetFileDescriptor afd = assetManager.openFd(key.getName()); // assetKey.getName()
|
|
||||||
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
|
|
||||||
afd.getLength());
|
|
||||||
mp.prepare();
|
|
||||||
setSourceParams(src, mp);
|
|
||||||
src.setChannel(0);
|
|
||||||
src.setStatus(Status.Playing);
|
|
||||||
musicPlaying.put(src, mp);
|
|
||||||
mp.start();
|
|
||||||
} else {
|
|
||||||
mp.start();
|
|
||||||
}
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSourceParams(AudioSource src, MediaPlayer mp) {
|
|
||||||
mp.setLooping(src.isLooping());
|
|
||||||
mp.setVolume(src.getVolume(), src.getVolume());
|
|
||||||
//src.getDryFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSourceParams(AudioSource src) {
|
|
||||||
soundPool.setLoop(src.getChannel(), src.isLooping() ? -1 : 0);
|
|
||||||
soundPool.setVolume(src.getChannel(), src.getVolume(), src.getVolume());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pause the current playing sounds. Both from the {@link SoundPool} and the
|
|
||||||
* active {@link MediaPlayer}s
|
|
||||||
*/
|
|
||||||
public void pauseAll() {
|
|
||||||
if (soundPool != null) {
|
|
||||||
soundPool.autoPause();
|
|
||||||
for (MediaPlayer mp : musicPlaying.values()) {
|
|
||||||
if(mp.isPlaying()){
|
|
||||||
mp.pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resume all paused sounds.
|
|
||||||
*/
|
|
||||||
public void resumeAll() {
|
|
||||||
if (soundPool != null) {
|
|
||||||
soundPool.autoResume();
|
|
||||||
for (MediaPlayer mp : musicPlaying.values()) {
|
|
||||||
mp.start(); //no resume -> api says call start to resume
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void pauseSource(AudioSource src) {
|
|
||||||
if (audioDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaPlayer mp = musicPlaying.get(src);
|
|
||||||
if (mp != null) {
|
|
||||||
mp.pause();
|
|
||||||
src.setStatus(Status.Paused);
|
|
||||||
} else {
|
|
||||||
int channel = src.getChannel();
|
|
||||||
if (channel != -1) {
|
|
||||||
soundPool.pause(channel); // is not very likley to make
|
|
||||||
} // something useful :)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopSource(AudioSource src) {
|
|
||||||
if (audioDisabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// can be stream or buffer -> so try to get mediaplayer
|
|
||||||
// if there is non try to stop soundpool
|
|
||||||
MediaPlayer mp = musicPlaying.get(src);
|
|
||||||
if (mp != null) {
|
|
||||||
mp.stop();
|
|
||||||
mp.reset();
|
|
||||||
src.setStatus(Status.Stopped);
|
|
||||||
} else {
|
|
||||||
int channel = src.getChannel();
|
|
||||||
if (channel != -1) {
|
|
||||||
soundPool.pause(channel); // is not very likley to make
|
|
||||||
// something useful :)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteAudioData(AudioData ad) {
|
|
||||||
|
|
||||||
for (AudioSource src : musicPlaying.keySet()) {
|
|
||||||
if (src.getAudioData() == ad) {
|
|
||||||
MediaPlayer mp = musicPlaying.remove(src);
|
|
||||||
mp.stop();
|
|
||||||
mp.release();
|
|
||||||
src.setStatus(Status.Stopped);
|
|
||||||
src.setChannel(-1);
|
|
||||||
ad.setId(-1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ad.getId() > 0) {
|
|
||||||
soundPool.unload(ad.getId());
|
|
||||||
ad.setId(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEnvironment(Environment env) {
|
|
||||||
// not yet supported
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteFilter(Filter filter) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getSourcePlaybackTime(AudioSource src) {
|
|
||||||
throw new UnsupportedOperationException("Not supported yet.");
|
|
||||||
}
|
|
||||||
}
|
|
@ -523,4 +523,9 @@ public class AndroidGL implements GL, GLExt, GLFbo {
|
|||||||
public Object glFenceSync(int condition, int flags) {
|
public Object glFenceSync(int condition, int flags) {
|
||||||
throw new UnsupportedOperationException("OpenGL ES 2 does not support sync fences");
|
throw new UnsupportedOperationException("OpenGL ES 2 does not support sync fences");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void glBlendEquationSeparate(int colorMode, int alphaMode) {
|
||||||
|
GLES20.glBlendEquationSeparate(colorMode, alphaMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,591 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
package com.jme3.renderer.android;
|
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.opengl.ETC1;
|
|
||||||
import android.opengl.ETC1Util.ETC1Texture;
|
|
||||||
import android.opengl.GLES20;
|
|
||||||
import android.opengl.GLUtils;
|
|
||||||
import com.jme3.asset.AndroidImageInfo;
|
|
||||||
import com.jme3.renderer.RendererException;
|
|
||||||
import com.jme3.texture.Image;
|
|
||||||
import com.jme3.texture.Image.Format;
|
|
||||||
import com.jme3.util.BufferUtils;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Should not be used anymore. Use {@link GLRenderer} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class TextureUtil {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(TextureUtil.class.getName());
|
|
||||||
//TODO Make this configurable through appSettings
|
|
||||||
public static boolean ENABLE_COMPRESSION = true;
|
|
||||||
private static boolean NPOT = false;
|
|
||||||
private static boolean ETC1support = false;
|
|
||||||
private static boolean DXT1 = false;
|
|
||||||
private static boolean DEPTH24_STENCIL8 = false;
|
|
||||||
private static boolean DEPTH_TEXTURE = false;
|
|
||||||
private static boolean RGBA8 = false;
|
|
||||||
|
|
||||||
// Same constant used by both GL_ARM_rgba8 and GL_OES_rgb8_rgba8.
|
|
||||||
private static final int GL_RGBA8 = 0x8058;
|
|
||||||
|
|
||||||
private static final int GL_DXT1 = 0x83F0;
|
|
||||||
private static final int GL_DXT1A = 0x83F1;
|
|
||||||
|
|
||||||
private static final int GL_DEPTH_STENCIL_OES = 0x84F9;
|
|
||||||
private static final int GL_UNSIGNED_INT_24_8_OES = 0x84FA;
|
|
||||||
private static final int GL_DEPTH24_STENCIL8_OES = 0x88F0;
|
|
||||||
|
|
||||||
public static void loadTextureFeatures(String extensionString) {
|
|
||||||
ETC1support = extensionString.contains("GL_OES_compressed_ETC1_RGB8_texture");
|
|
||||||
DEPTH24_STENCIL8 = extensionString.contains("GL_OES_packed_depth_stencil");
|
|
||||||
NPOT = extensionString.contains("GL_IMG_texture_npot")
|
|
||||||
|| extensionString.contains("GL_OES_texture_npot")
|
|
||||||
|| extensionString.contains("GL_NV_texture_npot_2D_mipmap");
|
|
||||||
|
|
||||||
DXT1 = extensionString.contains("GL_EXT_texture_compression_dxt1");
|
|
||||||
DEPTH_TEXTURE = extensionString.contains("GL_OES_depth_texture");
|
|
||||||
|
|
||||||
RGBA8 = extensionString.contains("GL_ARM_rgba8") ||
|
|
||||||
extensionString.contains("GL_OES_rgb8_rgba8");
|
|
||||||
|
|
||||||
logger.log(Level.FINE, "Supports ETC1? {0}", ETC1support);
|
|
||||||
logger.log(Level.FINE, "Supports DEPTH24_STENCIL8? {0}", DEPTH24_STENCIL8);
|
|
||||||
logger.log(Level.FINE, "Supports NPOT? {0}", NPOT);
|
|
||||||
logger.log(Level.FINE, "Supports DXT1? {0}", DXT1);
|
|
||||||
logger.log(Level.FINE, "Supports DEPTH_TEXTURE? {0}", DEPTH_TEXTURE);
|
|
||||||
logger.log(Level.FINE, "Supports RGBA8? {0}", RGBA8);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void buildMipmap(Bitmap bitmap, boolean compress) {
|
|
||||||
int level = 0;
|
|
||||||
int height = bitmap.getHeight();
|
|
||||||
int width = bitmap.getWidth();
|
|
||||||
|
|
||||||
logger.log(Level.FINEST, " - Generating mipmaps for bitmap using SOFTWARE");
|
|
||||||
|
|
||||||
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
|
|
||||||
while (height >= 1 || width >= 1) {
|
|
||||||
//First of all, generate the texture from our bitmap and set it to the according level
|
|
||||||
if (compress) {
|
|
||||||
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) with compression.", new Object[]{level, width, height});
|
|
||||||
uploadBitmapAsCompressed(GLES20.GL_TEXTURE_2D, level, bitmap, false, 0, 0);
|
|
||||||
} else {
|
|
||||||
logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) directly.", new Object[]{level, width, height});
|
|
||||||
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, level, bitmap, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (height == 1 || width == 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Increase the mipmap level
|
|
||||||
height /= 2;
|
|
||||||
width /= 2;
|
|
||||||
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
|
|
||||||
|
|
||||||
// Recycle any bitmaps created as a result of scaling the bitmap.
|
|
||||||
// Do not recycle the original image (mipmap level 0)
|
|
||||||
if (level != 0) {
|
|
||||||
bitmap.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
bitmap = bitmap2;
|
|
||||||
|
|
||||||
level++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap, boolean subTexture, int x, int y) {
|
|
||||||
if (bitmap.hasAlpha()) {
|
|
||||||
logger.log(Level.FINEST, " - Uploading bitmap directly. Cannot compress as alpha present.");
|
|
||||||
if (subTexture) {
|
|
||||||
GLUtils.texSubImage2D(target, level, x, y, bitmap);
|
|
||||||
RendererUtil.checkGLError();
|
|
||||||
} else {
|
|
||||||
GLUtils.texImage2D(target, level, bitmap, 0);
|
|
||||||
RendererUtil.checkGLError();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Convert to RGB565
|
|
||||||
int bytesPerPixel = 2;
|
|
||||||
Bitmap rgb565 = bitmap.copy(Bitmap.Config.RGB_565, true);
|
|
||||||
|
|
||||||
// Put texture data into ByteBuffer
|
|
||||||
ByteBuffer inputImage = BufferUtils.createByteBuffer(bitmap.getRowBytes() * bitmap.getHeight());
|
|
||||||
rgb565.copyPixelsToBuffer(inputImage);
|
|
||||||
inputImage.position(0);
|
|
||||||
|
|
||||||
// Delete the copied RGB565 image
|
|
||||||
rgb565.recycle();
|
|
||||||
|
|
||||||
// Encode the image into the output bytebuffer
|
|
||||||
int encodedImageSize = ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight());
|
|
||||||
ByteBuffer compressedImage = BufferUtils.createByteBuffer(encodedImageSize);
|
|
||||||
ETC1.encodeImage(inputImage, bitmap.getWidth(),
|
|
||||||
bitmap.getHeight(),
|
|
||||||
bytesPerPixel,
|
|
||||||
bytesPerPixel * bitmap.getWidth(),
|
|
||||||
compressedImage);
|
|
||||||
|
|
||||||
// Delete the input image buffer
|
|
||||||
BufferUtils.destroyDirectBuffer(inputImage);
|
|
||||||
|
|
||||||
// Create an ETC1Texture from the compressed image data
|
|
||||||
ETC1Texture etc1tex = new ETC1Texture(bitmap.getWidth(), bitmap.getHeight(), compressedImage);
|
|
||||||
|
|
||||||
// Upload the ETC1Texture
|
|
||||||
if (bytesPerPixel == 2) {
|
|
||||||
int oldSize = (bitmap.getRowBytes() * bitmap.getHeight());
|
|
||||||
int newSize = compressedImage.capacity();
|
|
||||||
logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float) oldSize / newSize});
|
|
||||||
if (subTexture) {
|
|
||||||
GLES20.glCompressedTexSubImage2D(target,
|
|
||||||
level,
|
|
||||||
x, y,
|
|
||||||
bitmap.getWidth(),
|
|
||||||
bitmap.getHeight(),
|
|
||||||
ETC1.ETC1_RGB8_OES,
|
|
||||||
etc1tex.getData().capacity(),
|
|
||||||
etc1tex.getData());
|
|
||||||
|
|
||||||
RendererUtil.checkGLError();
|
|
||||||
} else {
|
|
||||||
GLES20.glCompressedTexImage2D(target,
|
|
||||||
level,
|
|
||||||
ETC1.ETC1_RGB8_OES,
|
|
||||||
bitmap.getWidth(),
|
|
||||||
bitmap.getHeight(),
|
|
||||||
0,
|
|
||||||
etc1tex.getData().capacity(),
|
|
||||||
etc1tex.getData());
|
|
||||||
|
|
||||||
RendererUtil.checkGLError();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB,
|
|
||||||
// GLES20.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);
|
|
||||||
// } else if (bytesPerPixel == 3) {
|
|
||||||
// ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB,
|
|
||||||
// GLES20.GL_UNSIGNED_BYTE, etc1Texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferUtils.destroyDirectBuffer(compressedImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>uploadTextureBitmap</code> uploads a native android bitmap
|
|
||||||
*/
|
|
||||||
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips) {
|
|
||||||
uploadTextureBitmap(target, bitmap, needMips, false, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <code>uploadTextureBitmap</code> uploads a native android bitmap
|
|
||||||
*/
|
|
||||||
public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips, boolean subTexture, int x, int y) {
|
|
||||||
boolean recycleBitmap = false;
|
|
||||||
//TODO, maybe this should raise an exception when NPOT is not supported
|
|
||||||
|
|
||||||
boolean willCompress = ENABLE_COMPRESSION && ETC1support && !bitmap.hasAlpha();
|
|
||||||
if (needMips && willCompress) {
|
|
||||||
// Image is compressed and mipmaps are desired, generate them
|
|
||||||
// using software.
|
|
||||||
buildMipmap(bitmap, willCompress);
|
|
||||||
} else {
|
|
||||||
if (willCompress) {
|
|
||||||
// Image is compressed but mipmaps are not desired, upload directly.
|
|
||||||
logger.log(Level.FINEST, " - Uploading compressed bitmap. Mipmaps are not generated.");
|
|
||||||
uploadBitmapAsCompressed(target, 0, bitmap, subTexture, x, y);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Image is not compressed, mipmaps may or may not be desired.
|
|
||||||
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
|
|
||||||
(needMips
|
|
||||||
? " Mipmaps will be generated in HARDWARE"
|
|
||||||
: " Mipmaps are not generated."));
|
|
||||||
if (subTexture) {
|
|
||||||
System.err.println("x : " + x + " y :" + y + " , " + bitmap.getWidth() + "/" + bitmap.getHeight());
|
|
||||||
GLUtils.texSubImage2D(target, 0, x, y, bitmap);
|
|
||||||
RendererUtil.checkGLError();
|
|
||||||
} else {
|
|
||||||
GLUtils.texImage2D(target, 0, bitmap, 0);
|
|
||||||
RendererUtil.checkGLError();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needMips) {
|
|
||||||
// No pregenerated mips available,
|
|
||||||
// generate from base level if required
|
|
||||||
GLES20.glGenerateMipmap(target);
|
|
||||||
RendererUtil.checkGLError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recycleBitmap) {
|
|
||||||
bitmap.recycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void uploadTextureAny(Image img, int target, int index, boolean needMips) {
|
|
||||||
if (img.getEfficentData() instanceof AndroidImageInfo) {
|
|
||||||
logger.log(Level.FINEST, " === Uploading image {0}. Using BITMAP PATH === ", img);
|
|
||||||
// If image was loaded from asset manager, use fast path
|
|
||||||
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
|
|
||||||
uploadTextureBitmap(target, imageInfo.getBitmap(), needMips);
|
|
||||||
} else {
|
|
||||||
logger.log(Level.FINEST, " === Uploading image {0}. Using BUFFER PATH === ", img);
|
|
||||||
boolean wantGeneratedMips = needMips && !img.hasMipmaps();
|
|
||||||
if (wantGeneratedMips && img.getFormat().isCompressed()) {
|
|
||||||
logger.log(Level.WARNING, "Generating mipmaps is only"
|
|
||||||
+ " supported for Bitmap based or non-compressed images!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload using slower path
|
|
||||||
logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
|
|
||||||
(wantGeneratedMips
|
|
||||||
? " Mipmaps will be generated in HARDWARE"
|
|
||||||
: " Mipmaps are not generated."));
|
|
||||||
|
|
||||||
uploadTexture(img, target, index);
|
|
||||||
|
|
||||||
// Image was uploaded using slower path, since it is not compressed,
|
|
||||||
// then compress it
|
|
||||||
if (wantGeneratedMips) {
|
|
||||||
// No pregenerated mips available,
|
|
||||||
// generate from base level if required
|
|
||||||
GLES20.glGenerateMipmap(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void unsupportedFormat(Format fmt) {
|
|
||||||
throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AndroidGLImageFormat getImageFormat(Format fmt, boolean forRenderBuffer)
|
|
||||||
throws UnsupportedOperationException {
|
|
||||||
AndroidGLImageFormat imageFormat = new AndroidGLImageFormat();
|
|
||||||
switch (fmt) {
|
|
||||||
case Depth32:
|
|
||||||
case Depth32F:
|
|
||||||
throw new UnsupportedOperationException("The image format '"
|
|
||||||
+ fmt + "' is not supported by OpenGL ES 2.0 specification.");
|
|
||||||
case Alpha8:
|
|
||||||
imageFormat.format = GLES20.GL_ALPHA;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
// Highest precision alpha supported by vanilla OGLES2
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_RGBA4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Luminance8:
|
|
||||||
imageFormat.format = GLES20.GL_LUMINANCE;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
// Highest precision luminance supported by vanilla OGLES2
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB565;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Luminance8Alpha8:
|
|
||||||
imageFormat.format = GLES20.GL_LUMINANCE_ALPHA;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_RGBA4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RGB565:
|
|
||||||
imageFormat.format = GLES20.GL_RGB;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5;
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB565;
|
|
||||||
break;
|
|
||||||
case RGB5A1:
|
|
||||||
imageFormat.format = GLES20.GL_RGBA;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1;
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB5_A1;
|
|
||||||
break;
|
|
||||||
case RGB8:
|
|
||||||
imageFormat.format = GLES20.GL_RGB;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
// Fallback: Use RGB565 if RGBA8 is not available.
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB565;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BGR8:
|
|
||||||
imageFormat.format = GLES20.GL_RGB;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_RGB565;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RGBA8:
|
|
||||||
imageFormat.format = GLES20.GL_RGBA;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
|
|
||||||
if (RGBA8) {
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_RGBA8;
|
|
||||||
} else {
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_RGBA4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Depth:
|
|
||||||
case Depth16:
|
|
||||||
if (!DEPTH_TEXTURE && !forRenderBuffer) {
|
|
||||||
unsupportedFormat(fmt);
|
|
||||||
}
|
|
||||||
imageFormat.format = GLES20.GL_DEPTH_COMPONENT;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT;
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_DEPTH_COMPONENT16;
|
|
||||||
break;
|
|
||||||
case Depth24:
|
|
||||||
case Depth24Stencil8:
|
|
||||||
if (!DEPTH_TEXTURE) {
|
|
||||||
unsupportedFormat(fmt);
|
|
||||||
}
|
|
||||||
if (DEPTH24_STENCIL8) {
|
|
||||||
// NEW: True Depth24 + Stencil8 format.
|
|
||||||
imageFormat.format = GL_DEPTH_STENCIL_OES;
|
|
||||||
imageFormat.dataType = GL_UNSIGNED_INT_24_8_OES;
|
|
||||||
imageFormat.renderBufferStorageFormat = GL_DEPTH24_STENCIL8_OES;
|
|
||||||
} else {
|
|
||||||
// Vanilla OGLES2, only Depth16 available.
|
|
||||||
imageFormat.format = GLES20.GL_DEPTH_COMPONENT;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT;
|
|
||||||
imageFormat.renderBufferStorageFormat = GLES20.GL_DEPTH_COMPONENT16;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DXT1:
|
|
||||||
if (!DXT1) {
|
|
||||||
unsupportedFormat(fmt);
|
|
||||||
}
|
|
||||||
imageFormat.format = GL_DXT1;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
|
|
||||||
imageFormat.compress = true;
|
|
||||||
break;
|
|
||||||
case DXT1A:
|
|
||||||
if (!DXT1) {
|
|
||||||
unsupportedFormat(fmt);
|
|
||||||
}
|
|
||||||
imageFormat.format = GL_DXT1A;
|
|
||||||
imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE;
|
|
||||||
imageFormat.compress = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new UnsupportedOperationException("Unrecognized format: " + fmt);
|
|
||||||
}
|
|
||||||
return imageFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AndroidGLImageFormat {
|
|
||||||
|
|
||||||
boolean compress = false;
|
|
||||||
int format = -1;
|
|
||||||
int renderBufferStorageFormat = -1;
|
|
||||||
int dataType = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void uploadTexture(Image img,
|
|
||||||
int target,
|
|
||||||
int index) {
|
|
||||||
|
|
||||||
if (img.getEfficentData() instanceof AndroidImageInfo) {
|
|
||||||
throw new RendererException("This image uses efficient data. "
|
|
||||||
+ "Use uploadTextureBitmap instead.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise upload image directly.
|
|
||||||
// Prefer to only use power of 2 textures here to avoid errors.
|
|
||||||
Image.Format fmt = img.getFormat();
|
|
||||||
ByteBuffer data;
|
|
||||||
if (index >= 0 || img.getData() != null && img.getData().size() > 0) {
|
|
||||||
data = img.getData(index);
|
|
||||||
} else {
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = img.getWidth();
|
|
||||||
int height = img.getHeight();
|
|
||||||
|
|
||||||
if (!NPOT && img.isNPOT()) {
|
|
||||||
// Check if texture is POT
|
|
||||||
throw new RendererException("Non-power-of-2 textures "
|
|
||||||
+ "are not supported by the video hardware "
|
|
||||||
+ "and no scaling path available for image: " + img);
|
|
||||||
}
|
|
||||||
AndroidGLImageFormat imageFormat = getImageFormat(fmt, false);
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int[] mipSizes = img.getMipMapSizes();
|
|
||||||
int pos = 0;
|
|
||||||
if (mipSizes == null) {
|
|
||||||
if (data != null) {
|
|
||||||
mipSizes = new int[]{data.capacity()};
|
|
||||||
} else {
|
|
||||||
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < mipSizes.length; i++) {
|
|
||||||
int mipWidth = Math.max(1, width >> i);
|
|
||||||
int mipHeight = Math.max(1, height >> i);
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
data.position(pos);
|
|
||||||
data.limit(pos + mipSizes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageFormat.compress && data != null) {
|
|
||||||
GLES20.glCompressedTexImage2D(target,
|
|
||||||
i,
|
|
||||||
imageFormat.format,
|
|
||||||
mipWidth,
|
|
||||||
mipHeight,
|
|
||||||
0,
|
|
||||||
data.remaining(),
|
|
||||||
data);
|
|
||||||
} else {
|
|
||||||
GLES20.glTexImage2D(target,
|
|
||||||
i,
|
|
||||||
imageFormat.format,
|
|
||||||
mipWidth,
|
|
||||||
mipHeight,
|
|
||||||
0,
|
|
||||||
imageFormat.format,
|
|
||||||
imageFormat.dataType,
|
|
||||||
data);
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += mipSizes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the texture currently bound to target at with data from the given
|
|
||||||
* Image at position x and y. The parameter index is used as the zoffset in
|
|
||||||
* case a 3d texture or texture 2d array is being updated.
|
|
||||||
*
|
|
||||||
* @param image Image with the source data (this data will be put into the
|
|
||||||
* texture)
|
|
||||||
* @param target the target texture
|
|
||||||
* @param index the mipmap level to update
|
|
||||||
* @param x the x position where to put the image in the texture
|
|
||||||
* @param y the y position where to put the image in the texture
|
|
||||||
*/
|
|
||||||
public static void uploadSubTexture(
|
|
||||||
Image img,
|
|
||||||
int target,
|
|
||||||
int index,
|
|
||||||
int x,
|
|
||||||
int y) {
|
|
||||||
if (img.getEfficentData() instanceof AndroidImageInfo) {
|
|
||||||
AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
|
|
||||||
uploadTextureBitmap(target, imageInfo.getBitmap(), true, true, x, y);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise upload image directly.
|
|
||||||
// Prefer to only use power of 2 textures here to avoid errors.
|
|
||||||
Image.Format fmt = img.getFormat();
|
|
||||||
ByteBuffer data;
|
|
||||||
if (index >= 0 || img.getData() != null && img.getData().size() > 0) {
|
|
||||||
data = img.getData(index);
|
|
||||||
} else {
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = img.getWidth();
|
|
||||||
int height = img.getHeight();
|
|
||||||
|
|
||||||
if (!NPOT && img.isNPOT()) {
|
|
||||||
// Check if texture is POT
|
|
||||||
throw new RendererException("Non-power-of-2 textures "
|
|
||||||
+ "are not supported by the video hardware "
|
|
||||||
+ "and no scaling path available for image: " + img);
|
|
||||||
}
|
|
||||||
AndroidGLImageFormat imageFormat = getImageFormat(fmt, false);
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int[] mipSizes = img.getMipMapSizes();
|
|
||||||
int pos = 0;
|
|
||||||
if (mipSizes == null) {
|
|
||||||
if (data != null) {
|
|
||||||
mipSizes = new int[]{data.capacity()};
|
|
||||||
} else {
|
|
||||||
mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < mipSizes.length; i++) {
|
|
||||||
int mipWidth = Math.max(1, width >> i);
|
|
||||||
int mipHeight = Math.max(1, height >> i);
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
data.position(pos);
|
|
||||||
data.limit(pos + mipSizes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageFormat.compress && data != null) {
|
|
||||||
GLES20.glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, data.remaining(), data);
|
|
||||||
RendererUtil.checkGLError();
|
|
||||||
} else {
|
|
||||||
GLES20.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, imageFormat.dataType, data);
|
|
||||||
RendererUtil.checkGLError();
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += mipSizes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package com.jme3.texture.plugins;
|
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import com.jme3.asset.AndroidImageInfo;
|
|
||||||
import com.jme3.asset.AssetInfo;
|
|
||||||
import com.jme3.asset.AssetLoader;
|
|
||||||
import com.jme3.texture.Image;
|
|
||||||
import com.jme3.texture.image.ColorSpace;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public class AndroidImageLoader implements AssetLoader {
|
|
||||||
|
|
||||||
public Object load(AssetInfo info) throws IOException {
|
|
||||||
AndroidImageInfo imageInfo = new AndroidImageInfo(info);
|
|
||||||
Bitmap bitmap = imageInfo.getBitmap();
|
|
||||||
|
|
||||||
Image image = new Image(imageInfo.getFormat(), bitmap.getWidth(), bitmap.getHeight(), null, ColorSpace.sRGB);
|
|
||||||
|
|
||||||
image.setEfficentData(imageInfo);
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
}
|
|
@ -46,7 +46,7 @@ public class SimpleTexturedTest extends SimpleApplication {
|
|||||||
|
|
||||||
|
|
||||||
shapeSphere = new Sphere(16, 16, .5f);
|
shapeSphere = new Sphere(16, 16, .5f);
|
||||||
shapeBox = new Box(Vector3f.ZERO, 0.3f, 0.3f, 0.3f);
|
shapeBox = new Box(0.3f, 0.3f, 0.3f);
|
||||||
|
|
||||||
|
|
||||||
// ModelConverter.optimize(geom);
|
// ModelConverter.optimize(geom);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.jme3.scene.plugins.blender.materials;
|
package com.jme3.scene.plugins.blender.materials;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@ -157,14 +158,14 @@ public final class MaterialContext implements Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// applying textures
|
// applying textures
|
||||||
if (loadedTextures != null && loadedTextures.size() > 0) {
|
|
||||||
int textureIndex = 0;
|
int textureIndex = 0;
|
||||||
|
if (loadedTextures != null && loadedTextures.size() > 0) {
|
||||||
if (loadedTextures.size() > TextureHelper.TEXCOORD_TYPES.length) {
|
if (loadedTextures.size() > TextureHelper.TEXCOORD_TYPES.length) {
|
||||||
LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different textures. JME supports only {0} UV mappings.", TextureHelper.TEXCOORD_TYPES.length);
|
LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different textures. JME supports only {0} UV mappings.", TextureHelper.TEXCOORD_TYPES.length);
|
||||||
}
|
}
|
||||||
for (CombinedTexture combinedTexture : loadedTextures) {
|
for (CombinedTexture combinedTexture : loadedTextures) {
|
||||||
if (textureIndex < TextureHelper.TEXCOORD_TYPES.length) {
|
if (textureIndex < TextureHelper.TEXCOORD_TYPES.length) {
|
||||||
combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
|
String usedUserUVSet = combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
|
||||||
|
|
||||||
this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture());
|
this.setTexture(material, combinedTexture.getMappingType(), combinedTexture.getResultTexture());
|
||||||
List<Vector2f> uvs = combinedTexture.getResultUVS();
|
List<Vector2f> uvs = combinedTexture.getResultUVS();
|
||||||
@ -173,13 +174,19 @@ public final class MaterialContext implements Savable {
|
|||||||
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
|
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
|
||||||
geometry.getMesh().setBuffer(uvCoordsBuffer);
|
geometry.getMesh().setBuffer(uvCoordsBuffer);
|
||||||
}//uvs might be null if the user assigned non existing UV coordinates group name to the mesh (this should be fixed in blender file)
|
}//uvs might be null if the user assigned non existing UV coordinates group name to the mesh (this should be fixed in blender file)
|
||||||
|
|
||||||
|
if(usedUserUVSet != null) {
|
||||||
|
userDefinedUVCoordinates = new HashMap<>(userDefinedUVCoordinates);
|
||||||
|
userDefinedUVCoordinates.remove(usedUserUVSet);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length);
|
LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
|
}
|
||||||
LOGGER.fine("No textures found for the mesh, but UV coordinates are applied.");
|
|
||||||
int textureIndex = 0;
|
if (userDefinedUVCoordinates != null && userDefinedUVCoordinates.size() > 0) {
|
||||||
|
LOGGER.fine("Storing unused, user defined UV coordinates sets.");
|
||||||
if (userDefinedUVCoordinates.size() > TextureHelper.TEXCOORD_TYPES.length) {
|
if (userDefinedUVCoordinates.size() > TextureHelper.TEXCOORD_TYPES.length) {
|
||||||
LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different UV coordinates for the mesh. JME supports only {0} UV coordinates buffers.", TextureHelper.TEXCOORD_TYPES.length);
|
LOGGER.log(Level.WARNING, "The blender file has defined more than {0} different UV coordinates for the mesh. JME supports only {0} UV coordinates buffers.", TextureHelper.TEXCOORD_TYPES.length);
|
||||||
}
|
}
|
||||||
@ -190,7 +197,9 @@ public final class MaterialContext implements Savable {
|
|||||||
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
|
uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
|
||||||
geometry.getMesh().setBuffer(uvCoordsBuffer);
|
geometry.getMesh().setBuffer(uvCoordsBuffer);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.log(Level.WARNING, "The texture could not be applied because JME only supports up to {0} different UV's.", TextureHelper.TEXCOORD_TYPES.length);
|
LOGGER.log(Level.WARNING, "The user's UV set named: '{0}' could not be stored because JME only supports up to {1} different UV's.", new Object[] {
|
||||||
|
entry.getKey(), TextureHelper.TEXCOORD_TYPES.length
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,8 +133,10 @@ public class CombinedTexture {
|
|||||||
* were defined)
|
* were defined)
|
||||||
* @param blenderContext
|
* @param blenderContext
|
||||||
* the blender context
|
* the blender context
|
||||||
|
* @return the name of the user UV coordinates used (null if the UV's were
|
||||||
|
* generated)
|
||||||
*/
|
*/
|
||||||
public void flatten(Geometry geometry, Long geometriesOMA, Map<String, List<Vector2f>> userDefinedUVCoordinates, BlenderContext blenderContext) {
|
public String flatten(Geometry geometry, Long geometriesOMA, Map<String, List<Vector2f>> userDefinedUVCoordinates, BlenderContext blenderContext) {
|
||||||
Mesh mesh = geometry.getMesh();
|
Mesh mesh = geometry.getMesh();
|
||||||
Texture previousTexture = null;
|
Texture previousTexture = null;
|
||||||
UVCoordinatesType masterUVCoordinatesType = null;
|
UVCoordinatesType masterUVCoordinatesType = null;
|
||||||
@ -226,6 +228,7 @@ public class CombinedTexture {
|
|||||||
}
|
}
|
||||||
resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS();
|
resultUVS = ((TriangulatedTexture) resultTexture).getResultUVS();
|
||||||
resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture();
|
resultTexture = ((TriangulatedTexture) resultTexture).getResultTexture();
|
||||||
|
masterUserUVSetName = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// setting additional data
|
// setting additional data
|
||||||
@ -234,6 +237,8 @@ public class CombinedTexture {
|
|||||||
// otherwise ugly lines appear between the mesh faces
|
// otherwise ugly lines appear between the mesh faces
|
||||||
resultTexture.setMagFilter(MagFilter.Nearest);
|
resultTexture.setMagFilter(MagFilter.Nearest);
|
||||||
resultTexture.setMinFilter(MinFilter.NearestNoMipMaps);
|
resultTexture.setMinFilter(MinFilter.NearestNoMipMaps);
|
||||||
|
|
||||||
|
return masterUserUVSetName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,8 +34,15 @@ libraries {
|
|||||||
// linker.args "-static-libstdc++"
|
// linker.args "-static-libstdc++"
|
||||||
} else if (targetPlatform.operatingSystem.name == "windows") {
|
} else if (targetPlatform.operatingSystem.name == "windows") {
|
||||||
if (toolChain in Gcc) {
|
if (toolChain in Gcc) {
|
||||||
|
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 '-I', "${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"
|
||||||
}
|
}
|
||||||
|
cppCompiler.args "-fpermissive"
|
||||||
|
cppCompiler.args "-static"
|
||||||
|
linker.args "-static"
|
||||||
|
}
|
||||||
else if (toolChain in VisualCpp) {
|
else if (toolChain in VisualCpp) {
|
||||||
cppCompiler.args "/I${org.gradle.internal.jvm.Jvm.current().javaHome}\\include\\win32"
|
cppCompiler.args "/I${org.gradle.internal.jvm.Jvm.current().javaHome}\\include\\win32"
|
||||||
}
|
}
|
||||||
@ -76,6 +83,31 @@ sourceSets {
|
|||||||
|
|
||||||
// Set of target platforms, will be available based on build system
|
// Set of target platforms, will be available based on build system
|
||||||
model {
|
model {
|
||||||
|
|
||||||
|
toolChains {
|
||||||
|
gcc(Gcc)
|
||||||
|
mingw_x86(Gcc) {
|
||||||
|
eachPlatform() {
|
||||||
|
cCompiler.executable "i686-w64-mingw32-gcc"
|
||||||
|
cppCompiler.executable "i686-w64-mingw32-g++"
|
||||||
|
linker.executable "i686-w64-mingw32-g++"
|
||||||
|
assembler.executable "i686-w64-mingw32-g++"
|
||||||
|
staticLibArchiver.executable "i686-w64-mingw32-gcc-ar"
|
||||||
|
}
|
||||||
|
target("windows_x86")
|
||||||
|
}
|
||||||
|
mingw_x86_64(Gcc) {
|
||||||
|
eachPlatform() {
|
||||||
|
cCompiler.executable "x86_64-w64-mingw32-gcc"
|
||||||
|
cppCompiler.executable "x86_64-w64-mingw32-g++"
|
||||||
|
linker.executable "x86_64-w64-mingw32-g++"
|
||||||
|
assembler.executable "x86_64-w64-mingw32-g++"
|
||||||
|
staticLibArchiver.executable "x86_64-w64-mingw32-gcc-ar"
|
||||||
|
}
|
||||||
|
target("windows_x86_64")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
platforms{
|
platforms{
|
||||||
// osx_universal { // TODO: universal binary doesn't work?
|
// osx_universal { // TODO: universal binary doesn't work?
|
||||||
// architecture 'x86_64'
|
// architecture 'x86_64'
|
||||||
|
@ -204,6 +204,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
* @param skeleton the skeleton
|
* @param skeleton the skeleton
|
||||||
*/
|
*/
|
||||||
public SkeletonControl(Skeleton skeleton) {
|
public SkeletonControl(Skeleton skeleton) {
|
||||||
|
if (skeleton == null) {
|
||||||
|
throw new IllegalArgumentException("skeleton cannot be null");
|
||||||
|
}
|
||||||
this.skeleton = skeleton;
|
this.skeleton = skeleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +409,23 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
|
|||||||
// Not automatic set cloning yet
|
// Not automatic set cloning yet
|
||||||
Set<Material> newMaterials = new HashSet<Material>();
|
Set<Material> newMaterials = new HashSet<Material>();
|
||||||
for( Material m : this.materials ) {
|
for( Material m : this.materials ) {
|
||||||
newMaterials.add(cloner.clone(m));
|
Material mClone = cloner.clone(m);
|
||||||
|
newMaterials.add(mClone);
|
||||||
|
if( mClone != m ) {
|
||||||
|
// Material was really cloned so clear the bone matrices in case
|
||||||
|
// this is hardware skinned. This allows a local version to be
|
||||||
|
// used and will be reset on the material. Really this just avoids
|
||||||
|
// the 'safety' check in controlRenderHardware(). Right now material
|
||||||
|
// doesn't clone itself with the cloner (and doesn't clone its parameters)
|
||||||
|
// else this would be unnecessary.
|
||||||
|
MatParam boneMatrices = mClone.getParam("BoneMatrices");
|
||||||
|
|
||||||
|
// ...because for some strange reason you can't clear a non-existant
|
||||||
|
// parameter.
|
||||||
|
if( boneMatrices != null ) {
|
||||||
|
mClone.clearParam("BoneMatrices");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.materials = newMaterials;
|
this.materials = newMaterials;
|
||||||
}
|
}
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
package com.jme3.animation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack)
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class SpatialAnimation extends Animation {
|
|
||||||
public SpatialAnimation(String name, float length) {
|
|
||||||
super(name, length);
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,6 +32,7 @@
|
|||||||
package com.jme3.app;
|
package com.jme3.app;
|
||||||
|
|
||||||
import com.jme3.app.state.AppState;
|
import com.jme3.app.state.AppState;
|
||||||
|
import com.jme3.audio.AudioListenerState;
|
||||||
import com.jme3.font.BitmapFont;
|
import com.jme3.font.BitmapFont;
|
||||||
import com.jme3.font.BitmapText;
|
import com.jme3.font.BitmapText;
|
||||||
import com.jme3.input.FlyByCamera;
|
import com.jme3.input.FlyByCamera;
|
||||||
@ -96,7 +97,7 @@ public abstract class SimpleApplication extends LegacyApplication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SimpleApplication() {
|
public SimpleApplication() {
|
||||||
this( new StatsAppState(), new FlyCamAppState(), new DebugKeysAppState() );
|
this(new StatsAppState(), new FlyCamAppState(), new AudioListenerState(), new DebugKeysAppState());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleApplication( AppState... initialStates ) {
|
public SimpleApplication( AppState... initialStates ) {
|
||||||
|
@ -41,9 +41,7 @@ import com.jme3.post.FilterPostProcessor;
|
|||||||
import com.jme3.renderer.Caps;
|
import com.jme3.renderer.Caps;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.plugins.OBJLoader;
|
import com.jme3.scene.plugins.OBJLoader;
|
||||||
import com.jme3.shader.Shader;
|
|
||||||
import com.jme3.shader.ShaderGenerator;
|
import com.jme3.shader.ShaderGenerator;
|
||||||
import com.jme3.shader.ShaderKey;
|
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.texture.plugins.TGALoader;
|
import com.jme3.texture.plugins.TGALoader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -320,13 +318,6 @@ public interface AssetManager {
|
|||||||
*/
|
*/
|
||||||
public Material loadMaterial(String name);
|
public Material loadMaterial(String name);
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads shader file(s), shouldn't be used by end-user in most cases.
|
|
||||||
*
|
|
||||||
* @see AssetManager#loadAsset(com.jme3.asset.AssetKey)
|
|
||||||
*/
|
|
||||||
public Shader loadShader(ShaderKey key);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a font file. Font files are in AngelCode text format,
|
* Load a font file. Font files are in AngelCode text format,
|
||||||
* and are with the extension "fnt".
|
* and are with the extension "fnt".
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
package com.jme3.asset;
|
package com.jme3.asset;
|
||||||
|
|
||||||
import com.jme3.asset.cache.AssetCache;
|
import com.jme3.asset.cache.AssetCache;
|
||||||
import com.jme3.asset.cache.SimpleAssetCache;
|
|
||||||
import com.jme3.audio.AudioData;
|
import com.jme3.audio.AudioData;
|
||||||
import com.jme3.audio.AudioKey;
|
import com.jme3.audio.AudioKey;
|
||||||
import com.jme3.font.BitmapFont;
|
import com.jme3.font.BitmapFont;
|
||||||
@ -42,9 +41,7 @@ import com.jme3.renderer.Caps;
|
|||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.shader.Glsl100ShaderGenerator;
|
import com.jme3.shader.Glsl100ShaderGenerator;
|
||||||
import com.jme3.shader.Glsl150ShaderGenerator;
|
import com.jme3.shader.Glsl150ShaderGenerator;
|
||||||
import com.jme3.shader.Shader;
|
|
||||||
import com.jme3.shader.ShaderGenerator;
|
import com.jme3.shader.ShaderGenerator;
|
||||||
import com.jme3.shader.ShaderKey;
|
|
||||||
import com.jme3.system.JmeSystem;
|
import com.jme3.system.JmeSystem;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -431,36 +428,6 @@ public class DesktopAssetManager implements AssetManager {
|
|||||||
return loadFilter(new FilterKey(name));
|
return loadFilter(new FilterKey(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a vertex/fragment shader combo.
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
* @return the loaded {@link Shader}
|
|
||||||
*/
|
|
||||||
public Shader loadShader(ShaderKey key){
|
|
||||||
// cache abuse in method
|
|
||||||
// that doesn't use loaders/locators
|
|
||||||
AssetCache cache = handler.getCache(SimpleAssetCache.class);
|
|
||||||
Shader shader = (Shader) cache.getFromCache(key);
|
|
||||||
if (shader == null){
|
|
||||||
if (key.isUsesShaderNodes()) {
|
|
||||||
if(shaderGenerator == null){
|
|
||||||
throw new UnsupportedOperationException("ShaderGenerator was not initialized, make sure assetManager.getGenerator(caps) has been called");
|
|
||||||
}
|
|
||||||
shader = shaderGenerator.generateShader();
|
|
||||||
} else {
|
|
||||||
shader = new Shader();
|
|
||||||
shader.initialize();
|
|
||||||
for (Shader.ShaderType shaderType : key.getUsedShaderPrograms()) {
|
|
||||||
shader.addSource(shaderType,key.getShaderProgramName(shaderType),(String) loadAsset(new AssetKey(key.getShaderProgramName(shaderType))),key.getDefines().getCompiled(),key.getShaderProgramLanguage(shaderType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cache.addToCache(key, shader);
|
|
||||||
}
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -123,24 +123,6 @@ public class TextureKey extends AssetKey<Texture> {
|
|||||||
this.anisotropy = anisotropy;
|
this.anisotropy = anisotropy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #setTextureTypeHint(com.jme3.texture.Texture.Type) }
|
|
||||||
* instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public boolean isAsCube() {
|
|
||||||
return textureTypeHint == Type.CubeMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #setTextureTypeHint(com.jme3.texture.Texture.Type) }
|
|
||||||
* instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setAsCube(boolean asCube) {
|
|
||||||
textureTypeHint = asCube ? Type.CubeMap : Type.TwoDimensional;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGenerateMips() {
|
public boolean isGenerateMips() {
|
||||||
return generateMips;
|
return generateMips;
|
||||||
}
|
}
|
||||||
@ -149,24 +131,6 @@ public class TextureKey extends AssetKey<Texture> {
|
|||||||
this.generateMips = generateMips;
|
this.generateMips = generateMips;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #setTextureTypeHint(com.jme3.texture.Texture.Type) }
|
|
||||||
* instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public boolean isAsTexture3D() {
|
|
||||||
return textureTypeHint == Type.ThreeDimensional;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #setTextureTypeHint(com.jme3.texture.Texture.Type) }
|
|
||||||
* instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setAsTexture3D(boolean asTexture3D) {
|
|
||||||
textureTypeHint = asTexture3D ? Type.ThreeDimensional : Type.TwoDimensional;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of texture expected to be returned.
|
* The type of texture expected to be returned.
|
||||||
*
|
*
|
||||||
|
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 AudioData data = null;
|
||||||
protected transient volatile AudioSource.Status status = AudioSource.Status.Stopped;
|
protected transient volatile AudioSource.Status status = AudioSource.Status.Stopped;
|
||||||
protected transient volatile int channel = -1;
|
protected transient volatile int channel = -1;
|
||||||
|
protected Vector3f previousWorldTranslation = Vector3f.NAN;
|
||||||
protected Vector3f velocity = new Vector3f();
|
protected Vector3f velocity = new Vector3f();
|
||||||
protected boolean reverbEnabled = false;
|
protected boolean reverbEnabled = false;
|
||||||
protected float maxDistance = 200; // 200 meters
|
protected float maxDistance = 200; // 200 meters
|
||||||
@ -88,6 +89,8 @@ public class AudioNode extends Node implements AudioSource {
|
|||||||
protected float innerAngle = 360;
|
protected float innerAngle = 360;
|
||||||
protected float outerAngle = 360;
|
protected float outerAngle = 360;
|
||||||
protected boolean positional = true;
|
protected boolean positional = true;
|
||||||
|
protected boolean velocityFromTranslation = false;
|
||||||
|
protected float lastTpf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>Status</code> indicates the current status of the audio node.
|
* <code>Status</code> indicates the current status of the audio node.
|
||||||
@ -702,17 +705,44 @@ public class AudioNode extends Node implements AudioSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isVelocityFromTranslation() {
|
||||||
public void updateGeometricState(){
|
return velocityFromTranslation;
|
||||||
boolean updatePos = false;
|
|
||||||
if ((refreshFlags & RF_TRANSFORM) != 0){
|
|
||||||
updatePos = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
super.updateGeometricState();
|
||||||
|
|
||||||
if (updatePos && channel >= 0)
|
if (channel < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f currentWorldTranslation = worldTransform.getTranslation();
|
||||||
|
|
||||||
|
if (Float.isNaN(previousWorldTranslation.x)
|
||||||
|
|| !previousWorldTranslation.equals(currentWorldTranslation)) {
|
||||||
|
|
||||||
getRenderer().updateSourceParam(this, AudioParam.Position);
|
getRenderer().updateSourceParam(this, AudioParam.Position);
|
||||||
|
|
||||||
|
if (velocityFromTranslation) {
|
||||||
|
velocity.set(currentWorldTranslation).subtractLocal(previousWorldTranslation);
|
||||||
|
velocity.multLocal(1f / lastTpf);
|
||||||
|
|
||||||
|
getRenderer().updateSourceParam(this, AudioParam.Velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousWorldTranslation.set(currentWorldTranslation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -772,6 +802,7 @@ public class AudioNode extends Node implements AudioSource {
|
|||||||
oc.write(outerAngle, "outer_angle", 360);
|
oc.write(outerAngle, "outer_angle", 360);
|
||||||
|
|
||||||
oc.write(positional, "positional", false);
|
oc.write(positional, "positional", false);
|
||||||
|
oc.write(velocityFromTranslation, "velocity_from_translation", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -806,6 +837,7 @@ public class AudioNode extends Node implements AudioSource {
|
|||||||
outerAngle = ic.readFloat("outer_angle", 360);
|
outerAngle = ic.readFloat("outer_angle", 360);
|
||||||
|
|
||||||
positional = ic.readBoolean("positional", false);
|
positional = ic.readBoolean("positional", false);
|
||||||
|
velocityFromTranslation = ic.readBoolean("velocity_from_translation", false);
|
||||||
|
|
||||||
if (audioKey != null) {
|
if (audioKey != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -36,9 +36,9 @@ import com.jme3.math.Vector3f;
|
|||||||
|
|
||||||
public class Listener {
|
public class Listener {
|
||||||
|
|
||||||
private Vector3f location;
|
private final Vector3f location;
|
||||||
private Vector3f velocity;
|
private final Vector3f velocity;
|
||||||
private Quaternion rotation;
|
private final Quaternion rotation;
|
||||||
private float volume = 1;
|
private float volume = 1;
|
||||||
private AudioRenderer renderer;
|
private AudioRenderer renderer;
|
||||||
|
|
||||||
|
@ -904,11 +904,12 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
|
|||||||
} else {
|
} else {
|
||||||
// Buffer finished playing.
|
// Buffer finished playing.
|
||||||
if (src.isLooping()) {
|
if (src.isLooping()) {
|
||||||
throw new AssertionError("Unexpected state: " +
|
// When a device is disconnected, all sources
|
||||||
"A looping sound has stopped playing");
|
// will enter the "stopped" state.
|
||||||
} else {
|
logger.warning("A looping sound has stopped playing");
|
||||||
reclaimChannel = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reclaimChannel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reclaimChannel) {
|
if (reclaimChannel) {
|
||||||
|
@ -121,7 +121,8 @@ public class MotionPath implements Savable {
|
|||||||
Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
|
Material m = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
|
||||||
for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
|
for (Iterator<Vector3f> it = spline.getControlPoints().iterator(); it.hasNext();) {
|
||||||
Vector3f cp = it.next();
|
Vector3f cp = it.next();
|
||||||
Geometry geo = new Geometry("box", new Box(cp, 0.3f, 0.3f, 0.3f));
|
Geometry geo = new Geometry("box", new Box(0.3f, 0.3f, 0.3f));
|
||||||
|
geo.setLocalTranslation(cp);
|
||||||
geo.setMaterial(m);
|
geo.setMaterial(m);
|
||||||
debugNode.attachChild(geo);
|
debugNode.attachChild(geo);
|
||||||
|
|
||||||
|
@ -50,8 +50,9 @@ public abstract class ParticleMesh extends Mesh {
|
|||||||
public enum Type {
|
public enum Type {
|
||||||
/**
|
/**
|
||||||
* The particle mesh is composed of points. Each particle is a point.
|
* The particle mesh is composed of points. Each particle is a point.
|
||||||
* This can be used in conjuction with {@link RenderState#setPointSprite(boolean) point sprites}
|
* Note that point based particles do not support certain features such
|
||||||
* to render particles the usual way.
|
* as {@link ParticleEmitter#setRotateSpeed(float) rotation}, and
|
||||||
|
* {@link ParticleEmitter#setFacingVelocity(boolean) velocity following}.
|
||||||
*/
|
*/
|
||||||
Point,
|
Point,
|
||||||
|
|
||||||
|
@ -54,10 +54,10 @@ import java.util.logging.Logger;
|
|||||||
*/
|
*/
|
||||||
public class SavableClassUtil {
|
public class SavableClassUtil {
|
||||||
|
|
||||||
private final static HashMap<String, String> classRemappings = new HashMap<String, String>();
|
private final static HashMap<String, String> CLASS_REMAPPINGS = new HashMap<>();
|
||||||
|
|
||||||
private static void addRemapping(String oldClass, Class<? extends Savable> newClass){
|
private static void addRemapping(String oldClass, Class<? extends Savable> newClass){
|
||||||
classRemappings.put(oldClass, newClass.getName());
|
CLASS_REMAPPINGS.put(oldClass, newClass.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -74,7 +74,7 @@ public class SavableClassUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String remapClass(String className) throws ClassNotFoundException {
|
private static String remapClass(String className) throws ClassNotFoundException {
|
||||||
String result = classRemappings.get(className);
|
String result = CLASS_REMAPPINGS.get(className);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return className;
|
return className;
|
||||||
} else {
|
} else {
|
||||||
|
@ -34,7 +34,6 @@ package com.jme3.material;
|
|||||||
import com.jme3.asset.TextureKey;
|
import com.jme3.asset.TextureKey;
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
import com.jme3.math.*;
|
import com.jme3.math.*;
|
||||||
import com.jme3.renderer.Renderer;
|
|
||||||
import com.jme3.shader.VarType;
|
import com.jme3.shader.VarType;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.texture.Texture.WrapMode;
|
import com.jme3.texture.Texture.WrapMode;
|
||||||
@ -129,9 +128,6 @@ public class MatParam implements Savable, Cloneable {
|
|||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply(Renderer r, Technique technique) {
|
|
||||||
technique.updateUniformParam(getPrefixedName(), getVarType(), getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the material parameter value as it would appear in a J3M
|
* Returns the material parameter value as it would appear in a J3M
|
||||||
|
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.JmeExporter;
|
||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.renderer.Renderer;
|
|
||||||
import com.jme3.shader.VarType;
|
import com.jme3.shader.VarType;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.texture.image.ColorSpace;
|
import com.jme3.texture.image.ColorSpace;
|
||||||
@ -44,13 +43,11 @@ import java.io.IOException;
|
|||||||
public class MatParamTexture extends MatParam {
|
public class MatParamTexture extends MatParam {
|
||||||
|
|
||||||
private Texture texture;
|
private Texture texture;
|
||||||
private int unit;
|
|
||||||
private ColorSpace colorSpace;
|
private ColorSpace colorSpace;
|
||||||
|
|
||||||
public MatParamTexture(VarType type, String name, Texture texture, int unit, ColorSpace colorSpace) {
|
public MatParamTexture(VarType type, String name, Texture texture, ColorSpace colorSpace) {
|
||||||
super(type, name, texture);
|
super(type, name, texture);
|
||||||
this.texture = texture;
|
this.texture = texture;
|
||||||
this.unit = unit;
|
|
||||||
this.colorSpace = colorSpace;
|
this.colorSpace = colorSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,37 +89,18 @@ public class MatParamTexture extends MatParam {
|
|||||||
this.colorSpace = colorSpace;
|
this.colorSpace = colorSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUnit(int unit) {
|
|
||||||
this.unit = unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getUnit() {
|
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply(Renderer r, Technique technique) {
|
|
||||||
TechniqueDef techDef = technique.getDef();
|
|
||||||
r.setTexture(getUnit(), getTextureValue());
|
|
||||||
technique.updateUniformParam(getPrefixedName(), getVarType(), getUnit());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
oc.write(unit, "texture_unit", -1);
|
oc.write(0, "texture_unit", -1);
|
||||||
|
oc.write(texture, "texture", null); // For backwards compatibility
|
||||||
// For backwards compat
|
|
||||||
oc.write(texture, "texture", null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
super.read(im);
|
super.read(im);
|
||||||
InputCapsule ic = im.getCapsule(this);
|
InputCapsule ic = im.getCapsule(this);
|
||||||
unit = ic.readInt("texture_unit", -1);
|
|
||||||
texture = (Texture) value;
|
texture = (Texture) value;
|
||||||
//texture = (Texture) ic.readSavable("texture", null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -44,18 +44,16 @@ import com.jme3.math.*;
|
|||||||
import com.jme3.renderer.Caps;
|
import com.jme3.renderer.Caps;
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
import com.jme3.renderer.Renderer;
|
import com.jme3.renderer.Renderer;
|
||||||
import com.jme3.renderer.RendererException;
|
|
||||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
import com.jme3.scene.Mesh;
|
|
||||||
import com.jme3.scene.instancing.InstancedGeometry;
|
|
||||||
import com.jme3.shader.Shader;
|
import com.jme3.shader.Shader;
|
||||||
import com.jme3.shader.Uniform;
|
import com.jme3.shader.Uniform;
|
||||||
|
import com.jme3.shader.UniformBindingManager;
|
||||||
import com.jme3.shader.VarType;
|
import com.jme3.shader.VarType;
|
||||||
|
import com.jme3.texture.Image;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.texture.image.ColorSpace;
|
import com.jme3.texture.image.ColorSpace;
|
||||||
import com.jme3.util.ListMap;
|
import com.jme3.util.ListMap;
|
||||||
import com.jme3.util.TempVars;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -77,32 +75,18 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
// Version #2: Fixed issue with RenderState.apply*** flags not getting exported
|
// Version #2: Fixed issue with RenderState.apply*** flags not getting exported
|
||||||
public static final int SAVABLE_VERSION = 2;
|
public static final int SAVABLE_VERSION = 2;
|
||||||
private static final Logger logger = Logger.getLogger(Material.class.getName());
|
private static final Logger logger = Logger.getLogger(Material.class.getName());
|
||||||
private static final RenderState additiveLight = new RenderState();
|
|
||||||
private static final RenderState depthOnly = new RenderState();
|
|
||||||
private static final Quaternion nullDirLight = new Quaternion(0, -1, 0, -1);
|
|
||||||
|
|
||||||
static {
|
|
||||||
depthOnly.setDepthTest(true);
|
|
||||||
depthOnly.setDepthWrite(true);
|
|
||||||
depthOnly.setFaceCullMode(RenderState.FaceCullMode.Back);
|
|
||||||
depthOnly.setColorWrite(false);
|
|
||||||
|
|
||||||
additiveLight.setBlendMode(RenderState.BlendMode.AlphaAdditive);
|
|
||||||
additiveLight.setDepthWrite(false);
|
|
||||||
}
|
|
||||||
private AssetKey key;
|
private AssetKey key;
|
||||||
private String name;
|
private String name;
|
||||||
private MaterialDef def;
|
private MaterialDef def;
|
||||||
private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
|
private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
|
||||||
private Technique technique;
|
private Technique technique;
|
||||||
private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
|
private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
|
||||||
private int nextTexUnit = 0;
|
|
||||||
private RenderState additionalState = null;
|
private RenderState additionalState = null;
|
||||||
private RenderState mergedRenderState = new RenderState();
|
private RenderState mergedRenderState = new RenderState();
|
||||||
private boolean transparent = false;
|
private boolean transparent = false;
|
||||||
private boolean receivesShadows = false;
|
private boolean receivesShadows = false;
|
||||||
private int sortingId = -1;
|
private int sortingId = -1;
|
||||||
private transient ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
|
|
||||||
|
|
||||||
public Material(MaterialDef def) {
|
public Material(MaterialDef def) {
|
||||||
if (def == null) {
|
if (def == null) {
|
||||||
@ -175,22 +159,29 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
* @return The sorting ID used for sorting geometries for rendering.
|
* @return The sorting ID used for sorting geometries for rendering.
|
||||||
*/
|
*/
|
||||||
public int getSortId() {
|
public int getSortId() {
|
||||||
Technique t = getActiveTechnique();
|
if (sortingId == -1 && technique != null) {
|
||||||
if (sortingId == -1 && t != null && t.getShader() != null) {
|
sortingId = technique.getSortId() << 16;
|
||||||
int texId = -1;
|
int texturesSortId = 17;
|
||||||
for (int i = 0; i < paramValues.size(); i++) {
|
for (int i = 0; i < paramValues.size(); i++) {
|
||||||
MatParam param = paramValues.getValue(i);
|
MatParam param = paramValues.getValue(i);
|
||||||
if (param instanceof MatParamTexture) {
|
if (!param.getVarType().isTextureType()) {
|
||||||
MatParamTexture tex = (MatParamTexture) param;
|
continue;
|
||||||
if (tex.getTextureValue() != null && tex.getTextureValue().getImage() != null) {
|
|
||||||
if (texId == -1) {
|
|
||||||
texId = 0;
|
|
||||||
}
|
}
|
||||||
texId += tex.getTextureValue().getImage().getId() % 0xff;
|
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;
|
||||||
}
|
}
|
||||||
sortingId = texId + t.getShader().getId() * 1000;
|
texturesSortId = texturesSortId * 23 + textureId;
|
||||||
|
}
|
||||||
|
sortingId |= texturesSortId & 0xFFFF;
|
||||||
}
|
}
|
||||||
return sortingId;
|
return sortingId;
|
||||||
}
|
}
|
||||||
@ -215,6 +206,8 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
mat.paramValues.put(entry.getKey(), entry.getValue().clone());
|
mat.paramValues.put(entry.getKey(), entry.getValue().clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mat.sortingId = -1;
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
} catch (CloneNotSupportedException ex) {
|
} catch (CloneNotSupportedException ex) {
|
||||||
throw new AssertionError(ex);
|
throw new AssertionError(ex);
|
||||||
@ -258,8 +251,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
// E.g. if user chose custom technique for one material but
|
// E.g. if user chose custom technique for one material but
|
||||||
// uses default technique for other material, the materials
|
// uses default technique for other material, the materials
|
||||||
// are not equal.
|
// are not equal.
|
||||||
String thisDefName = this.technique != null ? this.technique.getDef().getName() : "Default";
|
String thisDefName = this.technique != null
|
||||||
String otherDefName = other.technique != null ? other.technique.getDef().getName() : "Default";
|
? this.technique.getDef().getName()
|
||||||
|
: TechniqueDef.DEFAULT_TECHNIQUE_NAME;
|
||||||
|
|
||||||
|
String otherDefName = other.technique != null
|
||||||
|
? other.technique.getDef().getName()
|
||||||
|
: TechniqueDef.DEFAULT_TECHNIQUE_NAME;
|
||||||
|
|
||||||
if (!thisDefName.equals(otherDefName)) {
|
if (!thisDefName.equals(otherDefName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -444,7 +443,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
*
|
*
|
||||||
* @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
|
* @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
|
||||||
*/
|
*/
|
||||||
public ListMap getParamsMap() {
|
public ListMap<String, MatParam> getParamsMap() {
|
||||||
return paramValues;
|
return paramValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,16 +503,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
|
|
||||||
paramValues.remove(name);
|
paramValues.remove(name);
|
||||||
if (matParam instanceof MatParamTexture) {
|
if (matParam instanceof MatParamTexture) {
|
||||||
int texUnit = ((MatParamTexture) matParam).getUnit();
|
|
||||||
nextTexUnit--;
|
|
||||||
for (MatParam param : paramValues.values()) {
|
|
||||||
if (param instanceof MatParamTexture) {
|
|
||||||
MatParamTexture texParam = (MatParamTexture) param;
|
|
||||||
if (texParam.getUnit() > texUnit) {
|
|
||||||
texParam.setUnit(texParam.getUnit() - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sortingId = -1;
|
sortingId = -1;
|
||||||
}
|
}
|
||||||
if (technique != null) {
|
if (technique != null) {
|
||||||
@ -556,13 +545,13 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
+ "Linear using texture.getImage.setColorSpace().",
|
+ "Linear using texture.getImage.setColorSpace().",
|
||||||
new Object[]{value.getName(), value.getImage().getColorSpace().name(), name});
|
new Object[]{value.getName(), value.getImage().getColorSpace().name(), name});
|
||||||
}
|
}
|
||||||
paramValues.put(name, new MatParamTexture(type, name, value, nextTexUnit++, null));
|
paramValues.put(name, new MatParamTexture(type, name, value, null));
|
||||||
} else {
|
} else {
|
||||||
val.setTextureValue(value);
|
val.setTextureValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (technique != null) {
|
if (technique != null) {
|
||||||
technique.notifyParamChanged(name, type, nextTexUnit - 1);
|
technique.notifyParamChanged(name, type, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to recompute sort ID
|
// need to recompute sort ID
|
||||||
@ -695,277 +684,21 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
setParam(name, VarType.Vector4, value);
|
setParam(name, VarType.Vector4, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ColorRGBA getAmbientColor(LightList lightList, boolean removeLights) {
|
|
||||||
ambientLightColor.set(0, 0, 0, 1);
|
|
||||||
for (int j = 0; j < lightList.size(); j++) {
|
|
||||||
Light l = lightList.get(j);
|
|
||||||
if (l instanceof AmbientLight) {
|
|
||||||
ambientLightColor.addLocal(l.getColor());
|
|
||||||
if(removeLights){
|
|
||||||
lightList.remove(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ambientLightColor.a = 1.0f;
|
|
||||||
return ambientLightColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void renderMeshFromGeometry(Renderer renderer, Geometry geom) {
|
|
||||||
Mesh mesh = geom.getMesh();
|
|
||||||
int lodLevel = geom.getLodLevel();
|
|
||||||
if (geom instanceof InstancedGeometry) {
|
|
||||||
InstancedGeometry instGeom = (InstancedGeometry) geom;
|
|
||||||
int numInstances = instGeom.getActualNumInstances();
|
|
||||||
if (numInstances == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (renderer.getCaps().contains(Caps.MeshInstancing)) {
|
|
||||||
renderer.renderMesh(mesh, lodLevel, numInstances, instGeom.getAllInstanceData());
|
|
||||||
} else {
|
|
||||||
throw new RendererException("Mesh instancing is not supported by the video hardware");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
renderer.renderMesh(mesh, lodLevel, 1, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads the lights in the light list as two uniform arrays.<br/><br/> *
|
|
||||||
* <p>
|
|
||||||
* <code>uniform vec4 g_LightColor[numLights];</code><br/> //
|
|
||||||
* g_LightColor.rgb is the diffuse/specular color of the light.<br/> //
|
|
||||||
* g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/> //
|
|
||||||
* 2 = Spot. <br/> <br/>
|
|
||||||
* <code>uniform vec4 g_LightPosition[numLights];</code><br/> //
|
|
||||||
* g_LightPosition.xyz is the position of the light (for point lights)<br/>
|
|
||||||
* // or the direction of the light (for directional lights).<br/> //
|
|
||||||
* g_LightPosition.w is the inverse radius (1/r) of the light (for
|
|
||||||
* attenuation) <br/> </p>
|
|
||||||
*/
|
|
||||||
protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex) {
|
|
||||||
if (numLights == 0) { // this shader does not do lighting, ignore.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uniform lightData = shader.getUniform("g_LightData");
|
|
||||||
lightData.setVector4Length(numLights * 3);//8 lights * max 3
|
|
||||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
|
||||||
|
|
||||||
|
|
||||||
if (startIndex != 0) {
|
|
||||||
// apply additive blending for 2nd and future passes
|
|
||||||
rm.getRenderer().applyRenderState(additiveLight);
|
|
||||||
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
|
||||||
}else{
|
|
||||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList,true));
|
|
||||||
}
|
|
||||||
|
|
||||||
int lightDataIndex = 0;
|
|
||||||
TempVars vars = TempVars.get();
|
|
||||||
Vector4f tmpVec = vars.vect4f1;
|
|
||||||
int curIndex;
|
|
||||||
int endIndex = numLights + startIndex;
|
|
||||||
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
|
|
||||||
|
|
||||||
|
|
||||||
Light l = lightList.get(curIndex);
|
|
||||||
if(l.getType() == Light.Type.Ambient){
|
|
||||||
endIndex++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ColorRGBA color = l.getColor();
|
|
||||||
//Color
|
|
||||||
lightData.setVector4InArray(color.getRed(),
|
|
||||||
color.getGreen(),
|
|
||||||
color.getBlue(),
|
|
||||||
l.getType().getId(),
|
|
||||||
lightDataIndex);
|
|
||||||
lightDataIndex++;
|
|
||||||
|
|
||||||
switch (l.getType()) {
|
|
||||||
case Directional:
|
|
||||||
DirectionalLight dl = (DirectionalLight) l;
|
|
||||||
Vector3f dir = dl.getDirection();
|
|
||||||
//Data directly sent in view space to avoid a matrix mult for each pixel
|
|
||||||
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
|
|
||||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
|
||||||
// tmpVec.divideLocal(tmpVec.w);
|
|
||||||
// tmpVec.normalizeLocal();
|
|
||||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
|
|
||||||
lightDataIndex++;
|
|
||||||
//PADDING
|
|
||||||
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
|
|
||||||
lightDataIndex++;
|
|
||||||
break;
|
|
||||||
case Point:
|
|
||||||
PointLight pl = (PointLight) l;
|
|
||||||
Vector3f pos = pl.getPosition();
|
|
||||||
float invRadius = pl.getInvRadius();
|
|
||||||
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
|
|
||||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
|
||||||
//tmpVec.divideLocal(tmpVec.w);
|
|
||||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
|
|
||||||
lightDataIndex++;
|
|
||||||
//PADDING
|
|
||||||
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
|
|
||||||
lightDataIndex++;
|
|
||||||
break;
|
|
||||||
case Spot:
|
|
||||||
SpotLight sl = (SpotLight) l;
|
|
||||||
Vector3f pos2 = sl.getPosition();
|
|
||||||
Vector3f dir2 = sl.getDirection();
|
|
||||||
float invRange = sl.getInvSpotRange();
|
|
||||||
float spotAngleCos = sl.getPackedAngleCos();
|
|
||||||
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f);
|
|
||||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
|
||||||
// tmpVec.divideLocal(tmpVec.w);
|
|
||||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
|
|
||||||
lightDataIndex++;
|
|
||||||
|
|
||||||
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
|
|
||||||
//one vec4 less and a vec4 that becomes a vec3
|
|
||||||
//the downside is that spotAngleCos decoding happens now in the frag shader.
|
|
||||||
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f);
|
|
||||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
|
||||||
tmpVec.normalizeLocal();
|
|
||||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
|
|
||||||
lightDataIndex++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vars.release();
|
|
||||||
//Padding of unsued buffer space
|
|
||||||
while(lightDataIndex < numLights * 3) {
|
|
||||||
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
|
|
||||||
lightDataIndex++;
|
|
||||||
}
|
|
||||||
return curIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void renderMultipassLighting(Shader shader, Geometry g, LightList lightList, RenderManager rm) {
|
|
||||||
|
|
||||||
Renderer r = rm.getRenderer();
|
|
||||||
Uniform lightDir = shader.getUniform("g_LightDirection");
|
|
||||||
Uniform lightColor = shader.getUniform("g_LightColor");
|
|
||||||
Uniform lightPos = shader.getUniform("g_LightPosition");
|
|
||||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
|
||||||
boolean isFirstLight = true;
|
|
||||||
boolean isSecondLight = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < lightList.size(); i++) {
|
|
||||||
Light l = lightList.get(i);
|
|
||||||
if (l instanceof AmbientLight) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFirstLight) {
|
|
||||||
// set ambient color for first light only
|
|
||||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, false));
|
|
||||||
isFirstLight = false;
|
|
||||||
isSecondLight = true;
|
|
||||||
} else if (isSecondLight) {
|
|
||||||
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
|
||||||
// apply additive blending for 2nd and future lights
|
|
||||||
r.applyRenderState(additiveLight);
|
|
||||||
isSecondLight = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TempVars vars = TempVars.get();
|
|
||||||
Quaternion tmpLightDirection = vars.quat1;
|
|
||||||
Quaternion tmpLightPosition = vars.quat2;
|
|
||||||
ColorRGBA tmpLightColor = vars.color;
|
|
||||||
Vector4f tmpVec = vars.vect4f1;
|
|
||||||
|
|
||||||
ColorRGBA color = l.getColor();
|
|
||||||
tmpLightColor.set(color);
|
|
||||||
tmpLightColor.a = l.getType().getId();
|
|
||||||
lightColor.setValue(VarType.Vector4, tmpLightColor);
|
|
||||||
|
|
||||||
switch (l.getType()) {
|
|
||||||
case Directional:
|
|
||||||
DirectionalLight dl = (DirectionalLight) l;
|
|
||||||
Vector3f dir = dl.getDirection();
|
|
||||||
//FIXME : there is an inconstency here due to backward
|
|
||||||
//compatibility of the lighting shader.
|
|
||||||
//The directional light direction is passed in the
|
|
||||||
//LightPosition uniform. The lighting shader needs to be
|
|
||||||
//reworked though in order to fix this.
|
|
||||||
tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
|
|
||||||
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
|
||||||
tmpLightDirection.set(0, 0, 0, 0);
|
|
||||||
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
|
||||||
break;
|
|
||||||
case Point:
|
|
||||||
PointLight pl = (PointLight) l;
|
|
||||||
Vector3f pos = pl.getPosition();
|
|
||||||
float invRadius = pl.getInvRadius();
|
|
||||||
|
|
||||||
tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
|
|
||||||
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
|
||||||
tmpLightDirection.set(0, 0, 0, 0);
|
|
||||||
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
|
||||||
break;
|
|
||||||
case Spot:
|
|
||||||
SpotLight sl = (SpotLight) l;
|
|
||||||
Vector3f pos2 = sl.getPosition();
|
|
||||||
Vector3f dir2 = sl.getDirection();
|
|
||||||
float invRange = sl.getInvSpotRange();
|
|
||||||
float spotAngleCos = sl.getPackedAngleCos();
|
|
||||||
|
|
||||||
tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
|
|
||||||
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
|
||||||
|
|
||||||
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
|
|
||||||
//one vec4 less and a vec4 that becomes a vec3
|
|
||||||
//the downside is that spotAngleCos decoding happens now in the frag shader.
|
|
||||||
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
|
|
||||||
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
|
||||||
tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
|
|
||||||
|
|
||||||
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
|
|
||||||
}
|
|
||||||
vars.release();
|
|
||||||
r.setShader(shader);
|
|
||||||
renderMeshFromGeometry(r, g);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFirstLight) {
|
|
||||||
// Either there are no lights at all, or only ambient lights.
|
|
||||||
// Render a dummy "normal light" so we can see the ambient color.
|
|
||||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, false));
|
|
||||||
lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
|
|
||||||
lightPos.setValue(VarType.Vector4, nullDirLight);
|
|
||||||
r.setShader(shader);
|
|
||||||
renderMeshFromGeometry(r, g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select the technique to use for rendering this material.
|
* Select the technique to use for rendering this material.
|
||||||
* <p>
|
* <p>
|
||||||
* If <code>name</code> is "Default", then one of the
|
|
||||||
* {@link MaterialDef#getDefaultTechniques() default techniques}
|
|
||||||
* on the material will be selected. Otherwise, the named technique
|
|
||||||
* will be found in the material definition.
|
|
||||||
* <p>
|
|
||||||
* Any candidate technique for selection (either default or named)
|
* Any candidate technique for selection (either default or named)
|
||||||
* must be verified to be compatible with the system, for that, the
|
* must be verified to be compatible with the system, for that, the
|
||||||
* <code>renderManager</code> is queried for capabilities.
|
* <code>renderManager</code> is queried for capabilities.
|
||||||
*
|
*
|
||||||
* @param name The name of the technique to select, pass "Default" to
|
* @param name The name of the technique to select, pass
|
||||||
* select one of the default techniques.
|
* {@link TechniqueDef#DEFAULT_TECHNIQUE_NAME} to select one of the default
|
||||||
|
* techniques.
|
||||||
* @param renderManager The {@link RenderManager render manager}
|
* @param renderManager The {@link RenderManager render manager}
|
||||||
* to query for capabilities.
|
* to query for capabilities.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException If "Default" is passed and no default
|
* @throws IllegalArgumentException If no technique exists with the given
|
||||||
* techniques are available on the material definition, or if a name
|
* name.
|
||||||
* is passed but there's no technique by that name.
|
|
||||||
* @throws UnsupportedOperationException If no candidate technique supports
|
* @throws UnsupportedOperationException If no candidate technique supports
|
||||||
* the system capabilities.
|
* the system capabilities.
|
||||||
*/
|
*/
|
||||||
@ -974,13 +707,13 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
Technique tech = techniques.get(name);
|
Technique tech = techniques.get(name);
|
||||||
// When choosing technique, we choose one that
|
// When choosing technique, we choose one that
|
||||||
// supports all the caps.
|
// supports all the caps.
|
||||||
EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
|
|
||||||
if (tech == null) {
|
if (tech == null) {
|
||||||
|
EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
|
||||||
|
List<TechniqueDef> techDefs = def.getTechniqueDefs(name);
|
||||||
|
|
||||||
if (name.equals("Default")) {
|
|
||||||
List<TechniqueDef> techDefs = def.getDefaultTechniques();
|
|
||||||
if (techDefs == null || techDefs.isEmpty()) {
|
if (techDefs == null || techDefs.isEmpty()) {
|
||||||
throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'");
|
throw new IllegalArgumentException(
|
||||||
|
String.format("The requested technique %s is not available on material %s", name, def.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TechniqueDef lastTech = null;
|
TechniqueDef lastTech = null;
|
||||||
@ -989,34 +722,19 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
// use the first one that supports all the caps
|
// use the first one that supports all the caps
|
||||||
tech = new Technique(this, techDef);
|
tech = new Technique(this, techDef);
|
||||||
techniques.put(name, tech);
|
techniques.put(name, tech);
|
||||||
if(tech.getDef().getLightMode() == renderManager.getPreferredLightMode() ||
|
if (tech.getDef().getLightMode() == renderManager.getPreferredLightMode()
|
||||||
tech.getDef().getLightMode() == LightMode.Disable){
|
|| tech.getDef().getLightMode() == LightMode.Disable) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastTech = techDef;
|
lastTech = techDef;
|
||||||
}
|
}
|
||||||
if (tech == null) {
|
if (tech == null) {
|
||||||
throw new UnsupportedOperationException("No default technique on material '" + def.getName() + "'\n"
|
throw new UnsupportedOperationException(
|
||||||
+ " is supported by the video hardware. The caps "
|
String.format("No technique '%s' on material "
|
||||||
+ lastTech.getRequiredCaps() + " are required.");
|
+ "'%s' is supported by the video hardware. "
|
||||||
}
|
+ "The capabilities %s are required.",
|
||||||
|
name, def.getName(), lastTech.getRequiredCaps()));
|
||||||
} 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);
|
|
||||||
}
|
}
|
||||||
} else if (technique == tech) {
|
} else if (technique == tech) {
|
||||||
// attempting to switch to an already
|
// attempting to switch to an already
|
||||||
@ -1025,17 +743,79 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
technique = tech;
|
technique = tech;
|
||||||
tech.makeCurrent(def.getAssetManager(), true, rendererCaps, renderManager);
|
tech.notifyTechniqueSwitched();
|
||||||
|
|
||||||
// shader was changed
|
// shader was changed
|
||||||
sortingId = -1;
|
sortingId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void autoSelectTechnique(RenderManager rm) {
|
private int applyOverrides(Renderer renderer, Shader shader, List<MatParamOverride> overrides, int unit) {
|
||||||
if (technique == null) {
|
for (MatParamOverride override : overrides) {
|
||||||
selectTechnique("Default", rm);
|
VarType type = override.getVarType();
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
technique.makeCurrent(def.getAssetManager(), false, rm.getRenderer().getCaps(), rm);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1046,20 +826,23 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
* used for rendering, there won't be any delay since the material has
|
* used for rendering, there won't be any delay since the material has
|
||||||
* been already been setup for rendering.
|
* been already been setup for rendering.
|
||||||
*
|
*
|
||||||
* @param rm The render manager to preload for
|
* @param renderManager The render manager to preload for
|
||||||
*/
|
*/
|
||||||
public void preload(RenderManager rm) {
|
public void preload(RenderManager renderManager) {
|
||||||
autoSelectTechnique(rm);
|
if (technique == null) {
|
||||||
|
selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
|
||||||
|
}
|
||||||
|
TechniqueDef techniqueDef = technique.getDef();
|
||||||
|
Renderer renderer = renderManager.getRenderer();
|
||||||
|
EnumSet<Caps> rendererCaps = renderer.getCaps();
|
||||||
|
|
||||||
Renderer r = rm.getRenderer();
|
if (techniqueDef.isNoRender()) {
|
||||||
TechniqueDef techDef = technique.getDef();
|
return;
|
||||||
|
|
||||||
Collection<MatParam> params = paramValues.values();
|
|
||||||
for (MatParam param : params) {
|
|
||||||
param.apply(r, technique);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r.setShader(technique.getShader());
|
Shader shader = technique.makeCurrent(renderManager, null, null, null, rendererCaps);
|
||||||
|
updateShaderMaterialParameters(renderer, shader, null, null);
|
||||||
|
renderManager.getRenderer().setShader(shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearUniformsSetByCurrent(Shader shader) {
|
private void clearUniformsSetByCurrent(Shader shader) {
|
||||||
@ -1141,80 +924,46 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param geom The geometry to render
|
* @param geometry The geometry to render
|
||||||
* @param lights Presorted and filtered light list to use for rendering
|
* @param lights Presorted and filtered light list to use for rendering
|
||||||
* @param rm The render manager requesting the rendering
|
* @param renderManager The render manager requesting the rendering
|
||||||
*/
|
*/
|
||||||
public void render(Geometry geom, LightList lights, RenderManager rm) {
|
public void render(Geometry geometry, LightList lights, RenderManager renderManager) {
|
||||||
autoSelectTechnique(rm);
|
if (technique == null) {
|
||||||
TechniqueDef techDef = technique.getDef();
|
selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
|
||||||
|
|
||||||
if (techDef.isNoRender()) return;
|
|
||||||
|
|
||||||
Renderer r = rm.getRenderer();
|
|
||||||
|
|
||||||
if (rm.getForcedRenderState() != null) {
|
|
||||||
r.applyRenderState(rm.getForcedRenderState());
|
|
||||||
} else {
|
|
||||||
if (techDef.getRenderState() != null) {
|
|
||||||
r.applyRenderState(techDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
|
|
||||||
} else {
|
|
||||||
r.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TechniqueDef techniqueDef = technique.getDef();
|
||||||
|
Renderer renderer = renderManager.getRenderer();
|
||||||
|
EnumSet<Caps> rendererCaps = renderer.getCaps();
|
||||||
|
|
||||||
// update camera and world matrices
|
if (techniqueDef.isNoRender()) {
|
||||||
// NOTE: setWorldTransform should have been called already
|
|
||||||
|
|
||||||
// reset unchanged uniform flag
|
|
||||||
clearUniformsSetByCurrent(technique.getShader());
|
|
||||||
rm.updateUniformBindings(technique.getWorldBindUniforms());
|
|
||||||
|
|
||||||
|
|
||||||
// setup textures and uniforms
|
|
||||||
for (int i = 0; i < paramValues.size(); i++) {
|
|
||||||
MatParam param = paramValues.getValue(i);
|
|
||||||
param.apply(r, technique);
|
|
||||||
}
|
|
||||||
|
|
||||||
Shader shader = technique.getShader();
|
|
||||||
|
|
||||||
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload and bind shader
|
// Apply render state
|
||||||
// any unset uniforms will be set to 0
|
updateRenderState(renderManager, renderer, techniqueDef);
|
||||||
resetUniformsNotSetByCurrent(shader);
|
|
||||||
r.setShader(shader);
|
|
||||||
|
|
||||||
renderMeshFromGeometry(r, geom);
|
// Get world overrides
|
||||||
|
List<MatParamOverride> overrides = geometry.getWorldMatParamOverrides();
|
||||||
|
|
||||||
|
// Select shader to use
|
||||||
|
Shader shader = technique.makeCurrent(renderManager, overrides, renderManager.getForcedMatParams(), lights, rendererCaps);
|
||||||
|
|
||||||
|
// Begin tracking which uniforms were changed by material.
|
||||||
|
clearUniformsSetByCurrent(shader);
|
||||||
|
|
||||||
|
// Set uniform bindings
|
||||||
|
renderManager.updateUniformBindings(shader);
|
||||||
|
|
||||||
|
// Set material parameters
|
||||||
|
updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams());
|
||||||
|
|
||||||
|
// Clear any uniforms not changed by material.
|
||||||
|
resetUniformsNotSetByCurrent(shader);
|
||||||
|
|
||||||
|
// Delegate rendering to the technique
|
||||||
|
technique.render(renderManager, shader, geometry, lights);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1240,6 +989,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
oc.writeStringSavableMap(paramValues, "parameters", null);
|
oc.writeStringSavableMap(paramValues, "parameters", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Material[name=" + name +
|
||||||
|
", def=" + def.getName() +
|
||||||
|
", tech=" + technique.getDef().getName() +
|
||||||
|
"]";
|
||||||
|
}
|
||||||
|
|
||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
InputCapsule ic = im.getCapsule(this);
|
InputCapsule ic = im.getCapsule(this);
|
||||||
|
|
||||||
@ -1296,11 +1053,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
MatParam param = entry.getValue();
|
MatParam param = entry.getValue();
|
||||||
if (param instanceof MatParamTexture) {
|
if (param instanceof MatParamTexture) {
|
||||||
MatParamTexture texVal = (MatParamTexture) param;
|
MatParamTexture texVal = (MatParamTexture) param;
|
||||||
|
|
||||||
if (nextTexUnit < texVal.getUnit() + 1) {
|
|
||||||
nextTexUnit = texVal.getUnit() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the texture failed to load for this param
|
// the texture failed to load for this param
|
||||||
// do not add to param values
|
// do not add to param values
|
||||||
if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) {
|
if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) {
|
||||||
@ -1335,14 +1087,11 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
|
|||||||
// Try to guess values of "apply" render state based on defaults
|
// Try to guess values of "apply" render state based on defaults
|
||||||
// if value != default then set apply to true
|
// if value != default then set apply to true
|
||||||
additionalState.applyPolyOffset = additionalState.offsetEnabled;
|
additionalState.applyPolyOffset = additionalState.offsetEnabled;
|
||||||
additionalState.applyAlphaFallOff = additionalState.alphaTest;
|
|
||||||
additionalState.applyAlphaTest = additionalState.alphaTest;
|
|
||||||
additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off;
|
additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off;
|
||||||
additionalState.applyColorWrite = !additionalState.colorWrite;
|
additionalState.applyColorWrite = !additionalState.colorWrite;
|
||||||
additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back;
|
additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back;
|
||||||
additionalState.applyDepthTest = !additionalState.depthTest;
|
additionalState.applyDepthTest = !additionalState.depthTest;
|
||||||
additionalState.applyDepthWrite = !additionalState.depthWrite;
|
additionalState.applyDepthWrite = !additionalState.depthWrite;
|
||||||
additionalState.applyPointSprite = additionalState.pointSprite;
|
|
||||||
additionalState.applyStencilTest = additionalState.stencilTest;
|
additionalState.applyStencilTest = additionalState.stencilTest;
|
||||||
additionalState.applyWireFrame = additionalState.wireframe;
|
additionalState.applyWireFrame = additionalState.wireframe;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
package com.jme3.material;
|
package com.jme3.material;
|
||||||
|
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.renderer.Caps;
|
||||||
import com.jme3.shader.VarType;
|
import com.jme3.shader.VarType;
|
||||||
import com.jme3.texture.image.ColorSpace;
|
import com.jme3.texture.image.ColorSpace;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -51,8 +52,7 @@ public class MaterialDef {
|
|||||||
private String assetName;
|
private String assetName;
|
||||||
private AssetManager assetManager;
|
private AssetManager assetManager;
|
||||||
|
|
||||||
private List<TechniqueDef> defaultTechs;
|
private Map<String, List<TechniqueDef>> techniques;
|
||||||
private Map<String, TechniqueDef> techniques;
|
|
||||||
private Map<String, MatParam> matParams;
|
private Map<String, MatParam> matParams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,9 +70,8 @@ public class MaterialDef {
|
|||||||
public MaterialDef(AssetManager assetManager, String name){
|
public MaterialDef(AssetManager assetManager, String name){
|
||||||
this.assetManager = assetManager;
|
this.assetManager = assetManager;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
techniques = new HashMap<String, TechniqueDef>();
|
techniques = new HashMap<String, List<TechniqueDef>>();
|
||||||
matParams = new HashMap<String, MatParam>();
|
matParams = new HashMap<String, MatParam>();
|
||||||
defaultTechs = new ArrayList<TechniqueDef>();
|
|
||||||
logger.log(Level.FINE, "Loaded material definition: {0}", name);
|
logger.log(Level.FINE, "Loaded material definition: {0}", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +134,7 @@ public class MaterialDef {
|
|||||||
* @see ColorSpace
|
* @see ColorSpace
|
||||||
*/
|
*/
|
||||||
public void addMaterialParamTexture(VarType type, String name, ColorSpace colorSpace) {
|
public void addMaterialParamTexture(VarType type, String name, ColorSpace colorSpace) {
|
||||||
matParams.put(name, new MatParamTexture(type, name, null , 0, colorSpace));
|
matParams.put(name, new MatParamTexture(type, name, null, colorSpace));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -164,40 +163,26 @@ public class MaterialDef {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new technique definition to this material definition.
|
* Adds a new technique definition to this material definition.
|
||||||
* <p>
|
|
||||||
* If the technique name is "Default", it will be added
|
|
||||||
* to the list of {@link MaterialDef#getDefaultTechniques() default techniques}.
|
|
||||||
*
|
*
|
||||||
* @param technique The technique definition to add.
|
* @param technique The technique definition to add.
|
||||||
*/
|
*/
|
||||||
public void addTechniqueDef(TechniqueDef technique) {
|
public void addTechniqueDef(TechniqueDef technique) {
|
||||||
if (technique.getName().equals("Default")) {
|
List<TechniqueDef> list = techniques.get(technique.getName());
|
||||||
defaultTechs.add(technique);
|
if (list == null) {
|
||||||
} else {
|
list = new ArrayList<>();
|
||||||
techniques.put(technique.getName(), technique);
|
techniques.put(technique.getName(), list);
|
||||||
}
|
}
|
||||||
|
list.add(technique);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all default techniques.
|
* Returns technique definitions with the given name.
|
||||||
*
|
*
|
||||||
* @return a list of all default techniques.
|
* @param name The name of the technique definitions to find
|
||||||
|
*
|
||||||
|
* @return The technique definitions, or null if cannot be found.
|
||||||
*/
|
*/
|
||||||
public List<TechniqueDef> getDefaultTechniques(){
|
public List<TechniqueDef> getTechniqueDefs(String name) {
|
||||||
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) {
|
|
||||||
return techniques.get(name);
|
return techniques.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,12 +75,11 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>TestFunction</code> specifies the testing function for stencil test
|
* <code>TestFunction</code> specifies the testing function for stencil test
|
||||||
* function and alpha test function.
|
* function.
|
||||||
*
|
*
|
||||||
* <p>The functions work similarly as described except that for stencil
|
* <p>
|
||||||
* test function, the reference value given in the stencil command is
|
* The reference value given in the stencil command is the input value while
|
||||||
* the input value while the reference is the value already in the stencil
|
* the reference is the value already in the stencil buffer.
|
||||||
* buffer.
|
|
||||||
*/
|
*/
|
||||||
public enum TestFunction {
|
public enum TestFunction {
|
||||||
|
|
||||||
@ -118,7 +117,94 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
/**
|
/**
|
||||||
* The test always passes
|
* The test always passes
|
||||||
*/
|
*/
|
||||||
Always,}
|
Always
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>BlendEquation</code> specifies the blending equation to combine
|
||||||
|
* pixels.
|
||||||
|
*/
|
||||||
|
public enum BlendEquation {
|
||||||
|
/**
|
||||||
|
* Sets the blend equation so that the source and destination data are
|
||||||
|
* added. (Default) Clamps to [0,1] Useful for things like antialiasing
|
||||||
|
* and transparency.
|
||||||
|
*/
|
||||||
|
Add,
|
||||||
|
/**
|
||||||
|
* Sets the blend equation so that the source and destination data are
|
||||||
|
* subtracted (Src - Dest). Clamps to [0,1] Falls back to Add if
|
||||||
|
* supportsSubtract is false.
|
||||||
|
*/
|
||||||
|
Subtract,
|
||||||
|
/**
|
||||||
|
* Same as Subtract, but the order is reversed (Dst - Src). Clamps to
|
||||||
|
* [0,1] Falls back to Add if supportsSubtract is false.
|
||||||
|
*/
|
||||||
|
ReverseSubtract,
|
||||||
|
/**
|
||||||
|
* Sets the blend equation so that each component of the result color is
|
||||||
|
* the minimum of the corresponding components of the source and
|
||||||
|
* destination colors. This and Max are useful for applications that
|
||||||
|
* analyze image data (image thresholding against a constant color, for
|
||||||
|
* example). Falls back to Add if supportsMinMax is false.
|
||||||
|
*/
|
||||||
|
Min,
|
||||||
|
/**
|
||||||
|
* Sets the blend equation so that each component of the result color is
|
||||||
|
* the maximum of the corresponding components of the source and
|
||||||
|
* destination colors. This and Min are useful for applications that
|
||||||
|
* analyze image data (image thresholding against a constant color, for
|
||||||
|
* example). Falls back to Add if supportsMinMax is false.
|
||||||
|
*/
|
||||||
|
Max
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>BlendEquationAlpha</code> specifies the blending equation to
|
||||||
|
* combine pixels for the alpha component.
|
||||||
|
*/
|
||||||
|
public enum BlendEquationAlpha {
|
||||||
|
/**
|
||||||
|
* Sets the blend equation to be the same as the one defined by
|
||||||
|
* {@link #blendEquation}.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
InheritColor,
|
||||||
|
/**
|
||||||
|
* Sets the blend equation so that the source and destination data are
|
||||||
|
* added. (Default) Clamps to [0,1] Useful for things like antialiasing
|
||||||
|
* and transparency.
|
||||||
|
*/
|
||||||
|
Add,
|
||||||
|
/**
|
||||||
|
* Sets the blend equation so that the source and destination data are
|
||||||
|
* subtracted (Src - Dest). Clamps to [0,1] Falls back to Add if
|
||||||
|
* supportsSubtract is false.
|
||||||
|
*/
|
||||||
|
Subtract,
|
||||||
|
/**
|
||||||
|
* Same as Subtract, but the order is reversed (Dst - Src). Clamps to
|
||||||
|
* [0,1] Falls back to Add if supportsSubtract is false.
|
||||||
|
*/
|
||||||
|
ReverseSubtract,
|
||||||
|
/**
|
||||||
|
* Sets the blend equation so that the result alpha is the minimum of
|
||||||
|
* the source alpha and destination alpha. This and Max are useful for
|
||||||
|
* applications that analyze image data (image thresholding against a
|
||||||
|
* constant color, for example). Falls back to Add if supportsMinMax is
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
Min,
|
||||||
|
/**
|
||||||
|
* sSets the blend equation so that the result alpha is the maximum of
|
||||||
|
* the source alpha and destination alpha. This and Min are useful for
|
||||||
|
* applications that analyze image data (image thresholding against a
|
||||||
|
* constant color, for example). Falls back to Add if supportsMinMax is
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
Max
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>BlendMode</code> specifies the blending operation to use.
|
* <code>BlendMode</code> specifies the blending operation to use.
|
||||||
@ -276,19 +362,16 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ADDITIONAL.applyPointSprite = false;
|
|
||||||
ADDITIONAL.applyWireFrame = false;
|
ADDITIONAL.applyWireFrame = false;
|
||||||
ADDITIONAL.applyCullMode = false;
|
ADDITIONAL.applyCullMode = false;
|
||||||
ADDITIONAL.applyDepthWrite = false;
|
ADDITIONAL.applyDepthWrite = false;
|
||||||
ADDITIONAL.applyDepthTest = false;
|
ADDITIONAL.applyDepthTest = false;
|
||||||
ADDITIONAL.applyColorWrite = false;
|
ADDITIONAL.applyColorWrite = false;
|
||||||
|
ADDITIONAL.applyBlendEquation = false;
|
||||||
|
ADDITIONAL.applyBlendEquationAlpha = false;
|
||||||
ADDITIONAL.applyBlendMode = false;
|
ADDITIONAL.applyBlendMode = false;
|
||||||
ADDITIONAL.applyAlphaTest = false;
|
|
||||||
ADDITIONAL.applyAlphaFallOff = false;
|
|
||||||
ADDITIONAL.applyPolyOffset = false;
|
ADDITIONAL.applyPolyOffset = false;
|
||||||
}
|
}
|
||||||
boolean pointSprite = false;
|
|
||||||
boolean applyPointSprite = true;
|
|
||||||
boolean wireframe = false;
|
boolean wireframe = false;
|
||||||
boolean applyWireFrame = true;
|
boolean applyWireFrame = true;
|
||||||
FaceCullMode cullMode = FaceCullMode.Back;
|
FaceCullMode cullMode = FaceCullMode.Back;
|
||||||
@ -299,12 +382,12 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
boolean applyDepthTest = true;
|
boolean applyDepthTest = true;
|
||||||
boolean colorWrite = true;
|
boolean colorWrite = true;
|
||||||
boolean applyColorWrite = true;
|
boolean applyColorWrite = true;
|
||||||
|
BlendEquation blendEquation = BlendEquation.Add;
|
||||||
|
boolean applyBlendEquation = true;
|
||||||
|
BlendEquationAlpha blendEquationAlpha = BlendEquationAlpha.InheritColor;
|
||||||
|
boolean applyBlendEquationAlpha = true;
|
||||||
BlendMode blendMode = BlendMode.Off;
|
BlendMode blendMode = BlendMode.Off;
|
||||||
boolean applyBlendMode = true;
|
boolean applyBlendMode = true;
|
||||||
boolean alphaTest = false;
|
|
||||||
boolean applyAlphaTest = true;
|
|
||||||
float alphaFallOff = 0;
|
|
||||||
boolean applyAlphaFallOff = true;
|
|
||||||
float offsetFactor = 0;
|
float offsetFactor = 0;
|
||||||
float offsetUnits = 0;
|
float offsetUnits = 0;
|
||||||
boolean offsetEnabled = false;
|
boolean offsetEnabled = false;
|
||||||
@ -316,9 +399,6 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
TestFunction depthFunc = TestFunction.LessOrEqual;
|
TestFunction depthFunc = TestFunction.LessOrEqual;
|
||||||
//by default depth func will be applied anyway if depth test is applied
|
//by default depth func will be applied anyway if depth test is applied
|
||||||
boolean applyDepthFunc = false;
|
boolean applyDepthFunc = false;
|
||||||
//by default alpha func will be applied anyway if alpha test is applied
|
|
||||||
TestFunction alphaFunc = TestFunction.Greater;
|
|
||||||
boolean applyAlphaFunc = false;
|
|
||||||
StencilOperation frontStencilStencilFailOperation = StencilOperation.Keep;
|
StencilOperation frontStencilStencilFailOperation = StencilOperation.Keep;
|
||||||
StencilOperation frontStencilDepthFailOperation = StencilOperation.Keep;
|
StencilOperation frontStencilDepthFailOperation = StencilOperation.Keep;
|
||||||
StencilOperation frontStencilDepthPassOperation = StencilOperation.Keep;
|
StencilOperation frontStencilDepthPassOperation = StencilOperation.Keep;
|
||||||
@ -331,15 +411,13 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
|
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
oc.write(pointSprite, "pointSprite", false);
|
oc.write(true, "pointSprite", false);
|
||||||
oc.write(wireframe, "wireframe", false);
|
oc.write(wireframe, "wireframe", false);
|
||||||
oc.write(cullMode, "cullMode", FaceCullMode.Back);
|
oc.write(cullMode, "cullMode", FaceCullMode.Back);
|
||||||
oc.write(depthWrite, "depthWrite", true);
|
oc.write(depthWrite, "depthWrite", true);
|
||||||
oc.write(depthTest, "depthTest", true);
|
oc.write(depthTest, "depthTest", true);
|
||||||
oc.write(colorWrite, "colorWrite", true);
|
oc.write(colorWrite, "colorWrite", true);
|
||||||
oc.write(blendMode, "blendMode", BlendMode.Off);
|
oc.write(blendMode, "blendMode", BlendMode.Off);
|
||||||
oc.write(alphaTest, "alphaTest", false);
|
|
||||||
oc.write(alphaFallOff, "alphaFallOff", 0);
|
|
||||||
oc.write(offsetEnabled, "offsetEnabled", false);
|
oc.write(offsetEnabled, "offsetEnabled", false);
|
||||||
oc.write(offsetFactor, "offsetFactor", 0);
|
oc.write(offsetFactor, "offsetFactor", 0);
|
||||||
oc.write(offsetUnits, "offsetUnits", 0);
|
oc.write(offsetUnits, "offsetUnits", 0);
|
||||||
@ -352,38 +430,34 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep);
|
oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep);
|
||||||
oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always);
|
oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always);
|
||||||
oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always);
|
oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always);
|
||||||
|
oc.write(blendEquation, "blendEquation", BlendEquation.Add);
|
||||||
|
oc.write(blendEquationAlpha, "blendEquationAlpha", BlendEquationAlpha.InheritColor);
|
||||||
oc.write(depthFunc, "depthFunc", TestFunction.LessOrEqual);
|
oc.write(depthFunc, "depthFunc", TestFunction.LessOrEqual);
|
||||||
oc.write(alphaFunc, "alphaFunc", TestFunction.Greater);
|
|
||||||
oc.write(lineWidth, "lineWidth", 1);
|
oc.write(lineWidth, "lineWidth", 1);
|
||||||
|
|
||||||
// Only "additional render state" has them set to false by default
|
// Only "additional render state" has them set to false by default
|
||||||
oc.write(applyPointSprite, "applyPointSprite", true);
|
|
||||||
oc.write(applyWireFrame, "applyWireFrame", true);
|
oc.write(applyWireFrame, "applyWireFrame", true);
|
||||||
oc.write(applyCullMode, "applyCullMode", true);
|
oc.write(applyCullMode, "applyCullMode", true);
|
||||||
oc.write(applyDepthWrite, "applyDepthWrite", true);
|
oc.write(applyDepthWrite, "applyDepthWrite", true);
|
||||||
oc.write(applyDepthTest, "applyDepthTest", true);
|
oc.write(applyDepthTest, "applyDepthTest", true);
|
||||||
oc.write(applyColorWrite, "applyColorWrite", true);
|
oc.write(applyColorWrite, "applyColorWrite", true);
|
||||||
|
oc.write(applyBlendEquation, "applyBlendEquation", true);
|
||||||
|
oc.write(applyBlendEquationAlpha, "applyBlendEquationAlpha", true);
|
||||||
oc.write(applyBlendMode, "applyBlendMode", true);
|
oc.write(applyBlendMode, "applyBlendMode", true);
|
||||||
oc.write(applyAlphaTest, "applyAlphaTest", true);
|
|
||||||
oc.write(applyAlphaFallOff, "applyAlphaFallOff", true);
|
|
||||||
oc.write(applyPolyOffset, "applyPolyOffset", true);
|
oc.write(applyPolyOffset, "applyPolyOffset", true);
|
||||||
oc.write(applyDepthFunc, "applyDepthFunc", true);
|
oc.write(applyDepthFunc, "applyDepthFunc", true);
|
||||||
oc.write(applyAlphaFunc, "applyAlphaFunc", false);
|
|
||||||
oc.write(applyLineWidth, "applyLineWidth", true);
|
oc.write(applyLineWidth, "applyLineWidth", true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
InputCapsule ic = im.getCapsule(this);
|
InputCapsule ic = im.getCapsule(this);
|
||||||
pointSprite = ic.readBoolean("pointSprite", false);
|
|
||||||
wireframe = ic.readBoolean("wireframe", false);
|
wireframe = ic.readBoolean("wireframe", false);
|
||||||
cullMode = ic.readEnum("cullMode", FaceCullMode.class, FaceCullMode.Back);
|
cullMode = ic.readEnum("cullMode", FaceCullMode.class, FaceCullMode.Back);
|
||||||
depthWrite = ic.readBoolean("depthWrite", true);
|
depthWrite = ic.readBoolean("depthWrite", true);
|
||||||
depthTest = ic.readBoolean("depthTest", true);
|
depthTest = ic.readBoolean("depthTest", true);
|
||||||
colorWrite = ic.readBoolean("colorWrite", true);
|
colorWrite = ic.readBoolean("colorWrite", true);
|
||||||
blendMode = ic.readEnum("blendMode", BlendMode.class, BlendMode.Off);
|
blendMode = ic.readEnum("blendMode", BlendMode.class, BlendMode.Off);
|
||||||
alphaTest = ic.readBoolean("alphaTest", false);
|
|
||||||
alphaFallOff = ic.readFloat("alphaFallOff", 0);
|
|
||||||
offsetEnabled = ic.readBoolean("offsetEnabled", false);
|
offsetEnabled = ic.readBoolean("offsetEnabled", false);
|
||||||
offsetFactor = ic.readFloat("offsetFactor", 0);
|
offsetFactor = ic.readFloat("offsetFactor", 0);
|
||||||
offsetUnits = ic.readFloat("offsetUnits", 0);
|
offsetUnits = ic.readFloat("offsetUnits", 0);
|
||||||
@ -396,23 +470,22 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
backStencilDepthPassOperation = ic.readEnum("backStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep);
|
backStencilDepthPassOperation = ic.readEnum("backStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep);
|
||||||
frontStencilFunction = ic.readEnum("frontStencilFunction", TestFunction.class, TestFunction.Always);
|
frontStencilFunction = ic.readEnum("frontStencilFunction", TestFunction.class, TestFunction.Always);
|
||||||
backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always);
|
backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always);
|
||||||
|
blendEquation = ic.readEnum("blendEquation", BlendEquation.class, BlendEquation.Add);
|
||||||
|
blendEquationAlpha = ic.readEnum("blendEquationAlpha", BlendEquationAlpha.class, BlendEquationAlpha.InheritColor);
|
||||||
depthFunc = ic.readEnum("depthFunc", TestFunction.class, TestFunction.LessOrEqual);
|
depthFunc = ic.readEnum("depthFunc", TestFunction.class, TestFunction.LessOrEqual);
|
||||||
alphaFunc = ic.readEnum("alphaFunc", TestFunction.class, TestFunction.Greater);
|
|
||||||
lineWidth = ic.readFloat("lineWidth", 1);
|
lineWidth = ic.readFloat("lineWidth", 1);
|
||||||
|
|
||||||
|
|
||||||
applyPointSprite = ic.readBoolean("applyPointSprite", true);
|
|
||||||
applyWireFrame = ic.readBoolean("applyWireFrame", true);
|
applyWireFrame = ic.readBoolean("applyWireFrame", true);
|
||||||
applyCullMode = ic.readBoolean("applyCullMode", true);
|
applyCullMode = ic.readBoolean("applyCullMode", true);
|
||||||
applyDepthWrite = ic.readBoolean("applyDepthWrite", true);
|
applyDepthWrite = ic.readBoolean("applyDepthWrite", true);
|
||||||
applyDepthTest = ic.readBoolean("applyDepthTest", true);
|
applyDepthTest = ic.readBoolean("applyDepthTest", true);
|
||||||
applyColorWrite = ic.readBoolean("applyColorWrite", true);
|
applyColorWrite = ic.readBoolean("applyColorWrite", true);
|
||||||
|
applyBlendEquation = ic.readBoolean("applyBlendEquation", true);
|
||||||
|
applyBlendEquationAlpha = ic.readBoolean("applyBlendEquationAlpha", true);
|
||||||
applyBlendMode = ic.readBoolean("applyBlendMode", true);
|
applyBlendMode = ic.readBoolean("applyBlendMode", true);
|
||||||
applyAlphaTest = ic.readBoolean("applyAlphaTest", true);
|
|
||||||
applyAlphaFallOff = ic.readBoolean("applyAlphaFallOff", true);
|
|
||||||
applyPolyOffset = ic.readBoolean("applyPolyOffset", true);
|
applyPolyOffset = ic.readBoolean("applyPolyOffset", true);
|
||||||
applyDepthFunc = ic.readBoolean("applyDepthFunc", true);
|
applyDepthFunc = ic.readBoolean("applyDepthFunc", true);
|
||||||
applyAlphaFunc = ic.readBoolean("applyAlphaFunc", false);
|
|
||||||
applyLineWidth = ic.readBoolean("applyLineWidth", true);
|
applyLineWidth = ic.readBoolean("applyLineWidth", true);
|
||||||
|
|
||||||
|
|
||||||
@ -433,8 +506,8 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns true if the given renderState is equall to this one
|
* returns true if the given renderState is equal to this one
|
||||||
* @param o the renderState to compate to
|
* @param o the renderState to compare to
|
||||||
* @return true if the renderStates are equal
|
* @return true if the renderStates are equal
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ -446,9 +519,6 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
RenderState rs = (RenderState) o;
|
RenderState rs = (RenderState) o;
|
||||||
if (pointSprite != rs.pointSprite) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wireframe != rs.wireframe) {
|
if (wireframe != rs.wireframe) {
|
||||||
return false;
|
return false;
|
||||||
@ -475,22 +545,18 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (blendEquation != rs.blendEquation) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blendEquationAlpha != rs.blendEquationAlpha) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (blendMode != rs.blendMode) {
|
if (blendMode != rs.blendMode) {
|
||||||
return false;
|
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) {
|
if (offsetEnabled != rs.offsetEnabled) {
|
||||||
return false;
|
return false;
|
||||||
@ -544,70 +610,30 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables point sprite mode.
|
* @deprecated Does nothing. Point sprite is already enabled by default for
|
||||||
*
|
* all supported platforms. jME3 does not support rendering conventional
|
||||||
* <p>When point sprite is enabled, any meshes
|
* point clouds.
|
||||||
* with the type of {@link Mode#Points} will be rendered as 2D quads
|
|
||||||
* with texturing enabled. Fragment shaders can write to the
|
|
||||||
* <code>gl_PointCoord</code> variable to manipulate the texture coordinate
|
|
||||||
* for each pixel. The size of the 2D quad can be controlled by writing
|
|
||||||
* to the <code>gl_PointSize</code> variable in the vertex shader.
|
|
||||||
*
|
|
||||||
* @param pointSprite Enables Point Sprite mode.
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setPointSprite(boolean pointSprite) {
|
public void setPointSprite(boolean pointSprite) {
|
||||||
applyPointSprite = true;
|
|
||||||
this.pointSprite = pointSprite;
|
|
||||||
cachedHashCode = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the alpha fall off value for alpha testing.
|
* @deprecated Does nothing. To use alpha test, set the
|
||||||
*
|
* <code>AlphaDiscardThreshold</code> material parameter.
|
||||||
* <p>If the pixel's alpha value is greater than the
|
* @param alphaFallOff does nothing
|
||||||
* <code>alphaFallOff</code> then the pixel will be rendered, otherwise
|
|
||||||
* the pixel will be discarded.
|
|
||||||
*
|
|
||||||
* Note : Alpha test is deprecated since opengl 3.0 and does not exists in
|
|
||||||
* openglES 2.0.
|
|
||||||
* The prefered way is to use the alphaDiscardThreshold on the material
|
|
||||||
* Or have a shader that discards the pixel when its alpha value meets the
|
|
||||||
* discarding condition.
|
|
||||||
*
|
|
||||||
* @param alphaFallOff The alpha of all rendered pixels must be higher
|
|
||||||
* than this value to be rendered. This value should be between 0 and 1.
|
|
||||||
*
|
|
||||||
* @see RenderState#setAlphaTest(boolean)
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setAlphaFallOff(float alphaFallOff) {
|
public void setAlphaFallOff(float alphaFallOff) {
|
||||||
applyAlphaFallOff = true;
|
|
||||||
this.alphaFallOff = alphaFallOff;
|
|
||||||
cachedHashCode = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable alpha testing.
|
* @deprecated Does nothing. To use alpha test, set the
|
||||||
*
|
* <code>AlphaDiscardThreshold</code> material parameter.
|
||||||
* <p>When alpha testing is enabled, all input pixels' alpha are compared
|
* @param alphaTest does nothing
|
||||||
* to the {@link RenderState#setAlphaFallOff(float) constant alpha falloff}.
|
|
||||||
* If the input alpha is greater than the falloff, the pixel will be rendered,
|
|
||||||
* otherwise it will be discarded.
|
|
||||||
*
|
|
||||||
* @param alphaTest Set to true to enable alpha testing.
|
|
||||||
*
|
|
||||||
* Note : Alpha test is deprecated since opengl 3.0 and does not exists in
|
|
||||||
* openglES 2.0.
|
|
||||||
* The prefered way is to use the alphaDiscardThreshold on the material
|
|
||||||
* Or have a shader that discards the pixel when its alpha value meets the
|
|
||||||
* discarding condition.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @see RenderState#setAlphaFallOff(float)
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setAlphaTest(boolean alphaTest) {
|
public void setAlphaTest(boolean alphaTest) {
|
||||||
applyAlphaTest = true;
|
|
||||||
this.alphaTest = alphaTest;
|
|
||||||
cachedHashCode = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -663,6 +689,61 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
cachedHashCode = -1;
|
cachedHashCode = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the blending equation.
|
||||||
|
* <p>
|
||||||
|
* When blending is enabled, (<code>blendMode</code> is not
|
||||||
|
* {@link BlendMode#Off}) the input pixel will be blended with the pixel
|
||||||
|
* already in the color buffer. The blending equation is determined by the
|
||||||
|
* {@link BlendEquation}. For example, the mode {@link BlendMode#Additive}
|
||||||
|
* and {@link BlendEquation#Add} will add the input pixel's color to the
|
||||||
|
* color already in the color buffer:
|
||||||
|
* <br/>
|
||||||
|
* <code>Result = Source Color + Destination Color</code>
|
||||||
|
* <br/>
|
||||||
|
* However, the mode {@link BlendMode#Additive}
|
||||||
|
* and {@link BlendEquation#Subtract} will subtract the input pixel's color to the
|
||||||
|
* color already in the color buffer:
|
||||||
|
* <br/>
|
||||||
|
* <code>Result = Source Color - Destination Color</code>
|
||||||
|
*
|
||||||
|
* @param blendEquation The blend equation to use.
|
||||||
|
*/
|
||||||
|
public void setBlendEquation(BlendEquation blendEquation) {
|
||||||
|
applyBlendEquation = true;
|
||||||
|
this.blendEquation = blendEquation;
|
||||||
|
cachedHashCode = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the blending equation for the alpha component.
|
||||||
|
* <p>
|
||||||
|
* When blending is enabled, (<code>blendMode</code> is not
|
||||||
|
* {@link BlendMode#Off}) the input pixel will be blended with the pixel
|
||||||
|
* already in the color buffer. The blending equation is determined by the
|
||||||
|
* {@link BlendEquation} and can be overrode for the alpha component using
|
||||||
|
* the {@link BlendEquationAlpha} . For example, the mode
|
||||||
|
* {@link BlendMode#Additive} and {@link BlendEquationAlpha#Add} will add
|
||||||
|
* the input pixel's alpha to the alpha component already in the color
|
||||||
|
* buffer:
|
||||||
|
* <br/>
|
||||||
|
* <code>Result = Source Alpha + Destination Alpha</code>
|
||||||
|
* <br/>
|
||||||
|
* However, the mode {@link BlendMode#Additive} and
|
||||||
|
* {@link BlendEquationAlpha#Subtract} will subtract the input pixel's alpha
|
||||||
|
* to the alpha component already in the color buffer:
|
||||||
|
* <br/>
|
||||||
|
* <code>Result = Source Alpha - Destination Alpha</code>
|
||||||
|
*
|
||||||
|
* @param blendEquationAlpha The blend equation to use for the alpha
|
||||||
|
* component.
|
||||||
|
*/
|
||||||
|
public void setBlendEquationAlpha(BlendEquationAlpha blendEquationAlpha) {
|
||||||
|
applyBlendEquationAlpha = true;
|
||||||
|
this.blendEquationAlpha = blendEquationAlpha;
|
||||||
|
cachedHashCode = -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable depth testing.
|
* Enable depth testing.
|
||||||
*
|
*
|
||||||
@ -796,24 +877,10 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the alpha comparision function to the given TestFunction
|
* @deprecated
|
||||||
* default is Greater (GL_GREATER)
|
|
||||||
*
|
|
||||||
* Note : Alpha test is deprecated since opengl 3.0 and does not exists in
|
|
||||||
* openglES 2.0.
|
|
||||||
* The prefered way is to use the alphaDiscardThreshold on the material
|
|
||||||
* Or have a shader taht discards the pixel when its alpha value meets the
|
|
||||||
* discarding condition.
|
|
||||||
*
|
|
||||||
* @see TestFunction
|
|
||||||
* @see RenderState#setAlphaTest(boolean)
|
|
||||||
* @see RenderState#setAlphaFallOff(float)
|
|
||||||
* @param alphaFunc the alpha comparision function
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setAlphaFunc(TestFunction alphaFunc) {
|
public void setAlphaFunc(TestFunction alphaFunc) {
|
||||||
applyAlphaFunc = true;
|
|
||||||
this.alphaFunc = alphaFunc;
|
|
||||||
cachedHashCode = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -822,6 +889,9 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
* @param lineWidth the line width.
|
* @param lineWidth the line width.
|
||||||
*/
|
*/
|
||||||
public void setLineWidth(float lineWidth) {
|
public void setLineWidth(float lineWidth) {
|
||||||
|
if (lineWidth < 1f) {
|
||||||
|
throw new IllegalArgumentException("lineWidth must be greater than or equal to 1.0");
|
||||||
|
}
|
||||||
this.lineWidth = lineWidth;
|
this.lineWidth = lineWidth;
|
||||||
this.applyLineWidth = true;
|
this.applyLineWidth = true;
|
||||||
cachedHashCode = -1;
|
cachedHashCode = -1;
|
||||||
@ -988,6 +1058,24 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
return backStencilFunction;
|
return backStencilFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the blend equation.
|
||||||
|
*
|
||||||
|
* @return the blend equation.
|
||||||
|
*/
|
||||||
|
public BlendEquation getBlendEquation() {
|
||||||
|
return blendEquation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the blend equation used for the alpha component.
|
||||||
|
*
|
||||||
|
* @return the blend equation for the alpha component.
|
||||||
|
*/
|
||||||
|
public BlendEquationAlpha getBlendEquationAlpha() {
|
||||||
|
return blendEquationAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the blend mode.
|
* Retrieve the blend mode.
|
||||||
*
|
*
|
||||||
@ -998,25 +1086,22 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if point sprite mode is enabled
|
* @return true
|
||||||
*
|
* @deprecated Always returns true since point sprite is always enabled.
|
||||||
* @return True if point sprite mode is enabled.
|
* @see #setPointSprite(boolean)
|
||||||
*
|
|
||||||
* @see RenderState#setPointSprite(boolean)
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public boolean isPointSprite() {
|
public boolean isPointSprite() {
|
||||||
return pointSprite;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if alpha test is enabled.
|
* @deprecated To use alpha test, set the <code>AlphaDiscardThreshold</code>
|
||||||
*
|
* material parameter.
|
||||||
* @return True if alpha test is enabled.
|
* @return false
|
||||||
*
|
|
||||||
* @see RenderState#setAlphaTest(boolean)
|
|
||||||
*/
|
*/
|
||||||
public boolean isAlphaTest() {
|
public boolean isAlphaTest() {
|
||||||
return alphaTest;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1108,14 +1193,12 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the alpha falloff value.
|
* @return 0
|
||||||
*
|
* @deprecated
|
||||||
* @return the alpha falloff value.
|
|
||||||
*
|
|
||||||
* @see RenderState#setAlphaFallOff(float)
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public float getAlphaFallOff() {
|
public float getAlphaFallOff() {
|
||||||
return alphaFallOff;
|
return 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1130,14 +1213,12 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the alpha comparison function
|
* @return {@link TestFunction#Greater}.
|
||||||
*
|
* @deprecated
|
||||||
* @return the alpha comparison function
|
|
||||||
*
|
|
||||||
* @see RenderState#setAlphaFunc(com.jme3.material.RenderState.TestFunction)
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public TestFunction getAlphaFunc() {
|
public TestFunction getAlphaFunc() {
|
||||||
return alphaFunc;
|
return TestFunction.Greater;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1150,18 +1231,19 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isApplyAlphaFallOff() {
|
|
||||||
return applyAlphaFallOff;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isApplyAlphaTest() {
|
|
||||||
return applyAlphaTest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isApplyBlendMode() {
|
public boolean isApplyBlendMode() {
|
||||||
return applyBlendMode;
|
return applyBlendMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isApplyBlendEquation() {
|
||||||
|
return applyBlendEquation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isApplyBlendEquationAlpha() {
|
||||||
|
return applyBlendEquationAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isApplyColorWrite() {
|
public boolean isApplyColorWrite() {
|
||||||
return applyColorWrite;
|
return applyColorWrite;
|
||||||
}
|
}
|
||||||
@ -1178,9 +1260,6 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
return applyDepthWrite;
|
return applyDepthWrite;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isApplyPointSprite() {
|
|
||||||
return applyPointSprite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isApplyPolyOffset() {
|
public boolean isApplyPolyOffset() {
|
||||||
return applyPolyOffset;
|
return applyPolyOffset;
|
||||||
@ -1194,9 +1273,6 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
return applyDepthFunc;
|
return applyDepthFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isApplyAlphaFunc() {
|
|
||||||
return applyAlphaFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isApplyLineWidth() {
|
public boolean isApplyLineWidth() {
|
||||||
return applyLineWidth;
|
return applyLineWidth;
|
||||||
@ -1208,7 +1284,6 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
public int contentHashCode() {
|
public int contentHashCode() {
|
||||||
if (cachedHashCode == -1){
|
if (cachedHashCode == -1){
|
||||||
int hash = 7;
|
int hash = 7;
|
||||||
hash = 79 * hash + (this.pointSprite ? 1 : 0);
|
|
||||||
hash = 79 * hash + (this.wireframe ? 1 : 0);
|
hash = 79 * hash + (this.wireframe ? 1 : 0);
|
||||||
hash = 79 * hash + (this.cullMode != null ? this.cullMode.hashCode() : 0);
|
hash = 79 * hash + (this.cullMode != null ? this.cullMode.hashCode() : 0);
|
||||||
hash = 79 * hash + (this.depthWrite ? 1 : 0);
|
hash = 79 * hash + (this.depthWrite ? 1 : 0);
|
||||||
@ -1216,9 +1291,8 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
hash = 79 * hash + (this.depthFunc != null ? this.depthFunc.hashCode() : 0);
|
hash = 79 * hash + (this.depthFunc != null ? this.depthFunc.hashCode() : 0);
|
||||||
hash = 79 * hash + (this.colorWrite ? 1 : 0);
|
hash = 79 * hash + (this.colorWrite ? 1 : 0);
|
||||||
hash = 79 * hash + (this.blendMode != null ? this.blendMode.hashCode() : 0);
|
hash = 79 * hash + (this.blendMode != null ? this.blendMode.hashCode() : 0);
|
||||||
hash = 79 * hash + (this.alphaTest ? 1 : 0);
|
hash = 79 * hash + (this.blendEquation != null ? this.blendEquation.hashCode() : 0);
|
||||||
hash = 79 * hash + (this.alphaFunc != null ? this.alphaFunc.hashCode() : 0);
|
hash = 79 * hash + (this.blendEquationAlpha != null ? this.blendEquationAlpha.hashCode() : 0);
|
||||||
hash = 79 * hash + Float.floatToIntBits(this.alphaFallOff);
|
|
||||||
hash = 79 * hash + Float.floatToIntBits(this.offsetFactor);
|
hash = 79 * hash + Float.floatToIntBits(this.offsetFactor);
|
||||||
hash = 79 * hash + Float.floatToIntBits(this.offsetUnits);
|
hash = 79 * hash + Float.floatToIntBits(this.offsetUnits);
|
||||||
hash = 79 * hash + (this.offsetEnabled ? 1 : 0);
|
hash = 79 * hash + (this.offsetEnabled ? 1 : 0);
|
||||||
@ -1263,11 +1337,6 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (additionalState.applyPointSprite) {
|
|
||||||
state.pointSprite = additionalState.pointSprite;
|
|
||||||
} else {
|
|
||||||
state.pointSprite = pointSprite;
|
|
||||||
}
|
|
||||||
if (additionalState.applyWireFrame) {
|
if (additionalState.applyWireFrame) {
|
||||||
state.wireframe = additionalState.wireframe;
|
state.wireframe = additionalState.wireframe;
|
||||||
} else {
|
} else {
|
||||||
@ -1299,27 +1368,22 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
} else {
|
} else {
|
||||||
state.colorWrite = colorWrite;
|
state.colorWrite = colorWrite;
|
||||||
}
|
}
|
||||||
|
if (additionalState.applyBlendEquation) {
|
||||||
|
state.blendEquation = additionalState.blendEquation;
|
||||||
|
} else {
|
||||||
|
state.blendEquation = blendEquation;
|
||||||
|
}
|
||||||
|
if (additionalState.applyBlendEquationAlpha) {
|
||||||
|
state.blendEquationAlpha = additionalState.blendEquationAlpha;
|
||||||
|
} else {
|
||||||
|
state.blendEquationAlpha = blendEquationAlpha;
|
||||||
|
}
|
||||||
if (additionalState.applyBlendMode) {
|
if (additionalState.applyBlendMode) {
|
||||||
state.blendMode = additionalState.blendMode;
|
state.blendMode = additionalState.blendMode;
|
||||||
} else {
|
} else {
|
||||||
state.blendMode = blendMode;
|
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) {
|
if (additionalState.applyPolyOffset) {
|
||||||
state.offsetEnabled = additionalState.offsetEnabled;
|
state.offsetEnabled = additionalState.offsetEnabled;
|
||||||
state.offsetFactor = additionalState.offsetFactor;
|
state.offsetFactor = additionalState.offsetFactor;
|
||||||
@ -1364,16 +1428,14 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
state.cachedHashCode = -1;
|
state.cachedHashCode = -1;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(RenderState state) {
|
public void set(RenderState state) {
|
||||||
pointSprite = state.pointSprite;
|
|
||||||
wireframe = state.wireframe;
|
wireframe = state.wireframe;
|
||||||
cullMode = state.cullMode;
|
cullMode = state.cullMode;
|
||||||
depthWrite = state.depthWrite;
|
depthWrite = state.depthWrite;
|
||||||
depthTest = state.depthTest;
|
depthTest = state.depthTest;
|
||||||
colorWrite = state.colorWrite;
|
colorWrite = state.colorWrite;
|
||||||
blendMode = state.blendMode;
|
blendMode = state.blendMode;
|
||||||
alphaTest = state.alphaTest;
|
|
||||||
alphaFallOff = state.alphaFallOff;
|
|
||||||
offsetEnabled = state.offsetEnabled;
|
offsetEnabled = state.offsetEnabled;
|
||||||
offsetFactor = state.offsetFactor;
|
offsetFactor = state.offsetFactor;
|
||||||
offsetUnits = state.offsetUnits;
|
offsetUnits = state.offsetUnits;
|
||||||
@ -1386,30 +1448,27 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
backStencilDepthPassOperation = state.backStencilDepthPassOperation;
|
backStencilDepthPassOperation = state.backStencilDepthPassOperation;
|
||||||
frontStencilFunction = state.frontStencilFunction;
|
frontStencilFunction = state.frontStencilFunction;
|
||||||
backStencilFunction = state.backStencilFunction;
|
backStencilFunction = state.backStencilFunction;
|
||||||
|
blendEquationAlpha = state.blendEquationAlpha;
|
||||||
|
blendEquation = state.blendEquation;
|
||||||
depthFunc = state.depthFunc;
|
depthFunc = state.depthFunc;
|
||||||
alphaFunc = state.alphaFunc;
|
|
||||||
lineWidth = state.lineWidth;
|
lineWidth = state.lineWidth;
|
||||||
|
|
||||||
applyPointSprite = true;
|
|
||||||
applyWireFrame = true;
|
applyWireFrame = true;
|
||||||
applyCullMode = true;
|
applyCullMode = true;
|
||||||
applyDepthWrite = true;
|
applyDepthWrite = true;
|
||||||
applyDepthTest = true;
|
applyDepthTest = true;
|
||||||
applyColorWrite = true;
|
applyColorWrite = true;
|
||||||
|
applyBlendEquation = true;
|
||||||
|
applyBlendEquationAlpha = true;
|
||||||
applyBlendMode = true;
|
applyBlendMode = true;
|
||||||
applyAlphaTest = true;
|
|
||||||
applyAlphaFallOff = true;
|
|
||||||
applyPolyOffset = true;
|
applyPolyOffset = true;
|
||||||
applyDepthFunc = true;
|
applyDepthFunc = true;
|
||||||
applyAlphaFunc = false;
|
|
||||||
applyLineWidth = true;
|
applyLineWidth = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "RenderState[\n"
|
return "RenderState[\n"
|
||||||
+ "pointSprite=" + pointSprite
|
|
||||||
+ "\napplyPointSprite=" + applyPointSprite
|
|
||||||
+ "\nwireframe=" + wireframe
|
+ "\nwireframe=" + wireframe
|
||||||
+ "\napplyWireFrame=" + applyWireFrame
|
+ "\napplyWireFrame=" + applyWireFrame
|
||||||
+ "\ncullMode=" + cullMode
|
+ "\ncullMode=" + cullMode
|
||||||
@ -1421,13 +1480,11 @@ public class RenderState implements Cloneable, Savable {
|
|||||||
+ "\napplyDepthTest=" + applyDepthTest
|
+ "\napplyDepthTest=" + applyDepthTest
|
||||||
+ "\ncolorWrite=" + colorWrite
|
+ "\ncolorWrite=" + colorWrite
|
||||||
+ "\napplyColorWrite=" + applyColorWrite
|
+ "\napplyColorWrite=" + applyColorWrite
|
||||||
|
+ "\nblendEquation=" + blendEquation
|
||||||
|
+ "\napplyBlendEquation=" + applyBlendEquation
|
||||||
|
+ "\napplyBlendEquationAlpha=" + applyBlendEquationAlpha
|
||||||
+ "\nblendMode=" + blendMode
|
+ "\nblendMode=" + blendMode
|
||||||
+ "\napplyBlendMode=" + applyBlendMode
|
+ "\napplyBlendMode=" + applyBlendMode
|
||||||
+ "\nalphaTest=" + alphaTest
|
|
||||||
+ "\nalphaFunc=" + alphaFunc
|
|
||||||
+ "\napplyAlphaTest=" + applyAlphaTest
|
|
||||||
+ "\nalphaFallOff=" + alphaFallOff
|
|
||||||
+ "\napplyAlphaFallOff=" + applyAlphaFallOff
|
|
||||||
+ "\noffsetEnabled=" + offsetEnabled
|
+ "\noffsetEnabled=" + offsetEnabled
|
||||||
+ "\napplyPolyOffset=" + applyPolyOffset
|
+ "\napplyPolyOffset=" + applyPolyOffset
|
||||||
+ "\noffsetFactor=" + offsetFactor
|
+ "\noffsetFactor=" + offsetFactor
|
||||||
|
@ -31,27 +31,30 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.material;
|
package com.jme3.material;
|
||||||
|
|
||||||
|
import com.jme3.material.logic.TechniqueDefLogic;
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.light.LightList;
|
||||||
|
import com.jme3.material.TechniqueDef.LightMode;
|
||||||
import com.jme3.renderer.Caps;
|
import com.jme3.renderer.Caps;
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
import com.jme3.shader.*;
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.shader.DefineList;
|
||||||
|
import com.jme3.shader.Shader;
|
||||||
|
import com.jme3.shader.VarType;
|
||||||
|
import com.jme3.util.ListMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a technique instance.
|
* Represents a technique instance.
|
||||||
*/
|
*/
|
||||||
public class Technique /* implements Savable */ {
|
public final class Technique {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Technique.class.getName());
|
private final TechniqueDef def;
|
||||||
private TechniqueDef def;
|
private final Material owner;
|
||||||
private Material owner;
|
private final DefineList paramDefines;
|
||||||
private ArrayList<Uniform> worldBindUniforms;
|
private final DefineList dynamicDefines;
|
||||||
private DefineList defines;
|
|
||||||
private Shader shader;
|
|
||||||
private boolean needReload = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new technique instance that implements the given
|
* Creates a new technique instance that implements the given
|
||||||
@ -63,14 +66,8 @@ public class Technique /* implements Savable */ {
|
|||||||
public Technique(Material owner, TechniqueDef def) {
|
public Technique(Material owner, TechniqueDef def) {
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.def = def;
|
this.def = def;
|
||||||
this.worldBindUniforms = new ArrayList<Uniform>();
|
this.paramDefines = def.createDefineList();
|
||||||
this.defines = new DefineList();
|
this.dynamicDefines = def.createDefineList();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialization only. Do not use.
|
|
||||||
*/
|
|
||||||
public Technique() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,158 +81,126 @@ public class Technique /* implements Savable */ {
|
|||||||
return def;
|
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.
|
* 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.
|
* Specify <code>null</code> for value if the param is to be cleared.
|
||||||
*/
|
*/
|
||||||
void notifyParamChanged(String paramName, VarType type, Object value) {
|
final void notifyParamChanged(String paramName, VarType type, Object value) {
|
||||||
// Check if there's a define binding associated with this
|
Integer defineId = def.getShaderParamDefineId(paramName);
|
||||||
// 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) {
|
if (defineId == null) {
|
||||||
// Clear the define.
|
return;
|
||||||
needReload = defines.remove(defineName) || needReload;
|
|
||||||
} else {
|
|
||||||
// Set the define.
|
|
||||||
needReload = defines.set(defineName, type, value) || needReload;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paramDefines.set(defineId, type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateUniformParam(String paramName, VarType type, Object value) {
|
private void applyOverrides(DefineList defineList, List<MatParamOverride> overrides) {
|
||||||
if (paramName == null) {
|
for (MatParamOverride override : overrides) {
|
||||||
throw new IllegalArgumentException();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
* Called by the material to determine which shader to use for rendering.
|
||||||
* <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.
|
* The {@link TechniqueDefLogic} is used to determine the shader to use
|
||||||
*/
|
* based on the {@link LightMode}.
|
||||||
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.
|
* @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.
|
||||||
*/
|
*/
|
||||||
public void makeCurrent(AssetManager assetManager, boolean techniqueSwitched, EnumSet<Caps> rendererCaps, RenderManager rm) {
|
Shader makeCurrent(RenderManager renderManager, List<MatParamOverride> worldOverrides,
|
||||||
if (techniqueSwitched) {
|
List<MatParamOverride> forcedOverrides,
|
||||||
if (defines.update(owner.getParamsMap(), def)) {
|
LightList lights, EnumSet<Caps> rendererCaps) {
|
||||||
needReload = true;
|
TechniqueDefLogic logic = def.getLogic();
|
||||||
}
|
AssetManager assetManager = owner.getMaterialDef().getAssetManager();
|
||||||
if (getDef().getLightMode() == TechniqueDef.LightMode.SinglePass) {
|
|
||||||
defines.set("SINGLE_PASS_LIGHTING", VarType.Boolean, true);
|
dynamicDefines.clear();
|
||||||
defines.set("NB_LIGHTS", VarType.Int, rm.getSinglePassLightBatchSize() * 3);
|
dynamicDefines.setAll(paramDefines);
|
||||||
} else {
|
|
||||||
defines.set("SINGLE_PASS_LIGHTING", VarType.Boolean, null);
|
if (worldOverrides != null) {
|
||||||
|
applyOverrides(dynamicDefines, worldOverrides);
|
||||||
}
|
}
|
||||||
|
if (forcedOverrides != null) {
|
||||||
|
applyOverrides(dynamicDefines, forcedOverrides);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needReload) {
|
return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines);
|
||||||
loadShader(assetManager,rendererCaps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadShader(AssetManager manager,EnumSet<Caps> rendererCaps) {
|
|
||||||
|
|
||||||
ShaderKey key = new ShaderKey(getAllDefines(),def.getShaderProgramLanguages(),def.getShaderProgramNames());
|
|
||||||
|
|
||||||
if (getDef().isUsingShaderNodes()) {
|
|
||||||
manager.getShaderGenerator(rendererCaps).initialize(this);
|
|
||||||
key.setUsesShaderNodes(true);
|
|
||||||
}
|
|
||||||
shader = manager.loadShader(key);
|
|
||||||
|
|
||||||
// register the world bound uniforms
|
|
||||||
worldBindUniforms.clear();
|
|
||||||
if (def.getWorldBindings() != null) {
|
|
||||||
for (UniformBinding binding : def.getWorldBindings()) {
|
|
||||||
Uniform uniform = shader.getUniform("g_" + binding.name());
|
|
||||||
uniform.setBinding(binding);
|
|
||||||
worldBindUniforms.add(uniform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
needReload = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the define list
|
* Render the technique according to its {@link TechniqueDefLogic}.
|
||||||
* @return the complete define list
|
*
|
||||||
|
* @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() {
|
public DefineList getAllDefines() {
|
||||||
DefineList allDefines = new DefineList();
|
throw new UnsupportedOperationException();
|
||||||
allDefines.addFrom(def.getShaderPresetDefines());
|
|
||||||
allDefines.addFrom(defines);
|
|
||||||
return allDefines;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
public void write(JmeExporter ex) throws IOException {
|
* Compute the sort ID. Similar to {@link Object#hashCode()} but used
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
* for sorting geometries for rendering.
|
||||||
oc.write(def, "def", null);
|
*
|
||||||
oc.writeSavableArrayList(worldBindUniforms, "worldBindUniforms", null);
|
* @return the sort ID for this technique instance.
|
||||||
oc.write(defines, "defines", null);
|
|
||||||
oc.write(shader, "shader", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
public int getSortId() {
|
||||||
|
int hash = 17;
|
||||||
|
hash = hash * 23 + def.getSortId();
|
||||||
|
hash = hash * 23 + paramDefines.hashCode();
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.material;
|
package com.jme3.material;
|
||||||
|
|
||||||
|
import com.jme3.material.logic.TechniqueDefLogic;
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
import com.jme3.renderer.Caps;
|
import com.jme3.renderer.Caps;
|
||||||
import com.jme3.shader.*;
|
import com.jme3.shader.*;
|
||||||
|
import com.jme3.shader.Shader.ShaderType;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -50,6 +53,14 @@ public class TechniqueDef implements Savable {
|
|||||||
*/
|
*/
|
||||||
public static final int SAVABLE_VERSION = 1;
|
public static final int SAVABLE_VERSION = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default technique name.
|
||||||
|
*
|
||||||
|
* The technique with this name is selected if no specific technique is
|
||||||
|
* requested by the user. Currently set to "Default".
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_TECHNIQUE_NAME = "Default";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes light rendering mode.
|
* Describes light rendering mode.
|
||||||
*/
|
*/
|
||||||
@ -91,13 +102,19 @@ public class TechniqueDef implements Savable {
|
|||||||
PostPass,
|
PostPass,
|
||||||
}
|
}
|
||||||
|
|
||||||
private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
|
private final EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
|
||||||
private String name;
|
private String name;
|
||||||
|
private int sortId;
|
||||||
|
|
||||||
private EnumMap<Shader.ShaderType,String> shaderLanguages;
|
private EnumMap<Shader.ShaderType,String> shaderLanguages;
|
||||||
private EnumMap<Shader.ShaderType,String> shaderNames;
|
private EnumMap<Shader.ShaderType,String> shaderNames;
|
||||||
|
|
||||||
private DefineList presetDefines;
|
private String shaderPrologue;
|
||||||
|
private ArrayList<String> defineNames;
|
||||||
|
private ArrayList<VarType> defineTypes;
|
||||||
|
private HashMap<String, Integer> paramToDefineId;
|
||||||
|
private final HashMap<DefineList, Shader> definesToShaderMap;
|
||||||
|
|
||||||
private boolean usesNodes = false;
|
private boolean usesNodes = false;
|
||||||
private List<ShaderNode> shaderNodes;
|
private List<ShaderNode> shaderNodes;
|
||||||
private ShaderGenerationInfo shaderGenerationInfo;
|
private ShaderGenerationInfo shaderGenerationInfo;
|
||||||
@ -108,8 +125,8 @@ public class TechniqueDef implements Savable {
|
|||||||
|
|
||||||
private LightMode lightMode = LightMode.Disable;
|
private LightMode lightMode = LightMode.Disable;
|
||||||
private ShadowMode shadowMode = ShadowMode.Disable;
|
private ShadowMode shadowMode = ShadowMode.Disable;
|
||||||
|
private TechniqueDefLogic logic;
|
||||||
|
|
||||||
private HashMap<String, String> defineParams;
|
|
||||||
private ArrayList<UniformBinding> worldBinds;
|
private ArrayList<UniformBinding> worldBinds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,12 +134,12 @@ public class TechniqueDef implements Savable {
|
|||||||
* <p>
|
* <p>
|
||||||
* Used internally by the J3M/J3MD loader.
|
* Used internally by the J3M/J3MD loader.
|
||||||
*
|
*
|
||||||
* @param name The name of the technique, should be set to <code>null</code>
|
* @param name The name of the technique
|
||||||
* for default techniques.
|
|
||||||
*/
|
*/
|
||||||
public TechniqueDef(String name){
|
public TechniqueDef(String name, int sortId){
|
||||||
this();
|
this();
|
||||||
this.name = name == null ? "Default" : name;
|
this.sortId = sortId;
|
||||||
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,11 +148,24 @@ public class TechniqueDef implements Savable {
|
|||||||
public TechniqueDef() {
|
public TechniqueDef() {
|
||||||
shaderLanguages = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
shaderLanguages = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||||
shaderNames = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
shaderNames = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
||||||
|
defineNames = new ArrayList<String>();
|
||||||
|
defineTypes = new ArrayList<VarType>();
|
||||||
|
paramToDefineId = new HashMap<String, Integer>();
|
||||||
|
definesToShaderMap = new HashMap<DefineList, Shader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A unique sort ID.
|
||||||
|
* No other technique definition can have the same ID.
|
||||||
|
*/
|
||||||
|
public int getSortId() {
|
||||||
|
return sortId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of this technique as specified in the J3MD file.
|
* Returns the name of this technique as specified in the J3MD file.
|
||||||
* Default techniques have the name "Default".
|
* Default
|
||||||
|
* techniques have the name {@link #DEFAULT_TECHNIQUE_NAME}.
|
||||||
*
|
*
|
||||||
* @return the name of this technique
|
* @return the name of this technique
|
||||||
*/
|
*/
|
||||||
@ -163,6 +193,14 @@ public class TechniqueDef implements Savable {
|
|||||||
this.lightMode = lightMode;
|
this.lightMode = lightMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLogic(TechniqueDefLogic logic) {
|
||||||
|
this.logic = logic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TechniqueDefLogic getLogic() {
|
||||||
|
return logic;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the shadow mode.
|
* Returns the shadow mode.
|
||||||
* @return the shadow mode.
|
* @return the shadow mode.
|
||||||
@ -224,14 +262,6 @@ public class TechniqueDef implements Savable {
|
|||||||
return noRender;
|
return noRender;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated jME3 always requires shaders now
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public boolean isUsingShaders(){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this technique uses Shader Nodes, false otherwise.
|
* Returns true if this technique uses Shader Nodes, false otherwise.
|
||||||
*
|
*
|
||||||
@ -273,6 +303,187 @@ public class TechniqueDef implements Savable {
|
|||||||
requiredCaps.add(fragCap);
|
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.
|
* 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
|
* Returns the name of the fragment shader used by the technique, or null
|
||||||
* if no fragment shader is specified.
|
* 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.TessellationControl), "tsctrlLanguage", null);
|
||||||
oc.write(shaderLanguages.get(Shader.ShaderType.TessellationEvaluation), "tsevalLanguage", null);
|
oc.write(shaderLanguages.get(Shader.ShaderType.TessellationEvaluation), "tsevalLanguage", null);
|
||||||
|
|
||||||
oc.write(presetDefines, "presetDefines", null);
|
oc.write(shaderPrologue, "shaderPrologue", null);
|
||||||
oc.write(lightMode, "lightMode", LightMode.Disable);
|
oc.write(lightMode, "lightMode", LightMode.Disable);
|
||||||
oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
|
oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
|
||||||
oc.write(renderState, "renderState", null);
|
oc.write(renderState, "renderState", null);
|
||||||
@ -490,7 +636,7 @@ public class TechniqueDef implements Savable {
|
|||||||
shaderNames.put(Shader.ShaderType.Geometry,ic.readString("geomName", null));
|
shaderNames.put(Shader.ShaderType.Geometry,ic.readString("geomName", null));
|
||||||
shaderNames.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlName", null));
|
shaderNames.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrlName", null));
|
||||||
shaderNames.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalName", null));
|
shaderNames.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tsevalName", null));
|
||||||
presetDefines = (DefineList) ic.readSavable("presetDefines", null);
|
shaderPrologue = ic.readString("shaderPrologue", null);
|
||||||
lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
|
lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
|
||||||
shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
|
shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
|
||||||
renderState = (RenderState) ic.readSavable("renderState", null);
|
renderState = (RenderState) ic.readSavable("renderState", null);
|
||||||
@ -547,9 +693,14 @@ public class TechniqueDef implements Savable {
|
|||||||
this.shaderGenerationInfo = shaderGenerationInfo;
|
this.shaderGenerationInfo = shaderGenerationInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo: make toString return something usefull
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "TechniqueDef{" + "requiredCaps=" + requiredCaps + ", name=" + name /*+ ", vertName=" + vertName + ", fragName=" + fragName + ", vertLanguage=" + vertLanguage + ", fragLanguage=" + fragLanguage */+ ", presetDefines=" + presetDefines + ", usesNodes=" + usesNodes + ", shaderNodes=" + shaderNodes + ", shaderGenerationInfo=" + shaderGenerationInfo + ", renderState=" + renderState + ", forcedRenderState=" + forcedRenderState + ", lightMode=" + lightMode + ", shadowMode=" + shadowMode + ", defineParams=" + defineParams + ", worldBinds=" + worldBinds + ", noRender=" + noRender + '}';
|
return "TechniqueDef[name=" + name
|
||||||
|
+ ", requiredCaps=" + requiredCaps
|
||||||
|
+ ", noRender=" + noRender
|
||||||
|
+ ", lightMode=" + lightMode
|
||||||
|
+ ", usesNodes=" + usesNodes
|
||||||
|
+ ", renderState=" + renderState
|
||||||
|
+ ", forcedRenderState=" + forcedRenderState + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.material.logic;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.light.AmbientLight;
|
||||||
|
import com.jme3.light.Light;
|
||||||
|
import com.jme3.light.LightList;
|
||||||
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.renderer.Caps;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.Renderer;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.instancing.InstancedGeometry;
|
||||||
|
import com.jme3.shader.DefineList;
|
||||||
|
import com.jme3.shader.Shader;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
public class DefaultTechniqueDefLogic implements TechniqueDefLogic {
|
||||||
|
|
||||||
|
protected final TechniqueDef techniqueDef;
|
||||||
|
|
||||||
|
public DefaultTechniqueDefLogic(TechniqueDef techniqueDef) {
|
||||||
|
this.techniqueDef = techniqueDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
|
||||||
|
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
|
||||||
|
return techniqueDef.getShader(assetManager, rendererCaps, defines);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void renderMeshFromGeometry(Renderer renderer, Geometry geom) {
|
||||||
|
Mesh mesh = geom.getMesh();
|
||||||
|
int lodLevel = geom.getLodLevel();
|
||||||
|
if (geom instanceof InstancedGeometry) {
|
||||||
|
InstancedGeometry instGeom = (InstancedGeometry) geom;
|
||||||
|
renderer.renderMesh(mesh, lodLevel, instGeom.getActualNumInstances(),
|
||||||
|
instGeom.getAllInstanceData());
|
||||||
|
} else {
|
||||||
|
renderer.renderMesh(mesh, lodLevel, 1, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static ColorRGBA getAmbientColor(LightList lightList, boolean removeLights, ColorRGBA ambientLightColor) {
|
||||||
|
ambientLightColor.set(0, 0, 0, 1);
|
||||||
|
for (int j = 0; j < lightList.size(); j++) {
|
||||||
|
Light l = lightList.get(j);
|
||||||
|
if (l instanceof AmbientLight) {
|
||||||
|
ambientLightColor.addLocal(l.getColor());
|
||||||
|
if (removeLights) {
|
||||||
|
lightList.remove(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ambientLightColor.a = 1.0f;
|
||||||
|
return ambientLightColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
|
||||||
|
Renderer renderer = renderManager.getRenderer();
|
||||||
|
renderer.setShader(shader);
|
||||||
|
renderMeshFromGeometry(renderer, geometry);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.material.logic;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.light.AmbientLight;
|
||||||
|
import com.jme3.light.DirectionalLight;
|
||||||
|
import com.jme3.light.Light;
|
||||||
|
import com.jme3.light.LightList;
|
||||||
|
import com.jme3.light.PointLight;
|
||||||
|
import com.jme3.light.SpotLight;
|
||||||
|
import com.jme3.material.RenderState;
|
||||||
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.math.Vector4f;
|
||||||
|
import com.jme3.renderer.Caps;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.Renderer;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.shader.DefineList;
|
||||||
|
import com.jme3.shader.Shader;
|
||||||
|
import com.jme3.shader.Uniform;
|
||||||
|
import com.jme3.shader.VarType;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
public final class MultiPassLightingLogic extends DefaultTechniqueDefLogic {
|
||||||
|
|
||||||
|
private static final RenderState ADDITIVE_LIGHT = new RenderState();
|
||||||
|
private static final Quaternion NULL_DIR_LIGHT = new Quaternion(0, -1, 0, -1);
|
||||||
|
|
||||||
|
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
|
||||||
|
|
||||||
|
static {
|
||||||
|
ADDITIVE_LIGHT.setBlendMode(RenderState.BlendMode.AlphaAdditive);
|
||||||
|
ADDITIVE_LIGHT.setDepthWrite(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiPassLightingLogic(TechniqueDef techniqueDef) {
|
||||||
|
super(techniqueDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
|
||||||
|
Renderer r = renderManager.getRenderer();
|
||||||
|
Uniform lightDir = shader.getUniform("g_LightDirection");
|
||||||
|
Uniform lightColor = shader.getUniform("g_LightColor");
|
||||||
|
Uniform lightPos = shader.getUniform("g_LightPosition");
|
||||||
|
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||||
|
boolean isFirstLight = true;
|
||||||
|
boolean isSecondLight = false;
|
||||||
|
|
||||||
|
getAmbientColor(lights, false, ambientLightColor);
|
||||||
|
|
||||||
|
for (int i = 0; i < lights.size(); i++) {
|
||||||
|
Light l = lights.get(i);
|
||||||
|
if (l instanceof AmbientLight) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFirstLight) {
|
||||||
|
// set ambient color for first light only
|
||||||
|
ambientColor.setValue(VarType.Vector4, ambientLightColor);
|
||||||
|
isFirstLight = false;
|
||||||
|
isSecondLight = true;
|
||||||
|
} else if (isSecondLight) {
|
||||||
|
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
||||||
|
// apply additive blending for 2nd and future lights
|
||||||
|
r.applyRenderState(ADDITIVE_LIGHT);
|
||||||
|
isSecondLight = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
Quaternion tmpLightDirection = vars.quat1;
|
||||||
|
Quaternion tmpLightPosition = vars.quat2;
|
||||||
|
ColorRGBA tmpLightColor = vars.color;
|
||||||
|
Vector4f tmpVec = vars.vect4f1;
|
||||||
|
|
||||||
|
ColorRGBA color = l.getColor();
|
||||||
|
tmpLightColor.set(color);
|
||||||
|
tmpLightColor.a = l.getType().getId();
|
||||||
|
lightColor.setValue(VarType.Vector4, tmpLightColor);
|
||||||
|
|
||||||
|
switch (l.getType()) {
|
||||||
|
case Directional:
|
||||||
|
DirectionalLight dl = (DirectionalLight) l;
|
||||||
|
Vector3f dir = dl.getDirection();
|
||||||
|
//FIXME : there is an inconstency here due to backward
|
||||||
|
//compatibility of the lighting shader.
|
||||||
|
//The directional light direction is passed in the
|
||||||
|
//LightPosition uniform. The lighting shader needs to be
|
||||||
|
//reworked though in order to fix this.
|
||||||
|
tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
|
||||||
|
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
||||||
|
tmpLightDirection.set(0, 0, 0, 0);
|
||||||
|
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
||||||
|
break;
|
||||||
|
case Point:
|
||||||
|
PointLight pl = (PointLight) l;
|
||||||
|
Vector3f pos = pl.getPosition();
|
||||||
|
float invRadius = pl.getInvRadius();
|
||||||
|
|
||||||
|
tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
|
||||||
|
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
||||||
|
tmpLightDirection.set(0, 0, 0, 0);
|
||||||
|
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
||||||
|
break;
|
||||||
|
case Spot:
|
||||||
|
SpotLight sl = (SpotLight) l;
|
||||||
|
Vector3f pos2 = sl.getPosition();
|
||||||
|
Vector3f dir2 = sl.getDirection();
|
||||||
|
float invRange = sl.getInvSpotRange();
|
||||||
|
float spotAngleCos = sl.getPackedAngleCos();
|
||||||
|
|
||||||
|
tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
|
||||||
|
lightPos.setValue(VarType.Vector4, tmpLightPosition);
|
||||||
|
|
||||||
|
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
|
||||||
|
//one vec4 less and a vec4 that becomes a vec3
|
||||||
|
//the downside is that spotAngleCos decoding happens now in the frag shader.
|
||||||
|
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
|
||||||
|
renderManager.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||||
|
tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
|
||||||
|
|
||||||
|
lightDir.setValue(VarType.Vector4, tmpLightDirection);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
r.setShader(shader);
|
||||||
|
renderMeshFromGeometry(r, geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFirstLight) {
|
||||||
|
// Either there are no lights at all, or only ambient lights.
|
||||||
|
// Render a dummy "normal light" so we can see the ambient color.
|
||||||
|
ambientColor.setValue(VarType.Vector4, getAmbientColor(lights, false, ambientLightColor));
|
||||||
|
lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
|
||||||
|
lightPos.setValue(VarType.Vector4, NULL_DIR_LIGHT);
|
||||||
|
r.setShader(shader);
|
||||||
|
renderMeshFromGeometry(r, geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,218 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.material.logic;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.light.DirectionalLight;
|
||||||
|
import com.jme3.light.Light;
|
||||||
|
import com.jme3.light.LightList;
|
||||||
|
import com.jme3.light.PointLight;
|
||||||
|
import com.jme3.light.SpotLight;
|
||||||
|
import com.jme3.material.RenderState;
|
||||||
|
import com.jme3.material.RenderState.BlendMode;
|
||||||
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.math.Vector4f;
|
||||||
|
import com.jme3.renderer.Caps;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.Renderer;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.shader.DefineList;
|
||||||
|
import com.jme3.shader.Shader;
|
||||||
|
import com.jme3.shader.Uniform;
|
||||||
|
import com.jme3.shader.VarType;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
public final class SinglePassLightingLogic extends DefaultTechniqueDefLogic {
|
||||||
|
|
||||||
|
private static final String DEFINE_SINGLE_PASS_LIGHTING = "SINGLE_PASS_LIGHTING";
|
||||||
|
private static final String DEFINE_NB_LIGHTS = "NB_LIGHTS";
|
||||||
|
private static final RenderState ADDITIVE_LIGHT = new RenderState();
|
||||||
|
|
||||||
|
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
|
||||||
|
|
||||||
|
static {
|
||||||
|
ADDITIVE_LIGHT.setBlendMode(BlendMode.AlphaAdditive);
|
||||||
|
ADDITIVE_LIGHT.setDepthWrite(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int singlePassLightingDefineId;
|
||||||
|
private final int nbLightsDefineId;
|
||||||
|
|
||||||
|
public SinglePassLightingLogic(TechniqueDef techniqueDef) {
|
||||||
|
super(techniqueDef);
|
||||||
|
singlePassLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_SINGLE_PASS_LIGHTING, VarType.Boolean);
|
||||||
|
nbLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_LIGHTS, VarType.Int);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
|
||||||
|
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
|
||||||
|
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3);
|
||||||
|
defines.set(singlePassLightingDefineId, true);
|
||||||
|
return super.makeCurrent(assetManager, renderManager, rendererCaps, lights, defines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the lights in the light list as two uniform arrays.<br/><br/> *
|
||||||
|
* <p>
|
||||||
|
* <code>uniform vec4 g_LightColor[numLights];</code><br/> //
|
||||||
|
* g_LightColor.rgb is the diffuse/specular color of the light.<br/> //
|
||||||
|
* g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/> //
|
||||||
|
* 2 = Spot. <br/> <br/>
|
||||||
|
* <code>uniform vec4 g_LightPosition[numLights];</code><br/> //
|
||||||
|
* g_LightPosition.xyz is the position of the light (for point lights)<br/>
|
||||||
|
* // or the direction of the light (for directional lights).<br/> //
|
||||||
|
* g_LightPosition.w is the inverse radius (1/r) of the light (for
|
||||||
|
* attenuation) <br/> </p>
|
||||||
|
*/
|
||||||
|
protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex) {
|
||||||
|
if (numLights == 0) { // this shader does not do lighting, ignore.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uniform lightData = shader.getUniform("g_LightData");
|
||||||
|
lightData.setVector4Length(numLights * 3);//8 lights * max 3
|
||||||
|
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||||
|
|
||||||
|
|
||||||
|
if (startIndex != 0) {
|
||||||
|
// apply additive blending for 2nd and future passes
|
||||||
|
rm.getRenderer().applyRenderState(ADDITIVE_LIGHT);
|
||||||
|
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
|
||||||
|
} else {
|
||||||
|
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, true, ambientLightColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
int lightDataIndex = 0;
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
Vector4f tmpVec = vars.vect4f1;
|
||||||
|
int curIndex;
|
||||||
|
int endIndex = numLights + startIndex;
|
||||||
|
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
|
||||||
|
|
||||||
|
Light l = lightList.get(curIndex);
|
||||||
|
if (l.getType() == Light.Type.Ambient) {
|
||||||
|
endIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ColorRGBA color = l.getColor();
|
||||||
|
//Color
|
||||||
|
lightData.setVector4InArray(color.getRed(),
|
||||||
|
color.getGreen(),
|
||||||
|
color.getBlue(),
|
||||||
|
l.getType().getId(),
|
||||||
|
lightDataIndex);
|
||||||
|
lightDataIndex++;
|
||||||
|
|
||||||
|
switch (l.getType()) {
|
||||||
|
case Directional:
|
||||||
|
DirectionalLight dl = (DirectionalLight) l;
|
||||||
|
Vector3f dir = dl.getDirection();
|
||||||
|
//Data directly sent in view space to avoid a matrix mult for each pixel
|
||||||
|
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
|
||||||
|
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||||
|
// tmpVec.divideLocal(tmpVec.w);
|
||||||
|
// tmpVec.normalizeLocal();
|
||||||
|
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
|
||||||
|
lightDataIndex++;
|
||||||
|
//PADDING
|
||||||
|
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex);
|
||||||
|
lightDataIndex++;
|
||||||
|
break;
|
||||||
|
case Point:
|
||||||
|
PointLight pl = (PointLight) l;
|
||||||
|
Vector3f pos = pl.getPosition();
|
||||||
|
float invRadius = pl.getInvRadius();
|
||||||
|
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
|
||||||
|
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||||
|
//tmpVec.divideLocal(tmpVec.w);
|
||||||
|
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
|
||||||
|
lightDataIndex++;
|
||||||
|
//PADDING
|
||||||
|
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex);
|
||||||
|
lightDataIndex++;
|
||||||
|
break;
|
||||||
|
case Spot:
|
||||||
|
SpotLight sl = (SpotLight) l;
|
||||||
|
Vector3f pos2 = sl.getPosition();
|
||||||
|
Vector3f dir2 = sl.getDirection();
|
||||||
|
float invRange = sl.getInvSpotRange();
|
||||||
|
float spotAngleCos = sl.getPackedAngleCos();
|
||||||
|
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f);
|
||||||
|
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||||
|
// tmpVec.divideLocal(tmpVec.w);
|
||||||
|
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
|
||||||
|
lightDataIndex++;
|
||||||
|
|
||||||
|
//We transform the spot direction in view space here to save 5 varying later in the lighting shader
|
||||||
|
//one vec4 less and a vec4 that becomes a vec3
|
||||||
|
//the downside is that spotAngleCos decoding happens now in the frag shader.
|
||||||
|
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f);
|
||||||
|
rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
|
||||||
|
tmpVec.normalizeLocal();
|
||||||
|
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
|
||||||
|
lightDataIndex++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
//Padding of unsued buffer space
|
||||||
|
while(lightDataIndex < numLights * 3) {
|
||||||
|
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
|
||||||
|
lightDataIndex++;
|
||||||
|
}
|
||||||
|
return curIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
|
||||||
|
int nbRenderedLights = 0;
|
||||||
|
Renderer renderer = renderManager.getRenderer();
|
||||||
|
int batchSize = renderManager.getSinglePassLightBatchSize();
|
||||||
|
if (lights.size() == 0) {
|
||||||
|
updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, 0);
|
||||||
|
renderer.setShader(shader);
|
||||||
|
renderMeshFromGeometry(renderer, geometry);
|
||||||
|
} else {
|
||||||
|
while (nbRenderedLights < lights.size()) {
|
||||||
|
nbRenderedLights = updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, nbRenderedLights);
|
||||||
|
renderer.setShader(shader);
|
||||||
|
renderMeshFromGeometry(renderer, geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.material.logic;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.light.DirectionalLight;
|
||||||
|
import com.jme3.light.Light;
|
||||||
|
import com.jme3.light.LightList;
|
||||||
|
import com.jme3.light.PointLight;
|
||||||
|
import com.jme3.light.SpotLight;
|
||||||
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.renderer.Caps;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.Renderer;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.shader.DefineList;
|
||||||
|
import com.jme3.shader.Shader;
|
||||||
|
import com.jme3.shader.Uniform;
|
||||||
|
import com.jme3.shader.VarType;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendering logic for static pass.
|
||||||
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
|
public final class StaticPassLightingLogic extends DefaultTechniqueDefLogic {
|
||||||
|
|
||||||
|
private static final String DEFINE_NUM_DIR_LIGHTS = "NUM_DIR_LIGHTS";
|
||||||
|
private static final String DEFINE_NUM_POINT_LIGHTS = "NUM_POINT_LIGHTS";
|
||||||
|
private static final String DEFINE_NUM_SPOT_LIGHTS = "NUM_SPOT_LIGHTS";
|
||||||
|
|
||||||
|
private final int numDirLightsDefineId;
|
||||||
|
private final int numPointLightsDefineId;
|
||||||
|
private final int numSpotLightsDefineId;
|
||||||
|
|
||||||
|
private final ArrayList<DirectionalLight> tempDirLights = new ArrayList<DirectionalLight>();
|
||||||
|
private final ArrayList<PointLight> tempPointLights = new ArrayList<PointLight>();
|
||||||
|
private final ArrayList<SpotLight> tempSpotLights = new ArrayList<SpotLight>();
|
||||||
|
|
||||||
|
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
|
||||||
|
private final Vector3f tempPosition = new Vector3f();
|
||||||
|
private final Vector3f tempDirection = new Vector3f();
|
||||||
|
|
||||||
|
public StaticPassLightingLogic(TechniqueDef techniqueDef) {
|
||||||
|
super(techniqueDef);
|
||||||
|
|
||||||
|
numDirLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_DIR_LIGHTS, VarType.Int);
|
||||||
|
numPointLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_POINT_LIGHTS, VarType.Int);
|
||||||
|
numSpotLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_SPOT_LIGHTS, VarType.Int);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
|
||||||
|
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
|
||||||
|
|
||||||
|
// TODO: if it ever changes that render isn't called
|
||||||
|
// right away with the same geometry after makeCurrent, it would be
|
||||||
|
// a problem.
|
||||||
|
// Do a radix sort.
|
||||||
|
tempDirLights.clear();
|
||||||
|
tempPointLights.clear();
|
||||||
|
tempSpotLights.clear();
|
||||||
|
for (Light light : lights) {
|
||||||
|
switch (light.getType()) {
|
||||||
|
case Directional:
|
||||||
|
tempDirLights.add((DirectionalLight) light);
|
||||||
|
break;
|
||||||
|
case Point:
|
||||||
|
tempPointLights.add((PointLight) light);
|
||||||
|
break;
|
||||||
|
case Spot:
|
||||||
|
tempSpotLights.add((SpotLight) light);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defines.set(numDirLightsDefineId, tempDirLights.size());
|
||||||
|
defines.set(numPointLightsDefineId, tempPointLights.size());
|
||||||
|
defines.set(numSpotLightsDefineId, tempSpotLights.size());
|
||||||
|
|
||||||
|
return techniqueDef.getShader(assetManager, rendererCaps, defines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void transformDirection(Matrix4f viewMatrix, Vector3f direction) {
|
||||||
|
viewMatrix.multNormal(direction, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void transformPosition(Matrix4f viewMatrix, Vector3f location) {
|
||||||
|
viewMatrix.mult(location, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLightListUniforms(Matrix4f viewMatrix, Shader shader, LightList lights) {
|
||||||
|
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
|
||||||
|
ambientColor.setValue(VarType.Vector4, getAmbientColor(lights, true, ambientLightColor));
|
||||||
|
|
||||||
|
Uniform lightData = shader.getUniform("g_LightData");
|
||||||
|
|
||||||
|
int totalSize = tempDirLights.size() * 2
|
||||||
|
+ tempPointLights.size() * 2
|
||||||
|
+ tempSpotLights.size() * 3;
|
||||||
|
lightData.setVector4Length(totalSize);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (DirectionalLight light : tempDirLights) {
|
||||||
|
ColorRGBA color = light.getColor();
|
||||||
|
tempDirection.set(light.getDirection());
|
||||||
|
transformDirection(viewMatrix, tempDirection);
|
||||||
|
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
|
||||||
|
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, 1f, index++);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PointLight light : tempPointLights) {
|
||||||
|
ColorRGBA color = light.getColor();
|
||||||
|
tempPosition.set(light.getPosition());
|
||||||
|
float invRadius = light.getInvRadius();
|
||||||
|
transformPosition(viewMatrix, tempPosition);
|
||||||
|
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
|
||||||
|
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRadius, index++);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SpotLight light : tempSpotLights) {
|
||||||
|
ColorRGBA color = light.getColor();
|
||||||
|
Vector3f pos = light.getPosition();
|
||||||
|
Vector3f dir = light.getDirection();
|
||||||
|
|
||||||
|
tempPosition.set(light.getPosition());
|
||||||
|
tempDirection.set(light.getDirection());
|
||||||
|
transformPosition(viewMatrix, tempPosition);
|
||||||
|
transformDirection(viewMatrix, tempDirection);
|
||||||
|
|
||||||
|
float invRange = light.getInvSpotRange();
|
||||||
|
float spotAngleCos = light.getPackedAngleCos();
|
||||||
|
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
|
||||||
|
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRange, index++);
|
||||||
|
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, spotAngleCos, index++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights) {
|
||||||
|
Renderer renderer = renderManager.getRenderer();
|
||||||
|
Matrix4f viewMatrix = renderManager.getCurrentCamera().getViewMatrix();
|
||||||
|
updateLightListUniforms(viewMatrix, shader, lights);
|
||||||
|
renderer.setShader(shader);
|
||||||
|
renderMeshFromGeometry(renderer, geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.material.logic;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.light.LightList;
|
||||||
|
import com.jme3.material.TechniqueDef.LightMode;
|
||||||
|
import com.jme3.renderer.Caps;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.shader.DefineList;
|
||||||
|
import com.jme3.shader.Shader;
|
||||||
|
import com.jme3.shader.Uniform;
|
||||||
|
import com.jme3.shader.UniformBinding;
|
||||||
|
import com.jme3.texture.Texture;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <code>TechniqueDefLogic</code> is used to customize how
|
||||||
|
* a material should be rendered.
|
||||||
|
*
|
||||||
|
* Typically used to implement {@link LightMode lighting modes}.
|
||||||
|
* Implementations can register
|
||||||
|
* {@link TechniqueDef#addShaderUnmappedDefine(java.lang.String) unmapped defines}
|
||||||
|
* in their constructor and then later set them based on the geometry
|
||||||
|
* or light environment being rendered.
|
||||||
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
|
public interface TechniqueDefLogic {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the shader to use for the given geometry / material combination.
|
||||||
|
*
|
||||||
|
* @param assetManager The asset manager to use for loading shader source code,
|
||||||
|
* shader nodes, and and lookup textures.
|
||||||
|
* @param renderManager The render manager for which rendering is to be performed.
|
||||||
|
* @param rendererCaps Renderer capabilities. The returned shader must
|
||||||
|
* support these capabilities.
|
||||||
|
* @param lights The lights with which the geometry shall be rendered. This
|
||||||
|
* list must not include culled lights.
|
||||||
|
* @param defines The define list used by the technique, any
|
||||||
|
* {@link TechniqueDef#addShaderUnmappedDefine(java.lang.String) unmapped defines}
|
||||||
|
* should be set here to change shader behavior.
|
||||||
|
*
|
||||||
|
* @return The shader to use for rendering.
|
||||||
|
*/
|
||||||
|
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
|
||||||
|
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that the <code>TechniqueDefLogic</code> renders the given geometry.
|
||||||
|
*
|
||||||
|
* Fixed material functionality such as {@link RenderState},
|
||||||
|
* {@link MatParam material parameters}, and
|
||||||
|
* {@link UniformBinding uniform bindings}
|
||||||
|
* have already been applied by the material, however,
|
||||||
|
* {@link RenderState}, {@link Uniform uniforms}, {@link Texture textures},
|
||||||
|
* can still be overriden.
|
||||||
|
*
|
||||||
|
* @param renderManager The render manager to perform the rendering against.
|
||||||
|
* * @param shader The shader that was selected by this logic in
|
||||||
|
* {@link #makeCurrent(com.jme3.asset.AssetManager, com.jme3.renderer.RenderManager, java.util.EnumSet, com.jme3.shader.DefineList)}.
|
||||||
|
* @param geometry The geometry to render
|
||||||
|
* @param lights Lights which influence the geometry.
|
||||||
|
*/
|
||||||
|
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights);
|
||||||
|
}
|
@ -1005,12 +1005,12 @@ public class Camera implements Savable, Cloneable {
|
|||||||
*
|
*
|
||||||
* NOTE: This method is used internally for culling, for public usage,
|
* NOTE: This method is used internally for culling, for public usage,
|
||||||
* the plane state of the bounding volume must be saved and restored, e.g:
|
* the plane state of the bounding volume must be saved and restored, e.g:
|
||||||
* <code>BoundingVolume bv;<br/>
|
* <code>BoundingVolume bv;<br>
|
||||||
* Camera c;<br/>
|
* Camera c;<br>
|
||||||
* int planeState = bv.getPlaneState();<br/>
|
* int planeState = bv.getPlaneState();<br>
|
||||||
* bv.setPlaneState(0);<br/>
|
* bv.setPlaneState(0);<br>
|
||||||
* c.contains(bv);<br/>
|
* c.contains(bv);<br>
|
||||||
* bv.setPlaneState(plateState);<br/>
|
* bv.setPlaneState(plateState);<br>
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* @param bound the bound to check for culling
|
* @param bound the bound to check for culling
|
||||||
|
@ -41,42 +41,25 @@ package com.jme3.renderer;
|
|||||||
*/
|
*/
|
||||||
public enum Limits {
|
public enum Limits {
|
||||||
/**
|
/**
|
||||||
* Maximum number of vertex texture units, or number of textures
|
* Maximum number of vertex texture units, or number of textures that can be
|
||||||
* that can be used in the vertex shader.
|
* used in the vertex shader.
|
||||||
*/
|
*/
|
||||||
VertexTextureUnits,
|
VertexTextureUnits,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of fragment texture units, or number of textures
|
* Maximum number of fragment texture units, or number of textures that can
|
||||||
* that can be used in the fragment shader.
|
* be used in the fragment shader.
|
||||||
*/
|
*/
|
||||||
FragmentTextureUnits,
|
FragmentTextureUnits,
|
||||||
|
FragmentUniformVectors,
|
||||||
FragmentUniforms,
|
|
||||||
|
|
||||||
VertexAttributes,
|
|
||||||
|
|
||||||
FrameBufferSamples,
|
|
||||||
|
|
||||||
FrameBufferAttachments,
|
|
||||||
|
|
||||||
FrameBufferMrtAttachments,
|
|
||||||
|
|
||||||
RenderBufferSize,
|
|
||||||
|
|
||||||
TextureSize,
|
|
||||||
|
|
||||||
CubemapSize,
|
|
||||||
|
|
||||||
VertexCount,
|
|
||||||
|
|
||||||
TriangleCount,
|
|
||||||
|
|
||||||
ColorTextureSamples,
|
|
||||||
|
|
||||||
DepthTextureSamples,
|
|
||||||
|
|
||||||
VertexUniformVectors,
|
VertexUniformVectors,
|
||||||
|
VertexAttributes,
|
||||||
|
FrameBufferSamples,
|
||||||
|
FrameBufferAttachments,
|
||||||
|
FrameBufferMrtAttachments,
|
||||||
|
RenderBufferSize,
|
||||||
|
TextureSize,
|
||||||
|
CubemapSize,
|
||||||
|
ColorTextureSamples,
|
||||||
|
DepthTextureSamples,
|
||||||
TextureAnisotropy,
|
TextureAnisotropy,
|
||||||
}
|
}
|
||||||
|
@ -55,16 +55,6 @@ public class RenderContext {
|
|||||||
*/
|
*/
|
||||||
public boolean depthTestEnabled = false;
|
public boolean depthTestEnabled = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @see RenderState#setAlphaFallOff(float)
|
|
||||||
*/
|
|
||||||
public float alphaTestFallOff = 0f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see RenderState#setAlphaTest(boolean)
|
|
||||||
*/
|
|
||||||
public boolean alphaTestEnabled = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see RenderState#setDepthWrite(boolean)
|
* @see RenderState#setDepthWrite(boolean)
|
||||||
*/
|
*/
|
||||||
@ -110,16 +100,21 @@ public class RenderContext {
|
|||||||
*/
|
*/
|
||||||
public RenderState.BlendMode blendMode = RenderState.BlendMode.Off;
|
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)
|
* @see RenderState#setWireframe(boolean)
|
||||||
*/
|
*/
|
||||||
public boolean wireframe = false;
|
public boolean wireframe = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @see RenderState#setPointSprite(boolean)
|
|
||||||
*/
|
|
||||||
public boolean pointSprite = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see Renderer#setShader(com.jme3.shader.Shader)
|
* @see Renderer#setShader(com.jme3.shader.Shader)
|
||||||
*/
|
*/
|
||||||
@ -261,7 +256,6 @@ public class RenderContext {
|
|||||||
public void reset(){
|
public void reset(){
|
||||||
cullMode = RenderState.FaceCullMode.Off;
|
cullMode = RenderState.FaceCullMode.Off;
|
||||||
depthTestEnabled = false;
|
depthTestEnabled = false;
|
||||||
alphaTestFallOff = 0f;
|
|
||||||
depthWriteEnabled = false;
|
depthWriteEnabled = false;
|
||||||
colorWriteEnabled = false;
|
colorWriteEnabled = false;
|
||||||
clipRectEnabled = false;
|
clipRectEnabled = false;
|
||||||
@ -270,6 +264,8 @@ public class RenderContext {
|
|||||||
polyOffsetUnits = 0;
|
polyOffsetUnits = 0;
|
||||||
pointSize = 1;
|
pointSize = 1;
|
||||||
blendMode = RenderState.BlendMode.Off;
|
blendMode = RenderState.BlendMode.Off;
|
||||||
|
blendEquation = RenderState.BlendEquation.Add;
|
||||||
|
blendEquationAlpha = RenderState.BlendEquationAlpha.InheritColor;
|
||||||
wireframe = false;
|
wireframe = false;
|
||||||
boundShaderProgram = 0;
|
boundShaderProgram = 0;
|
||||||
boundShader = null;
|
boundShader = null;
|
||||||
|
@ -34,8 +34,13 @@ package com.jme3.renderer;
|
|||||||
import com.jme3.light.DefaultLightFilter;
|
import com.jme3.light.DefaultLightFilter;
|
||||||
import com.jme3.light.LightFilter;
|
import com.jme3.light.LightFilter;
|
||||||
import com.jme3.light.LightList;
|
import com.jme3.light.LightList;
|
||||||
import com.jme3.material.*;
|
import com.jme3.material.MatParamOverride;
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.material.MaterialDef;
|
||||||
|
import com.jme3.material.RenderState;
|
||||||
|
import com.jme3.material.Technique;
|
||||||
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.math.*;
|
||||||
import com.jme3.post.SceneProcessor;
|
import com.jme3.post.SceneProcessor;
|
||||||
import com.jme3.profile.AppProfiler;
|
import com.jme3.profile.AppProfiler;
|
||||||
import com.jme3.profile.AppStep;
|
import com.jme3.profile.AppStep;
|
||||||
@ -45,13 +50,12 @@ import com.jme3.renderer.queue.RenderQueue;
|
|||||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||||
import com.jme3.scene.*;
|
import com.jme3.scene.*;
|
||||||
import com.jme3.shader.Uniform;
|
import com.jme3.shader.Shader;
|
||||||
import com.jme3.shader.UniformBinding;
|
import com.jme3.shader.UniformBinding;
|
||||||
import com.jme3.shader.UniformBindingManager;
|
import com.jme3.shader.UniformBindingManager;
|
||||||
import com.jme3.system.NullRenderer;
|
import com.jme3.system.NullRenderer;
|
||||||
import com.jme3.system.Timer;
|
import com.jme3.system.Timer;
|
||||||
import com.jme3.util.SafeArrayList;
|
import com.jme3.util.SafeArrayList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -70,25 +74,26 @@ import java.util.logging.Logger;
|
|||||||
public class RenderManager {
|
public class RenderManager {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
|
private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
|
||||||
private Renderer renderer;
|
private final Renderer renderer;
|
||||||
private UniformBindingManager uniformBindingManager = new UniformBindingManager();
|
private final UniformBindingManager uniformBindingManager = new UniformBindingManager();
|
||||||
private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
|
private final ArrayList<ViewPort> preViewPorts = new ArrayList<>();
|
||||||
private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
|
private final ArrayList<ViewPort> viewPorts = new ArrayList<>();
|
||||||
private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
|
private final ArrayList<ViewPort> postViewPorts = new ArrayList<>();
|
||||||
private Camera prevCam = null;
|
private Camera prevCam = null;
|
||||||
private Material forcedMaterial = null;
|
private Material forcedMaterial = null;
|
||||||
private String forcedTechnique = null;
|
private String forcedTechnique = null;
|
||||||
private RenderState forcedRenderState = null;
|
private RenderState forcedRenderState = null;
|
||||||
|
private final List<MatParamOverride> forcedOverrides = new ArrayList<>();
|
||||||
private int viewX, viewY, viewWidth, viewHeight;
|
private int viewX, viewY, viewWidth, viewHeight;
|
||||||
private Matrix4f orthoMatrix = new Matrix4f();
|
private final Matrix4f orthoMatrix = new Matrix4f();
|
||||||
private LightList filteredLightList = new LightList(null);
|
private final LightList filteredLightList = new LightList(null);
|
||||||
private String tmpTech;
|
|
||||||
private boolean handleTranlucentBucket = true;
|
private boolean handleTranlucentBucket = true;
|
||||||
private AppProfiler prof;
|
private AppProfiler prof;
|
||||||
private LightFilter lightFilter = new DefaultLightFilter();
|
private LightFilter lightFilter = new DefaultLightFilter();
|
||||||
private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass;
|
private TechniqueDef.LightMode preferredLightMode = TechniqueDef.LightMode.MultiPass;
|
||||||
private int singlePassLightBatchSize = 1;
|
private int singlePassLightBatchSize = 1;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a high-level rendering interface over the
|
* Create a high-level rendering interface over the
|
||||||
* low-level rendering interface.
|
* low-level rendering interface.
|
||||||
@ -423,6 +428,44 @@ public class RenderManager {
|
|||||||
this.forcedTechnique = forcedTechnique;
|
this.forcedTechnique = forcedTechnique;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a forced material parameter to use when rendering geometries.
|
||||||
|
* <p>
|
||||||
|
* The provided parameter takes precedence over parameters set on the
|
||||||
|
* material or any overrides that exist in the scene graph that have the
|
||||||
|
* same name.
|
||||||
|
*
|
||||||
|
* @param override The override to add
|
||||||
|
* @see MatParamOverride
|
||||||
|
* @see #removeForcedMatParam(com.jme3.material.MatParamOverride)
|
||||||
|
*/
|
||||||
|
public void addForcedMatParam(MatParamOverride override) {
|
||||||
|
forcedOverrides.add(override);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a forced material parameter previously added.
|
||||||
|
*
|
||||||
|
* @param override The override to remove.
|
||||||
|
* @see #addForcedMatParam(com.jme3.material.MatParamOverride)
|
||||||
|
*/
|
||||||
|
public void removeForcedMatParam(MatParamOverride override) {
|
||||||
|
forcedOverrides.remove(override);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the forced material parameters applied to rendered geometries.
|
||||||
|
* <p>
|
||||||
|
* Forced parameters can be added via
|
||||||
|
* {@link #addForcedMatParam(com.jme3.material.MatParamOverride)} or removed
|
||||||
|
* via {@link #removeForcedMatParam(com.jme3.material.MatParamOverride)}.
|
||||||
|
*
|
||||||
|
* @return The forced material parameters.
|
||||||
|
*/
|
||||||
|
public List<MatParamOverride> getForcedMatParams() {
|
||||||
|
return forcedOverrides;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable or disable alpha-to-coverage.
|
* Enable or disable alpha-to-coverage.
|
||||||
* <p>
|
* <p>
|
||||||
@ -480,8 +523,8 @@ public class RenderManager {
|
|||||||
* Updates the given list of uniforms with {@link UniformBinding uniform bindings}
|
* Updates the given list of uniforms with {@link UniformBinding uniform bindings}
|
||||||
* based on the current world state.
|
* based on the current world state.
|
||||||
*/
|
*/
|
||||||
public void updateUniformBindings(List<Uniform> params) {
|
public void updateUniformBindings(Shader shader) {
|
||||||
uniformBindingManager.updateUniformBindings(params);
|
uniformBindingManager.updateUniformBindings(shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -508,45 +551,54 @@ public class RenderManager {
|
|||||||
* for rendering the material, and the material's own render state is ignored.
|
* for rendering the material, and the material's own render state is ignored.
|
||||||
* Otherwise, the material's render state is used as intended.
|
* Otherwise, the material's render state is used as intended.
|
||||||
*
|
*
|
||||||
* @param g The geometry to render
|
* @param geom The geometry to render
|
||||||
*
|
*
|
||||||
* @see Technique
|
* @see Technique
|
||||||
* @see RenderState
|
* @see RenderState
|
||||||
* @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
|
* @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
|
||||||
* @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
|
* @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
|
||||||
*/
|
*/
|
||||||
public void renderGeometry(Geometry g) {
|
public void renderGeometry(Geometry geom) {
|
||||||
if (g.isIgnoreTransform()) {
|
if (geom.isIgnoreTransform()) {
|
||||||
setWorldMatrix(Matrix4f.IDENTITY);
|
setWorldMatrix(Matrix4f.IDENTITY);
|
||||||
} else {
|
} else {
|
||||||
setWorldMatrix(g.getWorldMatrix());
|
setWorldMatrix(geom.getWorldMatrix());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform light filtering if we have a light filter.
|
// Perform light filtering if we have a light filter.
|
||||||
LightList lightList = g.getWorldLightList();
|
LightList lightList = geom.getWorldLightList();
|
||||||
|
|
||||||
if (lightFilter != null) {
|
if (lightFilter != null) {
|
||||||
filteredLightList.clear();
|
filteredLightList.clear();
|
||||||
lightFilter.filterLights(g, filteredLightList);
|
lightFilter.filterLights(geom, filteredLightList);
|
||||||
lightList = filteredLightList;
|
lightList = filteredLightList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Material material = geom.getMaterial();
|
||||||
|
|
||||||
//if forcedTechnique we try to force it for render,
|
//if forcedTechnique we try to force it for render,
|
||||||
//if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
|
//if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
|
||||||
//else the geom is not rendered
|
//else the geom is not rendered
|
||||||
if (forcedTechnique != null) {
|
if (forcedTechnique != null) {
|
||||||
if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
|
MaterialDef matDef = material.getMaterialDef();
|
||||||
tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
|
if (matDef.getTechniqueDefs(forcedTechnique) != null) {
|
||||||
g.getMaterial().selectTechnique(forcedTechnique, this);
|
|
||||||
|
Technique activeTechnique = material.getActiveTechnique();
|
||||||
|
|
||||||
|
String previousTechniqueName = activeTechnique != null
|
||||||
|
? activeTechnique.getDef().getName()
|
||||||
|
: TechniqueDef.DEFAULT_TECHNIQUE_NAME;
|
||||||
|
|
||||||
|
geom.getMaterial().selectTechnique(forcedTechnique, this);
|
||||||
//saving forcedRenderState for future calls
|
//saving forcedRenderState for future calls
|
||||||
RenderState tmpRs = forcedRenderState;
|
RenderState tmpRs = forcedRenderState;
|
||||||
if (g.getMaterial().getActiveTechnique().getDef().getForcedRenderState() != null) {
|
if (geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState() != null) {
|
||||||
//forcing forced technique renderState
|
//forcing forced technique renderState
|
||||||
forcedRenderState = g.getMaterial().getActiveTechnique().getDef().getForcedRenderState();
|
forcedRenderState = geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState();
|
||||||
}
|
}
|
||||||
// use geometry's material
|
// use geometry's material
|
||||||
g.getMaterial().render(g, lightList, this);
|
material.render(geom, lightList, this);
|
||||||
g.getMaterial().selectTechnique(tmpTech, this);
|
material.selectTechnique(previousTechniqueName, this);
|
||||||
|
|
||||||
//restoring forcedRenderState
|
//restoring forcedRenderState
|
||||||
forcedRenderState = tmpRs;
|
forcedRenderState = tmpRs;
|
||||||
@ -555,13 +607,13 @@ public class RenderManager {
|
|||||||
//If forcedTechnique does not exists, and forcedMaterial is not set, the geom MUST NOT be rendered
|
//If forcedTechnique does not exists, and forcedMaterial is not set, the geom MUST NOT be rendered
|
||||||
} else if (forcedMaterial != null) {
|
} else if (forcedMaterial != null) {
|
||||||
// use forced material
|
// use forced material
|
||||||
forcedMaterial.render(g, lightList, this);
|
forcedMaterial.render(geom, lightList, this);
|
||||||
}
|
}
|
||||||
} else if (forcedMaterial != null) {
|
} else if (forcedMaterial != null) {
|
||||||
// use forced material
|
// use forced material
|
||||||
forcedMaterial.render(g, lightList, this);
|
forcedMaterial.render(geom, lightList, this);
|
||||||
} else {
|
} else {
|
||||||
g.getMaterial().render(g, lightList, this);
|
material.render(geom, lightList, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,7 +664,9 @@ public class RenderManager {
|
|||||||
|
|
||||||
gm.getMaterial().preload(this);
|
gm.getMaterial().preload(this);
|
||||||
Mesh mesh = gm.getMesh();
|
Mesh mesh = gm.getMesh();
|
||||||
if (mesh != null) {
|
if (mesh != null
|
||||||
|
&& mesh.getVertexCount() != 0
|
||||||
|
&& mesh.getTriangleCount() != 0) {
|
||||||
for (VertexBuffer vb : mesh.getBufferList().getArray()) {
|
for (VertexBuffer vb : mesh.getBufferList().getArray()) {
|
||||||
if (vb.getData() != null && vb.getUsage() != VertexBuffer.Usage.CpuOnly) {
|
if (vb.getData() != null && vb.getUsage() != VertexBuffer.Usage.CpuOnly) {
|
||||||
renderer.updateBufferData(vb);
|
renderer.updateBufferData(vb);
|
||||||
@ -637,8 +691,10 @@ public class RenderManager {
|
|||||||
* <p>
|
* <p>
|
||||||
* In addition to enqueuing the visible geometries, this method
|
* In addition to enqueuing the visible geometries, this method
|
||||||
* also scenes which cast or receive shadows, by putting them into the
|
* also scenes which cast or receive shadows, by putting them into the
|
||||||
* RenderQueue's {@link RenderQueue#renderShadowQueue(GeometryList, RenderManager, Camera, boolean) shadow queue}.
|
* RenderQueue's
|
||||||
* Each Spatial which has its {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
|
* {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
|
||||||
|
* shadow queue}. Each Spatial which has its
|
||||||
|
* {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
|
||||||
* set to not off, will be put into the appropriate shadow queue, note that
|
* set to not off, will be put into the appropriate shadow queue, note that
|
||||||
* this process does not check for frustum culling on any
|
* this process does not check for frustum culling on any
|
||||||
* {@link ShadowMode#Cast shadow casters}, as they don't have to be
|
* {@link ShadowMode#Cast shadow casters}, as they don't have to be
|
||||||
@ -985,7 +1041,8 @@ public class RenderManager {
|
|||||||
* (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
|
* (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
|
||||||
* <li>If any objects remained in the render queue, they are removed
|
* <li>If any objects remained in the render queue, they are removed
|
||||||
* from the queue. This is generally objects added to the
|
* from the queue. This is generally objects added to the
|
||||||
* {@link RenderQueue#renderShadowQueue(GeometryList, RenderManager, Camera, boolean) shadow queue}
|
* {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
|
||||||
|
* shadow queue}
|
||||||
* which were not rendered because of a missing shadow renderer.</li>
|
* which were not rendered because of a missing shadow renderer.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
|
@ -43,6 +43,7 @@ import com.jme3.texture.Image;
|
|||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.util.NativeObject;
|
import com.jme3.util.NativeObject;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,6 +67,13 @@ public interface Renderer {
|
|||||||
*/
|
*/
|
||||||
public EnumSet<Caps> getCaps();
|
public EnumSet<Caps> getCaps();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the limits of the renderer.
|
||||||
|
*
|
||||||
|
* @return The limits of the renderer.
|
||||||
|
*/
|
||||||
|
public EnumMap<Limits, Integer> getLimits();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The statistics allow tracking of how data
|
* The statistics allow tracking of how data
|
||||||
* per frame, such as number of objects rendered, number of triangles, etc.
|
* per frame, such as number of objects rendered, number of triangles, etc.
|
||||||
@ -303,6 +311,20 @@ public interface Renderer {
|
|||||||
*/
|
*/
|
||||||
public void cleanup();
|
public void cleanup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default anisotropic filter level for textures.
|
||||||
|
*
|
||||||
|
* If the
|
||||||
|
* {@link Texture#setAnisotropicFilter(int) texture anisotropic filter} is
|
||||||
|
* set to 0, then the default level is used. Otherwise if the texture level
|
||||||
|
* is 1 or greater, then the texture's value overrides the default value.
|
||||||
|
*
|
||||||
|
* @param level The default anisotropic filter level to use. Default: 1.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException If level is less than 1.
|
||||||
|
*/
|
||||||
|
public void setDefaultAnisotropicFilter(int level);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the alpha to coverage state.
|
* Sets the alpha to coverage state.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -71,6 +71,9 @@ public interface GL {
|
|||||||
public static final int GL_FLOAT = 0x1406;
|
public static final int GL_FLOAT = 0x1406;
|
||||||
public static final int GL_FRAGMENT_SHADER = 0x8B30;
|
public static final int GL_FRAGMENT_SHADER = 0x8B30;
|
||||||
public static final int GL_FRONT = 0x404;
|
public static final int GL_FRONT = 0x404;
|
||||||
|
public static final int GL_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_FRONT_AND_BACK = 0x408;
|
||||||
public static final int GL_GEQUAL = 0x206;
|
public static final int GL_GEQUAL = 0x206;
|
||||||
public static final int GL_GREATER = 0x204;
|
public static final int GL_GREATER = 0x204;
|
||||||
@ -95,7 +98,10 @@ public interface GL {
|
|||||||
public static final int GL_LINK_STATUS = 0x8B82;
|
public static final int GL_LINK_STATUS = 0x8B82;
|
||||||
public static final int GL_LUMINANCE = 0x1909;
|
public static final int GL_LUMINANCE = 0x1909;
|
||||||
public static final int GL_LUMINANCE_ALPHA = 0x190A;
|
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_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_IMAGE_UNITS = 0x8872;
|
||||||
public static final int GL_MAX_TEXTURE_SIZE = 0xD33;
|
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_ATTRIBS = 0x8869;
|
||||||
@ -103,6 +109,7 @@ public interface GL {
|
|||||||
public static final int GL_MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A;
|
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_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB;
|
||||||
public static final int GL_MIRRORED_REPEAT = 0x8370;
|
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 = 0x2600;
|
||||||
public static final int GL_NEAREST_MIPMAP_LINEAR = 0x2702;
|
public static final int GL_NEAREST_MIPMAP_LINEAR = 0x2702;
|
||||||
public static final int GL_NEAREST_MIPMAP_NEAREST = 0x2700;
|
public static final int GL_NEAREST_MIPMAP_NEAREST = 0x2700;
|
||||||
@ -188,6 +195,7 @@ public interface GL {
|
|||||||
public void glAttachShader(int program, int shader);
|
public void glAttachShader(int program, int shader);
|
||||||
public void glBindBuffer(int target, int buffer);
|
public void glBindBuffer(int target, int buffer);
|
||||||
public void glBindTexture(int target, int texture);
|
public void glBindTexture(int target, int texture);
|
||||||
|
public void glBlendEquationSeparate(int colorMode, int alphaMode);
|
||||||
public void glBlendFunc(int sfactor, int dfactor);
|
public void glBlendFunc(int sfactor, int dfactor);
|
||||||
public void glBufferData(int target, long data_size, int usage);
|
public void glBufferData(int target, long data_size, int usage);
|
||||||
public void glBufferData(int target, FloatBuffer data, int usage);
|
public void glBufferData(int target, FloatBuffer data, int usage);
|
||||||
|
@ -100,4 +100,9 @@ public class GLDebugDesktop extends GLDebugES implements GL2, GL3, GL4 {
|
|||||||
gl3.glFramebufferTextureLayer(param1, param2, param3, param4, param5);
|
gl3.glFramebufferTextureLayer(param1, param2, param3, param4, param5);
|
||||||
checkError();
|
checkError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void glBlendEquationSeparate(int colorMode, int alphaMode) {
|
||||||
|
gl.glBlendEquationSeparate(colorMode, alphaMode);
|
||||||
|
checkError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -560,4 +560,9 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
|
|||||||
checkError();
|
checkError();
|
||||||
return sync;
|
return sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void glBlendEquationSeparate(int colorMode, int alphaMode) {
|
||||||
|
gl.glBlendEquationSeparate(colorMode, alphaMode);
|
||||||
|
checkError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,7 @@ public final class GLRenderer implements Renderer {
|
|||||||
private final Statistics statistics = new Statistics();
|
private final Statistics statistics = new Statistics();
|
||||||
private int vpX, vpY, vpW, vpH;
|
private int vpX, vpY, vpW, vpH;
|
||||||
private int clipX, clipY, clipW, clipH;
|
private int clipX, clipY, clipW, clipH;
|
||||||
|
private int defaultAnisotropicFilter = 1;
|
||||||
private boolean linearizeSrgbImages;
|
private boolean linearizeSrgbImages;
|
||||||
private HashSet<String> extensions;
|
private HashSet<String> extensions;
|
||||||
|
|
||||||
@ -252,18 +253,14 @@ public final class GLRenderer implements Renderer {
|
|||||||
|
|
||||||
limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS));
|
limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS));
|
||||||
|
|
||||||
// gl.glGetInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16);
|
|
||||||
// vertexUniforms = intBuf16.get(0);
|
|
||||||
// logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
|
|
||||||
//
|
|
||||||
// gl.glGetInteger(GL.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16);
|
|
||||||
// fragUniforms = intBuf16.get(0);
|
|
||||||
// logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
|
|
||||||
if (caps.contains(Caps.OpenGLES20)) {
|
if (caps.contains(Caps.OpenGLES20)) {
|
||||||
|
limits.put(Limits.FragmentUniformVectors, getInteger(GL.GL_MAX_FRAGMENT_UNIFORM_VECTORS));
|
||||||
limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_VECTORS));
|
limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_VECTORS));
|
||||||
} else {
|
} else {
|
||||||
|
limits.put(Limits.FragmentUniformVectors, getInteger(GL.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS) / 4);
|
||||||
limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4);
|
limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
limits.put(Limits.VertexAttributes, getInteger(GL.GL_MAX_VERTEX_ATTRIBS));
|
limits.put(Limits.VertexAttributes, getInteger(GL.GL_MAX_VERTEX_ATTRIBS));
|
||||||
limits.put(Limits.TextureSize, getInteger(GL.GL_MAX_TEXTURE_SIZE));
|
limits.put(Limits.TextureSize, getInteger(GL.GL_MAX_TEXTURE_SIZE));
|
||||||
limits.put(Limits.CubemapSize, getInteger(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE));
|
limits.put(Limits.CubemapSize, getInteger(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE));
|
||||||
@ -474,6 +471,17 @@ public final class GLRenderer implements Renderer {
|
|||||||
{
|
{
|
||||||
sb.append("\t").append(cap.toString()).append("\n");
|
sb.append("\t").append(cap.toString()).append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sb.append("\nHardware limits: \n");
|
||||||
|
for (Limits limit : Limits.values()) {
|
||||||
|
Integer value = limits.get(limit);
|
||||||
|
if (value == null) {
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
sb.append("\t").append(limit.name()).append(" = ")
|
||||||
|
.append(value).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
logger.log(Level.FINE, sb.toString());
|
logger.log(Level.FINE, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,7 +530,6 @@ public final class GLRenderer implements Renderer {
|
|||||||
gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
|
gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||||
if (!caps.contains(Caps.CoreProfile)) {
|
if (!caps.contains(Caps.CoreProfile)) {
|
||||||
gl2.glEnable(GL2.GL_POINT_SPRITE);
|
gl2.glEnable(GL2.GL_POINT_SPRITE);
|
||||||
context.pointSprite = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -594,6 +601,14 @@ public final class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDefaultAnisotropicFilter(int level) {
|
||||||
|
if (level < 1) {
|
||||||
|
throw new IllegalArgumentException("level cannot be less than 1");
|
||||||
|
}
|
||||||
|
this.defaultAnisotropicFilter = level;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAlphaToCoverage(boolean value) {
|
public void setAlphaToCoverage(boolean value) {
|
||||||
if (caps.contains(Caps.Multisample)) {
|
if (caps.contains(Caps.Multisample)) {
|
||||||
if (value) {
|
if (value) {
|
||||||
@ -735,6 +750,19 @@ public final class GLRenderer implements Renderer {
|
|||||||
throw new UnsupportedOperationException("Unrecognized blend mode: "
|
throw new UnsupportedOperationException("Unrecognized blend mode: "
|
||||||
+ state.getBlendMode());
|
+ state.getBlendMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.getBlendEquation() != context.blendEquation || state.getBlendEquationAlpha() != context.blendEquationAlpha) {
|
||||||
|
int colorMode = convertBlendEquation(state.getBlendEquation());
|
||||||
|
int alphaMode;
|
||||||
|
if (state.getBlendEquationAlpha() == RenderState.BlendEquationAlpha.InheritColor) {
|
||||||
|
alphaMode = colorMode;
|
||||||
|
} else {
|
||||||
|
alphaMode = convertBlendEquationAlpha(state.getBlendEquationAlpha());
|
||||||
|
}
|
||||||
|
gl.glBlendEquationSeparate(colorMode, alphaMode);
|
||||||
|
context.blendEquation = state.getBlendEquation();
|
||||||
|
context.blendEquationAlpha = state.getBlendEquationAlpha();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.blendMode = state.getBlendMode();
|
context.blendMode = state.getBlendMode();
|
||||||
@ -785,6 +813,41 @@ public final class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int convertBlendEquation(RenderState.BlendEquation blendEquation) {
|
||||||
|
switch (blendEquation) {
|
||||||
|
case Add:
|
||||||
|
return GL2.GL_FUNC_ADD;
|
||||||
|
case Subtract:
|
||||||
|
return GL2.GL_FUNC_SUBTRACT;
|
||||||
|
case ReverseSubtract:
|
||||||
|
return GL2.GL_FUNC_REVERSE_SUBTRACT;
|
||||||
|
case Min:
|
||||||
|
return GL2.GL_MIN;
|
||||||
|
case Max:
|
||||||
|
return GL2.GL_MAX;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("Unrecognized blend operation: " + blendEquation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int convertBlendEquationAlpha(RenderState.BlendEquationAlpha blendEquationAlpha) {
|
||||||
|
//Note: InheritColor mode should already be handled, that is why it does not belong the the switch case.
|
||||||
|
switch (blendEquationAlpha) {
|
||||||
|
case Add:
|
||||||
|
return GL2.GL_FUNC_ADD;
|
||||||
|
case Subtract:
|
||||||
|
return GL2.GL_FUNC_SUBTRACT;
|
||||||
|
case ReverseSubtract:
|
||||||
|
return GL2.GL_FUNC_REVERSE_SUBTRACT;
|
||||||
|
case Min:
|
||||||
|
return GL2.GL_MIN;
|
||||||
|
case Max:
|
||||||
|
return GL2.GL_MAX;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("Unrecognized alpha blend operation: " + blendEquationAlpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int convertStencilOperation(StencilOperation stencilOp) {
|
private int convertStencilOperation(StencilOperation stencilOp) {
|
||||||
switch (stencilOp) {
|
switch (stencilOp) {
|
||||||
case Keep:
|
case Keep:
|
||||||
@ -964,12 +1027,12 @@ public final class GLRenderer implements Renderer {
|
|||||||
gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE);
|
gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE);
|
||||||
break;
|
break;
|
||||||
case Matrix3:
|
case Matrix3:
|
||||||
fb = (FloatBuffer) uniform.getValue();
|
fb = uniform.getMultiData();
|
||||||
assert fb.remaining() == 9;
|
assert fb.remaining() == 9;
|
||||||
gl.glUniformMatrix3(loc, false, fb);
|
gl.glUniformMatrix3(loc, false, fb);
|
||||||
break;
|
break;
|
||||||
case Matrix4:
|
case Matrix4:
|
||||||
fb = (FloatBuffer) uniform.getValue();
|
fb = uniform.getMultiData();
|
||||||
assert fb.remaining() == 16;
|
assert fb.remaining() == 16;
|
||||||
gl.glUniformMatrix4(loc, false, fb);
|
gl.glUniformMatrix4(loc, false, fb);
|
||||||
break;
|
break;
|
||||||
@ -978,23 +1041,23 @@ public final class GLRenderer implements Renderer {
|
|||||||
gl.glUniform1(loc, ib);
|
gl.glUniform1(loc, ib);
|
||||||
break;
|
break;
|
||||||
case FloatArray:
|
case FloatArray:
|
||||||
fb = (FloatBuffer) uniform.getValue();
|
fb = uniform.getMultiData();
|
||||||
gl.glUniform1(loc, fb);
|
gl.glUniform1(loc, fb);
|
||||||
break;
|
break;
|
||||||
case Vector2Array:
|
case Vector2Array:
|
||||||
fb = (FloatBuffer) uniform.getValue();
|
fb = uniform.getMultiData();
|
||||||
gl.glUniform2(loc, fb);
|
gl.glUniform2(loc, fb);
|
||||||
break;
|
break;
|
||||||
case Vector3Array:
|
case Vector3Array:
|
||||||
fb = (FloatBuffer) uniform.getValue();
|
fb = uniform.getMultiData();
|
||||||
gl.glUniform3(loc, fb);
|
gl.glUniform3(loc, fb);
|
||||||
break;
|
break;
|
||||||
case Vector4Array:
|
case Vector4Array:
|
||||||
fb = (FloatBuffer) uniform.getValue();
|
fb = uniform.getMultiData();
|
||||||
gl.glUniform4(loc, fb);
|
gl.glUniform4(loc, fb);
|
||||||
break;
|
break;
|
||||||
case Matrix4Array:
|
case Matrix4Array:
|
||||||
fb = (FloatBuffer) uniform.getValue();
|
fb = uniform.getMultiData();
|
||||||
gl.glUniformMatrix4(loc, false, fb);
|
gl.glUniformMatrix4(loc, false, fb);
|
||||||
break;
|
break;
|
||||||
case Int:
|
case Int:
|
||||||
@ -1872,13 +1935,18 @@ public final class GLRenderer implements Renderer {
|
|||||||
gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, convertMinFilter(tex.getMinFilter(), haveMips));
|
gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, convertMinFilter(tex.getMinFilter(), haveMips));
|
||||||
curState.minFilter = tex.getMinFilter();
|
curState.minFilter = tex.getMinFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int desiredAnisoFilter = tex.getAnisotropicFilter() == 0
|
||||||
|
? defaultAnisotropicFilter
|
||||||
|
: tex.getAnisotropicFilter();
|
||||||
|
|
||||||
if (caps.contains(Caps.TextureFilterAnisotropic)
|
if (caps.contains(Caps.TextureFilterAnisotropic)
|
||||||
&& curState.anisoFilter != tex.getAnisotropicFilter()) {
|
&& curState.anisoFilter != desiredAnisoFilter) {
|
||||||
bindTextureAndUnit(target, image, unit);
|
bindTextureAndUnit(target, image, unit);
|
||||||
gl.glTexParameterf(target,
|
gl.glTexParameterf(target,
|
||||||
GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
||||||
tex.getAnisotropicFilter());
|
desiredAnisoFilter);
|
||||||
curState.anisoFilter = tex.getAnisotropicFilter();
|
curState.anisoFilter = desiredAnisoFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (tex.getType()) {
|
switch (tex.getType()) {
|
||||||
@ -2689,12 +2757,15 @@ public final class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
|
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
|
||||||
if (mesh.getVertexCount() == 0) {
|
if (mesh.getVertexCount() == 0 || mesh.getTriangleCount() == 0 || count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//this is kept for backward compatibility.
|
if (count > 1 && !caps.contains(Caps.MeshInstancing)) {
|
||||||
if (mesh.getLineWidth() != -1 && context.lineWidth != mesh.getLineWidth()) {
|
throw new RendererException("Mesh instancing is not supported by the video hardware");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesh.getLineWidth() != 1f && context.lineWidth != mesh.getLineWidth()) {
|
||||||
gl.glLineWidth(mesh.getLineWidth());
|
gl.glLineWidth(mesh.getLineWidth());
|
||||||
context.lineWidth = mesh.getLineWidth();
|
context.lineWidth = mesh.getLineWidth();
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,16 @@ public class GeometryList implements Iterable<Geometry>{
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the element at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to set
|
||||||
|
* @param value The value
|
||||||
|
*/
|
||||||
|
public void set(int index, Geometry value) {
|
||||||
|
geometries[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the element at the given index.
|
* Returns the element at the given index.
|
||||||
*
|
*
|
||||||
|
@ -69,11 +69,12 @@ public class OpaqueComparator implements GeometryComparator {
|
|||||||
return spat.queueDistance;
|
return spat.queueDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int compare(Geometry o1, Geometry o2) {
|
public int compare(Geometry o1, Geometry o2) {
|
||||||
Material m1 = o1.getMaterial();
|
Material m1 = o1.getMaterial();
|
||||||
Material m2 = o2.getMaterial();
|
Material m2 = o2.getMaterial();
|
||||||
|
|
||||||
int compareResult = m2.getSortId() - m1.getSortId();
|
int compareResult = Integer.compare(m1.getSortId(), m2.getSortId());
|
||||||
if (compareResult == 0){
|
if (compareResult == 0){
|
||||||
// use the same shader.
|
// use the same shader.
|
||||||
// sort front-to-back then.
|
// sort front-to-back then.
|
||||||
|
@ -175,7 +175,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
|
private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
|
||||||
private VertexBuffer[] lodLevels;
|
private VertexBuffer[] lodLevels;
|
||||||
private float pointSize = 1;
|
private float pointSize = 1;
|
||||||
private float lineWidth = -1;
|
private float lineWidth = 1;
|
||||||
|
|
||||||
private transient int vertexArrayID = -1;
|
private transient int vertexArrayID = -1;
|
||||||
|
|
||||||
@ -589,28 +589,26 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of points for point meshes
|
* @deprecated Always returns <code>1.0</code> since point size is
|
||||||
|
* determined in the vertex shader.
|
||||||
*
|
*
|
||||||
* @return the size of points
|
* @return <code>1.0</code>
|
||||||
*
|
*
|
||||||
* @see #setPointSize(float)
|
* @see #setPointSize(float)
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public float getPointSize() {
|
public float getPointSize() {
|
||||||
return pointSize;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the size of points for meshes of mode {@link Mode#Points}.
|
* @deprecated Does nothing, since the size of {@link Mode#Points points} is
|
||||||
* The point size is specified as on-screen pixels, the default
|
* determined via the vertex shader's <code>gl_PointSize</code> output.
|
||||||
* value is 1.0. The point size
|
|
||||||
* does nothing if {@link RenderState#setPointSprite(boolean) point sprite}
|
|
||||||
* render state is enabled, in that case, the vertex shader must specify the
|
|
||||||
* point size by writing to <code>gl_PointSize</code>.
|
|
||||||
*
|
*
|
||||||
* @param pointSize The size of points
|
* @param pointSize ignored
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setPointSize(float pointSize) {
|
public void setPointSize(float pointSize) {
|
||||||
this.pointSize = pointSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -634,6 +632,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setLineWidth(float lineWidth) {
|
public void setLineWidth(float lineWidth) {
|
||||||
|
if (lineWidth < 1f) {
|
||||||
|
throw new IllegalArgumentException("lineWidth must be greater than or equal to 1.0");
|
||||||
|
}
|
||||||
this.lineWidth = lineWidth;
|
this.lineWidth = lineWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,6 @@ public class Node extends Spatial {
|
|||||||
* requiresUpdate() method.
|
* requiresUpdate() method.
|
||||||
*/
|
*/
|
||||||
private SafeArrayList<Spatial> updateList = null;
|
private SafeArrayList<Spatial> updateList = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* False if the update list requires rebuilding. This is Node.class
|
* False if the update list requires rebuilding. This is Node.class
|
||||||
* specific and therefore not included as part of the Spatial update flags.
|
* specific and therefore not included as part of the Spatial update flags.
|
||||||
@ -100,7 +99,6 @@ public class Node extends Spatial {
|
|||||||
*/
|
*/
|
||||||
public Node(String name) {
|
public Node(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
|
|
||||||
// For backwards compatibility, only clear the "requires
|
// For backwards compatibility, only clear the "requires
|
||||||
// update" flag if we are not a subclass of Node.
|
// update" flag if we are not a subclass of Node.
|
||||||
// This prevents subclass from silently failing to receive
|
// This prevents subclass from silently failing to receive
|
||||||
@ -141,10 +139,21 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setMatParamOverrideRefresh() {
|
||||||
|
super.setMatParamOverrideRefresh();
|
||||||
|
for (Spatial child : children.getArray()) {
|
||||||
|
if ((child.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
child.setMatParamOverrideRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateWorldBound(){
|
protected void updateWorldBound(){
|
||||||
super.updateWorldBound();
|
super.updateWorldBound();
|
||||||
|
|
||||||
// for a node, the world bound is a combination of all it's children
|
// for a node, the world bound is a combination of all it's children
|
||||||
// bounds
|
// bounds
|
||||||
BoundingVolume resultBound = null;
|
BoundingVolume resultBound = null;
|
||||||
@ -239,19 +248,19 @@ public class Node extends Spatial {
|
|||||||
// This branch has no geometric state that requires updates.
|
// This branch has no geometric state that requires updates.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((refreshFlags & RF_LIGHTLIST) != 0){
|
if ((refreshFlags & RF_LIGHTLIST) != 0){
|
||||||
updateWorldLightList();
|
updateWorldLightList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((refreshFlags & RF_TRANSFORM) != 0){
|
if ((refreshFlags & RF_TRANSFORM) != 0){
|
||||||
// combine with parent transforms- same for all spatial
|
// combine with parent transforms- same for all spatial
|
||||||
// subclasses.
|
// subclasses.
|
||||||
updateWorldTransforms();
|
updateWorldTransforms();
|
||||||
}
|
}
|
||||||
|
if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||||
|
updateMatParamOverrides();
|
||||||
|
}
|
||||||
|
|
||||||
refreshFlags &= ~RF_CHILD_LIGHTLIST;
|
refreshFlags &= ~RF_CHILD_LIGHTLIST;
|
||||||
|
|
||||||
if (!children.isEmpty()) {
|
if (!children.isEmpty()) {
|
||||||
// the important part- make sure child geometric state is refreshed
|
// the important part- make sure child geometric state is refreshed
|
||||||
// first before updating own world bound. This saves
|
// first before updating own world bound. This saves
|
||||||
@ -287,7 +296,6 @@ public class Node extends Spatial {
|
|||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>getVertexCount</code> returns the number of vertices contained
|
* <code>getVertexCount</code> returns the number of vertices contained
|
||||||
* in all sub-branches of this node that contain geometry.
|
* in all sub-branches of this node that contain geometry.
|
||||||
@ -321,7 +329,6 @@ public class Node extends Spatial {
|
|||||||
public int attachChild(Spatial child) {
|
public int attachChild(Spatial child) {
|
||||||
return attachChildAt(child, children.size());
|
return attachChildAt(child, children.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* <code>attachChildAt</code> attaches a child to this node at an index. This node
|
* <code>attachChildAt</code> attaches a child to this node at an index. This node
|
||||||
@ -345,20 +352,18 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
child.setParent(this);
|
child.setParent(this);
|
||||||
children.add(index, child);
|
children.add(index, child);
|
||||||
|
|
||||||
// XXX: Not entirely correct? Forces bound update up the
|
// XXX: Not entirely correct? Forces bound update up the
|
||||||
// tree stemming from the attached child. Also forces
|
// tree stemming from the attached child. Also forces
|
||||||
// transform update down the tree-
|
// transform update down the tree-
|
||||||
child.setTransformRefresh();
|
child.setTransformRefresh();
|
||||||
child.setLightListRefresh();
|
child.setLightListRefresh();
|
||||||
|
child.setMatParamOverrideRefresh();
|
||||||
if (logger.isLoggable(Level.FINE)) {
|
if (logger.isLoggable(Level.FINE)) {
|
||||||
logger.log(Level.FINE,"Child ({0}) attached to this node ({1})",
|
logger.log(Level.FINE,"Child ({0}) attached to this node ({1})",
|
||||||
new Object[]{child.getName(), getName()});
|
new Object[]{child.getName(), getName()});
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateUpdateList();
|
invalidateUpdateList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return children.size();
|
return children.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,6 +438,7 @@ public class Node extends Spatial {
|
|||||||
child.setTransformRefresh();
|
child.setTransformRefresh();
|
||||||
// lights are also inherited from parent
|
// lights are also inherited from parent
|
||||||
child.setLightListRefresh();
|
child.setLightListRefresh();
|
||||||
|
child.setMatParamOverrideRefresh();
|
||||||
|
|
||||||
invalidateUpdateList();
|
invalidateUpdateList();
|
||||||
}
|
}
|
||||||
@ -519,7 +525,6 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* determines if the provided Spatial is contained in the children list of
|
* determines if the provided Spatial is contained in the children list of
|
||||||
* this node.
|
* this node.
|
||||||
@ -567,39 +572,32 @@ public class Node extends Spatial {
|
|||||||
|
|
||||||
public int collideWith(Collidable other, CollisionResults results){
|
public int collideWith(Collidable other, CollisionResults results){
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
||||||
// optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
|
// optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
|
||||||
// number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
|
// number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
|
||||||
// The idea is when there are few children, it can be too expensive to test boundingVolume first.
|
// The idea is when there are few children, it can be too expensive to test boundingVolume first.
|
||||||
/*
|
/*
|
||||||
I'm removing this change until some issues can be addressed and I really
|
I'm removing this change until some issues can be addressed and I really
|
||||||
think it needs to be implemented a better way anyway.
|
think it needs to be implemented a better way anyway.
|
||||||
|
|
||||||
First, it causes issues for anyone doing collideWith() with BoundingVolumes
|
First, it causes issues for anyone doing collideWith() with BoundingVolumes
|
||||||
and expecting it to trickle down to the children. For example, children
|
and expecting it to trickle down to the children. For example, children
|
||||||
with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing
|
with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing
|
||||||
a collision check at the parent level then has to do a BoundingSphere to BoundingBox
|
a collision check at the parent level then has to do a BoundingSphere to BoundingBox
|
||||||
collision which isn't resolved. (Having to come up with a collision point in that
|
collision which isn't resolved. (Having to come up with a collision point in that
|
||||||
case is tricky and the first sign that this is the wrong approach.)
|
case is tricky and the first sign that this is the wrong approach.)
|
||||||
|
|
||||||
Second, the rippling changes this caused to 'optimize' collideWith() for this
|
Second, the rippling changes this caused to 'optimize' collideWith() for this
|
||||||
special use-case are another sign that this approach was a bit dodgy. The whole
|
special use-case are another sign that this approach was a bit dodgy. The whole
|
||||||
idea of calculating a full collision just to see if the two shapes collide at all
|
idea of calculating a full collision just to see if the two shapes collide at all
|
||||||
is very wasteful.
|
is very wasteful.
|
||||||
|
|
||||||
A proper implementation should support a simpler boolean check that doesn't do
|
A proper implementation should support a simpler boolean check that doesn't do
|
||||||
all of that calculation. For example, if 'other' is also a BoundingVolume (ie: 99.9%
|
all of that calculation. For example, if 'other' is also a BoundingVolume (ie: 99.9%
|
||||||
of all non-Ray cases) then a direct BV to BV intersects() test can be done. So much
|
of all non-Ray cases) then a direct BV to BV intersects() test can be done. So much
|
||||||
faster. And if 'other' _is_ a Ray then the BV.intersects(Ray) call can be done.
|
faster. And if 'other' _is_ a Ray then the BV.intersects(Ray) call can be done.
|
||||||
|
|
||||||
I don't have time to do it right now but I'll at least un-break a bunch of peoples'
|
I don't have time to do it right now but I'll at least un-break a bunch of peoples'
|
||||||
code until it can be 'optimized' properly. Hopefully it's not too late to back out
|
code until it can be 'optimized' properly. Hopefully it's not too late to back out
|
||||||
the other dodgy ripples this caused. -pspeed (hindsight-expert ;))
|
the other dodgy ripples this caused. -pspeed (hindsight-expert ;))
|
||||||
|
|
||||||
Note: the code itself is relatively simple to implement but I don't have time to
|
Note: the code itself is relatively simple to implement but I don't have time to
|
||||||
a) test it, and b) see if '> 4' is still a decent check for it. Could be it's fast
|
a) test it, and b) see if '> 4' is still a decent check for it. Could be it's fast
|
||||||
enough to do all the time for > 1.
|
enough to do all the time for > 1.
|
||||||
|
|
||||||
if (children.size() > 4)
|
if (children.size() > 4)
|
||||||
{
|
{
|
||||||
BoundingVolume bv = this.getWorldBound();
|
BoundingVolume bv = this.getWorldBound();
|
||||||
@ -692,7 +690,6 @@ public class Node extends Spatial {
|
|||||||
// Reset the fields of the clone that should be in a 'new' state.
|
// Reset the fields of the clone that should be in a 'new' state.
|
||||||
nodeClone.updateList = null;
|
nodeClone.updateList = null;
|
||||||
nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone()
|
nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone()
|
||||||
|
|
||||||
return nodeClone;
|
return nodeClone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,7 +729,6 @@ public class Node extends Spatial {
|
|||||||
// cloning this list is fine.
|
// cloning this list is fine.
|
||||||
this.updateList = cloner.clone(updateList);
|
this.updateList = cloner.clone(updateList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter e) throws IOException {
|
public void write(JmeExporter e) throws IOException {
|
||||||
super.write(e);
|
super.write(e);
|
||||||
@ -744,7 +740,6 @@ public class Node extends Spatial {
|
|||||||
// XXX: Load children before loading itself!!
|
// XXX: Load children before loading itself!!
|
||||||
// This prevents empty children list if controls query
|
// This prevents empty children list if controls query
|
||||||
// it in Control.setSpatial().
|
// it in Control.setSpatial().
|
||||||
|
|
||||||
children = new SafeArrayList( Spatial.class,
|
children = new SafeArrayList( Spatial.class,
|
||||||
e.getCapsule(this).readSavableArrayList("children", null) );
|
e.getCapsule(this).readSavableArrayList("children", null) );
|
||||||
|
|
||||||
@ -754,7 +749,6 @@ public class Node extends Spatial {
|
|||||||
child.parent = this;
|
child.parent = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.read(e);
|
super.read(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,7 +769,6 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void depthFirstTraversal(SceneGraphVisitor visitor) {
|
public void depthFirstTraversal(SceneGraphVisitor visitor) {
|
||||||
for (Spatial child : children.getArray()) {
|
for (Spatial child : children.getArray()) {
|
||||||
@ -783,7 +776,6 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
visitor.visit(this);
|
visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
|
protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
|
||||||
queue.addAll(children);
|
queue.addAll(children);
|
||||||
|
@ -38,6 +38,7 @@ import com.jme3.collision.Collidable;
|
|||||||
import com.jme3.export.*;
|
import com.jme3.export.*;
|
||||||
import com.jme3.light.Light;
|
import com.jme3.light.Light;
|
||||||
import com.jme3.light.LightList;
|
import com.jme3.light.LightList;
|
||||||
|
import com.jme3.material.MatParamOverride;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.math.*;
|
import com.jme3.math.*;
|
||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
@ -122,7 +123,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
|
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
|
||||||
RF_BOUND = 0x02,
|
RF_BOUND = 0x02,
|
||||||
RF_LIGHTLIST = 0x04, // changes in light lists
|
RF_LIGHTLIST = 0x04, // changes in light lists
|
||||||
RF_CHILD_LIGHTLIST = 0x08; // some child need geometry update
|
RF_CHILD_LIGHTLIST = 0x08, // some child need geometry update
|
||||||
|
RF_MATPARAM_OVERRIDE = 0x10;
|
||||||
|
|
||||||
protected CullHint cullHint = CullHint.Inherit;
|
protected CullHint cullHint = CullHint.Inherit;
|
||||||
protected BatchHint batchHint = BatchHint.Inherit;
|
protected BatchHint batchHint = BatchHint.Inherit;
|
||||||
@ -135,6 +137,10 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
*/
|
*/
|
||||||
protected LightList localLights;
|
protected LightList localLights;
|
||||||
protected transient LightList worldLights;
|
protected transient LightList worldLights;
|
||||||
|
|
||||||
|
protected ArrayList<MatParamOverride> localOverrides;
|
||||||
|
protected ArrayList<MatParamOverride> worldOverrides;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This spatial's name.
|
* This spatial's name.
|
||||||
*/
|
*/
|
||||||
@ -195,13 +201,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
*/
|
*/
|
||||||
protected Spatial(String name) {
|
protected Spatial(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
localTransform = new Transform();
|
localTransform = new Transform();
|
||||||
worldTransform = new Transform();
|
worldTransform = new Transform();
|
||||||
|
|
||||||
localLights = new LightList(this);
|
localLights = new LightList(this);
|
||||||
worldLights = new LightList(this);
|
worldLights = new LightList(this);
|
||||||
|
|
||||||
|
localOverrides = new ArrayList<>();
|
||||||
|
worldOverrides = new ArrayList<>();
|
||||||
refreshFlags |= RF_BOUND;
|
refreshFlags |= RF_BOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +229,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
boolean requiresUpdates() {
|
boolean requiresUpdates() {
|
||||||
return requiresUpdates | !controls.isEmpty();
|
return requiresUpdates | !controls.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses can call this with true to denote that they require
|
* Subclasses can call this with true to denote that they require
|
||||||
* updateLogicalState() to be called even if they contain no controls.
|
* updateLogicalState() to be called even if they contain no controls.
|
||||||
@ -272,35 +278,33 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
|
|
||||||
protected void setLightListRefresh() {
|
protected void setLightListRefresh() {
|
||||||
refreshFlags |= RF_LIGHTLIST;
|
refreshFlags |= RF_LIGHTLIST;
|
||||||
|
|
||||||
// Make sure next updateGeometricState() visits this branch
|
// Make sure next updateGeometricState() visits this branch
|
||||||
// to update lights.
|
// to update lights.
|
||||||
Spatial p = parent;
|
Spatial p = parent;
|
||||||
while (p != null) {
|
while (p != null) {
|
||||||
//if (p.refreshFlags != 0) {
|
|
||||||
// any refresh flag is sufficient,
|
|
||||||
// as each propagates to the root Node
|
|
||||||
|
|
||||||
// 2015/2/8:
|
|
||||||
// This is not true, because using e.g. getWorldBound()
|
|
||||||
// or getWorldTransform() activates a "partial refresh"
|
|
||||||
// which does not update the lights but does clear
|
|
||||||
// the refresh flags on the ancestors!
|
|
||||||
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if ((p.refreshFlags & RF_CHILD_LIGHTLIST) != 0) {
|
if ((p.refreshFlags & RF_CHILD_LIGHTLIST) != 0) {
|
||||||
// The parent already has this flag,
|
// The parent already has this flag,
|
||||||
// so must all ancestors.
|
// so must all ancestors.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.refreshFlags |= RF_CHILD_LIGHTLIST;
|
p.refreshFlags |= RF_CHILD_LIGHTLIST;
|
||||||
p = p.parent;
|
p = p.parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setMatParamOverrideRefresh() {
|
||||||
|
refreshFlags |= RF_MATPARAM_OVERRIDE;
|
||||||
|
Spatial p = parent;
|
||||||
|
while (p != null) {
|
||||||
|
if ((p.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.refreshFlags |= RF_MATPARAM_OVERRIDE;
|
||||||
|
p = p.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicate that the bounding of this spatial has changed and that
|
* Indicate that the bounding of this spatial has changed and that
|
||||||
* a refresh is required.
|
* a refresh is required.
|
||||||
@ -318,7 +322,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
p = p.parent;
|
p = p.parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (Internal use only) Forces a refresh of the given types of data.
|
* (Internal use only) Forces a refresh of the given types of data.
|
||||||
*
|
*
|
||||||
@ -424,6 +427,29 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
return worldLights;
|
return worldLights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the local material parameter overrides.
|
||||||
|
*
|
||||||
|
* @return The list of local material parameter overrides.
|
||||||
|
*/
|
||||||
|
public List<MatParamOverride> getLocalMatParamOverrides() {
|
||||||
|
return localOverrides;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the world material parameter overrides.
|
||||||
|
*
|
||||||
|
* Note that this list is only updated on a call to
|
||||||
|
* {@link #updateGeometricState()}. After update, the world overrides list
|
||||||
|
* will contain the {@link #getParent() parent's} world overrides combined
|
||||||
|
* with this spatial's {@link #getLocalMatParamOverrides() local overrides}.
|
||||||
|
*
|
||||||
|
* @return The list of world material parameter overrides.
|
||||||
|
*/
|
||||||
|
public List<MatParamOverride> getWorldMatParamOverrides() {
|
||||||
|
return worldOverrides;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>getWorldRotation</code> retrieves the absolute rotation of the
|
* <code>getWorldRotation</code> retrieves the absolute rotation of the
|
||||||
* Spatial.
|
* Spatial.
|
||||||
@ -525,10 +551,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
TempVars vars = TempVars.get();
|
TempVars vars = TempVars.get();
|
||||||
|
|
||||||
Vector3f compVecA = vars.vect4;
|
Vector3f compVecA = vars.vect4;
|
||||||
|
|
||||||
compVecA.set(position).subtractLocal(worldTranslation);
|
compVecA.set(position).subtractLocal(worldTranslation);
|
||||||
getLocalRotation().lookAt(compVecA, upVector);
|
getLocalRotation().lookAt(compVecA, upVector);
|
||||||
|
|
||||||
if ( getParent() != null ) {
|
if ( getParent() != null ) {
|
||||||
Quaternion rot=vars.quat1;
|
Quaternion rot=vars.quat1;
|
||||||
rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation());
|
rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation());
|
||||||
@ -555,13 +579,61 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
worldLights.update(localLights, null);
|
worldLights.update(localLights, null);
|
||||||
refreshFlags &= ~RF_LIGHTLIST;
|
refreshFlags &= ~RF_LIGHTLIST;
|
||||||
} else {
|
} else {
|
||||||
if ((parent.refreshFlags & RF_LIGHTLIST) == 0) {
|
assert (parent.refreshFlags & RF_LIGHTLIST) == 0;
|
||||||
worldLights.update(localLights, parent.worldLights);
|
worldLights.update(localLights, parent.worldLights);
|
||||||
refreshFlags &= ~RF_LIGHTLIST;
|
refreshFlags &= ~RF_LIGHTLIST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateMatParamOverrides() {
|
||||||
|
refreshFlags &= ~RF_MATPARAM_OVERRIDE;
|
||||||
|
|
||||||
|
worldOverrides.clear();
|
||||||
|
if (parent == null) {
|
||||||
|
worldOverrides.addAll(localOverrides);
|
||||||
} else {
|
} else {
|
||||||
assert false;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -696,7 +768,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
controls.add(control);
|
controls.add(control);
|
||||||
control.setSpatial(this);
|
control.setSpatial(this);
|
||||||
boolean after = requiresUpdates();
|
boolean after = requiresUpdates();
|
||||||
|
|
||||||
// If the requirement to be updated has changed
|
// If the requirement to be updated has changed
|
||||||
// then we need to let the parent node know so it
|
// then we need to let the parent node know so it
|
||||||
// can rebuild its update list.
|
// can rebuild its update list.
|
||||||
@ -720,7 +791,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean after = requiresUpdates();
|
boolean after = requiresUpdates();
|
||||||
|
|
||||||
// If the requirement to be updated has changed
|
// If the requirement to be updated has changed
|
||||||
// then we need to let the parent node know so it
|
// then we need to let the parent node know so it
|
||||||
// can rebuild its update list.
|
// can rebuild its update list.
|
||||||
@ -746,14 +816,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean after = requiresUpdates();
|
boolean after = requiresUpdates();
|
||||||
|
|
||||||
// If the requirement to be updated has changed
|
// If the requirement to be updated has changed
|
||||||
// then we need to let the parent node know so it
|
// then we need to let the parent node know so it
|
||||||
// can rebuild its update list.
|
// can rebuild its update list.
|
||||||
if( parent != null && before != after ) {
|
if( parent != null && before != after ) {
|
||||||
parent.invalidateUpdateList();
|
parent.invalidateUpdateList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -838,6 +906,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
if ((refreshFlags & RF_BOUND) != 0) {
|
if ((refreshFlags & RF_BOUND) != 0) {
|
||||||
updateWorldBound();
|
updateWorldBound();
|
||||||
}
|
}
|
||||||
|
if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||||
|
updateMatParamOverrides();
|
||||||
|
}
|
||||||
|
|
||||||
assert refreshFlags == 0;
|
assert refreshFlags == 0;
|
||||||
}
|
}
|
||||||
@ -1292,6 +1363,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
// the transforms and stuff get refreshed.
|
// the transforms and stuff get refreshed.
|
||||||
clone.setTransformRefresh();
|
clone.setTransformRefresh();
|
||||||
clone.setLightListRefresh();
|
clone.setLightListRefresh();
|
||||||
|
clone.setMatParamOverrideRefresh();
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
@ -1312,6 +1384,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
clone.localLights.setOwner(clone);
|
clone.localLights.setOwner(clone);
|
||||||
clone.worldLights.setOwner(clone);
|
clone.worldLights.setOwner(clone);
|
||||||
|
|
||||||
|
clone.worldOverrides = new ArrayList<MatParamOverride>();
|
||||||
|
clone.localOverrides = new ArrayList<MatParamOverride>();
|
||||||
|
|
||||||
|
for (MatParamOverride override : localOverrides) {
|
||||||
|
clone.localOverrides.add((MatParamOverride) override.clone());
|
||||||
|
}
|
||||||
|
|
||||||
// No need to force cloned to update.
|
// No need to force cloned to update.
|
||||||
// This node already has the refresh flags
|
// This node already has the refresh flags
|
||||||
// set below so it will have to update anyway.
|
// set below so it will have to update anyway.
|
||||||
@ -1333,6 +1412,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
clone.setBoundRefresh();
|
clone.setBoundRefresh();
|
||||||
clone.setTransformRefresh();
|
clone.setTransformRefresh();
|
||||||
clone.setLightListRefresh();
|
clone.setLightListRefresh();
|
||||||
|
clone.setMatParamOverrideRefresh();
|
||||||
|
|
||||||
clone.controls = new SafeArrayList<Control>(Control.class);
|
clone.controls = new SafeArrayList<Control>(Control.class);
|
||||||
for (int i = 0; i < controls.size(); i++) {
|
for (int i = 0; i < controls.size(); i++) {
|
||||||
@ -1388,6 +1468,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
// the transforms and stuff get refreshed.
|
// the transforms and stuff get refreshed.
|
||||||
clone.setTransformRefresh();
|
clone.setTransformRefresh();
|
||||||
clone.setLightListRefresh();
|
clone.setLightListRefresh();
|
||||||
|
clone.setMatParamOverrideRefresh();
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
@ -1419,6 +1500,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
this.localLights = cloner.clone(localLights);
|
this.localLights = cloner.clone(localLights);
|
||||||
this.worldTransform = cloner.clone(worldTransform);
|
this.worldTransform = cloner.clone(worldTransform);
|
||||||
this.localTransform = cloner.clone(localTransform);
|
this.localTransform = cloner.clone(localTransform);
|
||||||
|
this.worldOverrides = cloner.clone(worldOverrides);
|
||||||
|
this.localOverrides = cloner.clone(localOverrides);
|
||||||
this.controls = cloner.clone(controls);
|
this.controls = cloner.clone(controls);
|
||||||
|
|
||||||
// Cloner doesn't handle maps on its own just yet.
|
// Cloner doesn't handle maps on its own just yet.
|
||||||
@ -1515,6 +1598,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
|
capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
|
||||||
capsule.write(localTransform, "transform", Transform.IDENTITY);
|
capsule.write(localTransform, "transform", Transform.IDENTITY);
|
||||||
capsule.write(localLights, "lights", null);
|
capsule.write(localLights, "lights", null);
|
||||||
|
capsule.writeSavableArrayList(localOverrides, "overrides", null);
|
||||||
|
|
||||||
// Shallow clone the controls array to convert its type.
|
// Shallow clone the controls array to convert its type.
|
||||||
capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null);
|
capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null);
|
||||||
@ -1538,6 +1622,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
localLights = (LightList) ic.readSavable("lights", null);
|
localLights = (LightList) ic.readSavable("lights", null);
|
||||||
localLights.setOwner(this);
|
localLights.setOwner(this);
|
||||||
|
|
||||||
|
localOverrides = ic.readSavableArrayList("overrides", null);
|
||||||
|
if (localOverrides == null) {
|
||||||
|
localOverrides = new ArrayList<>();
|
||||||
|
}
|
||||||
|
worldOverrides = new ArrayList<>();
|
||||||
|
|
||||||
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
|
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
|
||||||
//the AnimControl creates the SkeletonControl for old files and add it to the spatial.
|
//the AnimControl creates the SkeletonControl for old files and add it to the spatial.
|
||||||
//The SkeletonControl must be the last in the stack so we add the list of all other control before it.
|
//The SkeletonControl must be the last in the stack so we add the list of all other control before it.
|
||||||
|
@ -108,8 +108,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
* Do not use.
|
* Do not use.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
MiscAttrib,
|
Reserved0,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the index buffer, must contain integer data
|
* Specifies the index buffer, must contain integer data
|
||||||
* (ubyte, ushort, or uint).
|
* (ubyte, ushort, or uint).
|
||||||
@ -522,6 +521,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
|
|||||||
// throw new UnsupportedOperationException("Data has already been sent. Cannot set usage.");
|
// throw new UnsupportedOperationException("Data has already been sent. Cannot set usage.");
|
||||||
|
|
||||||
this.usage = usage;
|
this.usage = usage;
|
||||||
|
this.setUpdateNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,14 +102,6 @@ public class WireBox extends Mesh {
|
|||||||
updateBound();
|
updateBound();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Old method retained for compatibility: use makeGeometry instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void fromBoundingBox(BoundingBox bbox) {
|
|
||||||
updatePositions(bbox.getXExtent(), bbox.getYExtent(), bbox.getZExtent());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a geometry suitable for visualizing the specified bounding box.
|
* Create a geometry suitable for visualizing the specified bounding box.
|
||||||
*
|
*
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -31,256 +31,149 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.shader;
|
package com.jme3.shader;
|
||||||
|
|
||||||
import com.jme3.export.*;
|
import java.util.Arrays;
|
||||||
import com.jme3.material.MatParam;
|
import java.util.List;
|
||||||
import com.jme3.material.TechniqueDef;
|
|
||||||
import com.jme3.util.ListMap;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
/**
|
||||||
import java.util.Map;
|
* The new define list.
|
||||||
import java.util.TreeMap;
|
*
|
||||||
|
* @author Kirill Vainer
|
||||||
|
*/
|
||||||
|
public final class DefineList {
|
||||||
|
|
||||||
public final class DefineList implements Savable, Cloneable {
|
public static final int MAX_DEFINES = 64;
|
||||||
|
|
||||||
private static final String ONE = "1";
|
private long hash;
|
||||||
|
private final int[] vals;
|
||||||
|
|
||||||
private TreeMap<String, String> defines = new TreeMap<String, String>();
|
public DefineList(int numValues) {
|
||||||
private String compiled = null;
|
if (numValues < 0 || numValues > MAX_DEFINES) {
|
||||||
private int cachedHashCode = 0;
|
throw new IllegalArgumentException("numValues must be between 0 and 64");
|
||||||
|
}
|
||||||
public void write(JmeExporter ex) throws IOException{
|
vals = new int[numValues];
|
||||||
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);
|
private DefineList(DefineList original) {
|
||||||
oc.write(vals, "vals", null);
|
this.hash = original.hash;
|
||||||
|
this.vals = new int[original.vals.length];
|
||||||
|
System.arraycopy(original.vals, 0, vals, 0, vals.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void read(JmeImporter im) throws IOException{
|
public void set(int id, int val) {
|
||||||
InputCapsule ic = im.getCapsule(this);
|
assert 0 <= id && id < 64;
|
||||||
|
if (val != 0) {
|
||||||
|
hash |= (1L << id);
|
||||||
|
} else {
|
||||||
|
hash &= ~(1L << id);
|
||||||
|
}
|
||||||
|
vals[id] = val;
|
||||||
|
}
|
||||||
|
|
||||||
String[] keys = ic.readStringArray("keys", null);
|
public void set(int id, float val) {
|
||||||
String[] vals = ic.readStringArray("vals", null);
|
set(id, Float.floatToIntBits(val));
|
||||||
for (int i = 0; i < keys.length; i++){
|
}
|
||||||
defines.put(keys[i], vals[i]);
|
|
||||||
|
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() {
|
public void clear() {
|
||||||
defines.clear();
|
hash = 0;
|
||||||
compiled = "";
|
Arrays.fill(vals, 0);
|
||||||
cachedHashCode = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String get(String key){
|
public boolean getBoolean(int id) {
|
||||||
return defines.get(key);
|
return vals[id] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public float getFloat(int id) {
|
||||||
public DefineList clone() {
|
return Float.intBitsToFloat(vals[id]);
|
||||||
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){
|
public int getInt(int id) {
|
||||||
if (val == null){
|
return vals[id];
|
||||||
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
if (cachedHashCode == 0) {
|
return (int)((hash >> 32) ^ hash);
|
||||||
cachedHashCode = defines.hashCode();
|
|
||||||
}
|
|
||||||
return cachedHashCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public boolean equals(Object other) {
|
||||||
StringBuilder sb = new StringBuilder();
|
DefineList o = (DefineList) other;
|
||||||
int i = 0;
|
if (hash == o.hash) {
|
||||||
for (Map.Entry<String, String> entry : defines.entrySet()) {
|
for (int i = 0; i < vals.length; i++) {
|
||||||
sb.append(entry.getKey()).append("=").append(entry.getValue());
|
if (vals[i] != o.vals[i]) return false;
|
||||||
if (i != defines.size() - 1) {
|
|
||||||
sb.append(", ");
|
|
||||||
}
|
}
|
||||||
i++;
|
return true;
|
||||||
}
|
}
|
||||||
return sb.toString();
|
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.
|
* A list of all shader sources currently attached.
|
||||||
*/
|
*/
|
||||||
private ArrayList<ShaderSource> shaderSourceList;
|
private final ArrayList<ShaderSource> shaderSourceList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps uniform name to the uniform variable.
|
* Maps uniform name to the uniform variable.
|
||||||
*/
|
*/
|
||||||
private ListMap<String, Uniform> uniforms;
|
private final ListMap<String, Uniform> uniforms;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uniforms bound to {@link UniformBinding}s.
|
||||||
|
*
|
||||||
|
* Managed by the {@link UniformBindingManager}.
|
||||||
|
*/
|
||||||
|
private final ArrayList<Uniform> boundUniforms;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps attribute name to the location of the attribute in the shader.
|
* Maps attribute name to the location of the attribute in the shader.
|
||||||
*/
|
*/
|
||||||
private IntMap<Attribute> attribs;
|
private final IntMap<Attribute> attribs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of shader. The shader will control the pipeline of it's type.
|
* Type of shader. The shader will control the pipeline of it's type.
|
||||||
*/
|
*/
|
||||||
public static enum ShaderType {
|
public static enum ShaderType {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Control fragment rasterization. (e.g color of pixel).
|
* Control fragment rasterization. (e.g color of pixel).
|
||||||
*/
|
*/
|
||||||
Fragment,
|
Fragment("frag"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Control vertex processing. (e.g transform of model to clip space)
|
* Control vertex processing. (e.g transform of model to clip space)
|
||||||
*/
|
*/
|
||||||
Vertex,
|
Vertex("vert"),
|
||||||
|
/**
|
||||||
|
* Control geometry assembly. (e.g compile a triangle list from input
|
||||||
|
* data)
|
||||||
|
*/
|
||||||
|
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");
|
||||||
|
|
||||||
/**
|
private String extension;
|
||||||
* Control geometry assembly. (e.g compile a triangle list from input data)
|
|
||||||
*/
|
public String getExtension() {
|
||||||
Geometry,
|
return extension;
|
||||||
/**
|
}
|
||||||
* Controls tesselation factor (e.g how often a input patch should be subdivided)
|
|
||||||
*/
|
private ShaderType(String extension) {
|
||||||
TessellationControl,
|
this.extension = extension;
|
||||||
/**
|
}
|
||||||
* Controls tesselation transform (e.g similar to the vertex shader, but required to mix inputs manual)
|
|
||||||
*/
|
|
||||||
TessellationEvaluation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,22 +214,16 @@ public final class Shader extends NativeObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the shader for use, must be called after the
|
|
||||||
* constructor without arguments is used.
|
|
||||||
*/
|
|
||||||
public void initialize() {
|
|
||||||
shaderSourceList = new ArrayList<ShaderSource>();
|
|
||||||
uniforms = new ListMap<String, Uniform>();
|
|
||||||
attribs = new IntMap<Attribute>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new shader, {@link #initialize() } must be called
|
* Creates a new shader, {@link #initialize() } must be called
|
||||||
* after this constructor for the shader to be usable.
|
* after this constructor for the shader to be usable.
|
||||||
*/
|
*/
|
||||||
public Shader(){
|
public Shader(){
|
||||||
super();
|
super();
|
||||||
|
shaderSourceList = new ArrayList<ShaderSource>();
|
||||||
|
uniforms = new ListMap<String, Uniform>();
|
||||||
|
attribs = new IntMap<Attribute>();
|
||||||
|
boundUniforms = new ArrayList<Uniform>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -225,6 +238,10 @@ public final class Shader extends NativeObject {
|
|||||||
for (ShaderSource source : s.shaderSourceList){
|
for (ShaderSource source : s.shaderSourceList){
|
||||||
shaderSourceList.add( (ShaderSource)source.createDestructableClone() );
|
shaderSourceList.add( (ShaderSource)source.createDestructableClone() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uniforms = null;
|
||||||
|
boundUniforms = null;
|
||||||
|
attribs = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -248,6 +265,18 @@ public final class Shader extends NativeObject {
|
|||||||
setUpdateNeeded();
|
setUpdateNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addUniformBinding(UniformBinding binding){
|
||||||
|
String uniformName = "g_" + binding.name();
|
||||||
|
Uniform uniform = uniforms.get(uniformName);
|
||||||
|
if (uniform == null) {
|
||||||
|
uniform = new Uniform();
|
||||||
|
uniform.name = uniformName;
|
||||||
|
uniform.binding = binding;
|
||||||
|
uniforms.put(uniformName, uniform);
|
||||||
|
boundUniforms.add(uniform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Uniform getUniform(String name){
|
public Uniform getUniform(String name){
|
||||||
assert name.startsWith("m_") || name.startsWith("g_");
|
assert name.startsWith("m_") || name.startsWith("g_");
|
||||||
Uniform uniform = uniforms.get(name);
|
Uniform uniform = uniforms.get(name);
|
||||||
@ -278,6 +307,10 @@ public final class Shader extends NativeObject {
|
|||||||
return uniforms;
|
return uniforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ArrayList<Uniform> getBoundUniforms() {
|
||||||
|
return boundUniforms;
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<ShaderSource> getSources(){
|
public Collection<ShaderSource> getSources(){
|
||||||
return shaderSourceList;
|
return shaderSourceList;
|
||||||
}
|
}
|
||||||
|
@ -57,9 +57,9 @@ public abstract class ShaderGenerator {
|
|||||||
*/
|
*/
|
||||||
protected int indent;
|
protected int indent;
|
||||||
/**
|
/**
|
||||||
* the technique to use for the shader generation
|
* the technique def to use for the shader generation
|
||||||
*/
|
*/
|
||||||
protected Technique technique = null;
|
protected TechniqueDef techniqueDef = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a shaderGenerator
|
* Build a shaderGenerator
|
||||||
@ -70,8 +70,8 @@ public abstract class ShaderGenerator {
|
|||||||
this.assetManager = assetManager;
|
this.assetManager = assetManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize(Technique technique){
|
public void initialize(TechniqueDef techniqueDef){
|
||||||
this.technique = technique;
|
this.techniqueDef = techniqueDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,24 +79,29 @@ public abstract class ShaderGenerator {
|
|||||||
*
|
*
|
||||||
* @return a Shader program
|
* @return a Shader program
|
||||||
*/
|
*/
|
||||||
public Shader generateShader() {
|
public Shader generateShader(String definesSourceCode) {
|
||||||
if(technique == null){
|
if (techniqueDef == null) {
|
||||||
throw new UnsupportedOperationException("The shaderGenerator was not properly initialized, call initialize(Technique) before any generation");
|
throw new UnsupportedOperationException("The shaderGenerator was not "
|
||||||
|
+ "properly initialized, call "
|
||||||
|
+ "initialize(TechniqueDef) before any generation");
|
||||||
}
|
}
|
||||||
|
|
||||||
DefineList defines = technique.getAllDefines();
|
String techniqueName = techniqueDef.getName();
|
||||||
TechniqueDef def = technique.getDef();
|
ShaderGenerationInfo info = techniqueDef.getShaderGenerationInfo();
|
||||||
ShaderGenerationInfo info = def.getShaderGenerationInfo();
|
|
||||||
|
|
||||||
String vertexSource = buildShader(def.getShaderNodes(), info, ShaderType.Vertex);
|
|
||||||
String fragmentSource = buildShader(def.getShaderNodes(), info, ShaderType.Fragment);
|
|
||||||
|
|
||||||
Shader shader = new Shader();
|
Shader shader = new Shader();
|
||||||
shader.initialize();
|
for (ShaderType type : ShaderType.values()) {
|
||||||
shader.addSource(Shader.ShaderType.Vertex, technique.getDef().getName() + ".vert", vertexSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Vertex));
|
String extension = type.getExtension();
|
||||||
shader.addSource(Shader.ShaderType.Fragment, technique.getDef().getName() + ".frag", fragmentSource, defines.getCompiled(), getLanguageAndVersion(ShaderType.Fragment));
|
String language = getLanguageAndVersion(type);
|
||||||
|
String shaderSourceCode = buildShader(techniqueDef.getShaderNodes(), info, type);
|
||||||
|
|
||||||
technique = null;
|
if (shaderSourceCode != null) {
|
||||||
|
String shaderSourceAssetName = techniqueName + "." + extension;
|
||||||
|
shader.addSource(type, shaderSourceAssetName, shaderSourceCode, definesSourceCode, language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
techniqueDef = null;
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +114,14 @@ public abstract class ShaderGenerator {
|
|||||||
* @return the code of the generated vertex shader
|
* @return the code of the generated vertex shader
|
||||||
*/
|
*/
|
||||||
protected String buildShader(List<ShaderNode> shaderNodes, ShaderGenerationInfo info, ShaderType type) {
|
protected String buildShader(List<ShaderNode> shaderNodes, ShaderGenerationInfo info, ShaderType type) {
|
||||||
|
if (type == ShaderType.TessellationControl ||
|
||||||
|
type == ShaderType.TessellationEvaluation ||
|
||||||
|
type == ShaderType.Geometry) {
|
||||||
|
// TODO: Those are not supported.
|
||||||
|
// Too much code assumes that type is either Vertex or Fragment
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
indent = 0;
|
indent = 0;
|
||||||
|
|
||||||
StringBuilder sourceDeclaration = new StringBuilder();
|
StringBuilder sourceDeclaration = new StringBuilder();
|
||||||
|
@ -1,201 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
package com.jme3.shader;
|
|
||||||
|
|
||||||
import com.jme3.asset.AssetKey;
|
|
||||||
import com.jme3.export.InputCapsule;
|
|
||||||
import com.jme3.export.JmeExporter;
|
|
||||||
import com.jme3.export.JmeImporter;
|
|
||||||
import com.jme3.export.OutputCapsule;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class ShaderKey extends AssetKey<Shader> {
|
|
||||||
|
|
||||||
protected EnumMap<Shader.ShaderType,String> shaderLanguage;
|
|
||||||
protected EnumMap<Shader.ShaderType,String> shaderName;
|
|
||||||
protected DefineList defines;
|
|
||||||
protected int cachedHashedCode = 0;
|
|
||||||
protected boolean usesShaderNodes = false;
|
|
||||||
|
|
||||||
public ShaderKey(){
|
|
||||||
shaderLanguage=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
|
||||||
shaderName=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderKey(DefineList defines, EnumMap<Shader.ShaderType,String> shaderLanguage,EnumMap<Shader.ShaderType,String> shaderName){
|
|
||||||
super("");
|
|
||||||
this.name = reducePath(getShaderName(Shader.ShaderType.Vertex));
|
|
||||||
this.shaderLanguage=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
|
||||||
this.shaderName=new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
|
||||||
this.defines = defines;
|
|
||||||
for (Shader.ShaderType shaderType : shaderName.keySet()) {
|
|
||||||
this.shaderName.put(shaderType,shaderName.get(shaderType));
|
|
||||||
this.shaderLanguage.put(shaderType,shaderLanguage.get(shaderType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ShaderKey clone() {
|
|
||||||
ShaderKey clone = (ShaderKey) super.clone();
|
|
||||||
clone.cachedHashedCode = 0;
|
|
||||||
clone.defines = defines.clone();
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(){
|
|
||||||
//todo:
|
|
||||||
return "V="+name+";";
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String getShaderName(Shader.ShaderType type) {
|
|
||||||
if (shaderName == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
String shName = shaderName.get(type);
|
|
||||||
return shName != null ? shName : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo: make equals and hashCode work
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
final ShaderKey other = (ShaderKey) obj;
|
|
||||||
if (name.equals(other.name) && getShaderName(Shader.ShaderType.Fragment).equals(other.getShaderName(Shader.ShaderType.Fragment))){
|
|
||||||
if (defines != null && other.defines != null) {
|
|
||||||
return defines.equals(other.defines);
|
|
||||||
} else if (defines != null || other.defines != null) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
if (cachedHashedCode == 0) {
|
|
||||||
int hash = 7;
|
|
||||||
hash = 41 * hash + name.hashCode();
|
|
||||||
hash = 41 * hash + getShaderName(Shader.ShaderType.Fragment).hashCode();
|
|
||||||
hash = getShaderName(Shader.ShaderType.Geometry) == null ? hash : 41 * hash + getShaderName(Shader.ShaderType.Geometry).hashCode();
|
|
||||||
hash = getShaderName(Shader.ShaderType.TessellationControl) == null ? hash : 41 * hash + getShaderName(Shader.ShaderType.TessellationControl).hashCode();
|
|
||||||
hash = getShaderName(Shader.ShaderType.TessellationEvaluation) == null ? hash : 41 * hash + getShaderName(Shader.ShaderType.TessellationEvaluation).hashCode();
|
|
||||||
hash = 41 * hash + (defines != null ? defines.hashCode() : 0);
|
|
||||||
cachedHashedCode = hash;
|
|
||||||
}
|
|
||||||
return cachedHashedCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DefineList getDefines() {
|
|
||||||
return defines;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getVertName(){
|
|
||||||
return getShaderName(Shader.ShaderType.Vertex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFragName() {
|
|
||||||
return getShaderName(Shader.ShaderType.Fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #getVertexShaderLanguage() } instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public String getLanguage() {
|
|
||||||
return shaderLanguage.get(Shader.ShaderType.Vertex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getVertexShaderLanguage() {
|
|
||||||
return shaderLanguage.get(Shader.ShaderType.Vertex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFragmentShaderLanguage() {
|
|
||||||
return shaderLanguage.get(Shader.ShaderType.Vertex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUsesShaderNodes() {
|
|
||||||
return usesShaderNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsesShaderNodes(boolean usesShaderNodes) {
|
|
||||||
this.usesShaderNodes = usesShaderNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<Shader.ShaderType> getUsedShaderPrograms(){
|
|
||||||
return shaderName.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getShaderProgramLanguage(Shader.ShaderType shaderType){
|
|
||||||
return shaderLanguage.get(shaderType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getShaderProgramName(Shader.ShaderType shaderType){
|
|
||||||
return getShaderName(shaderType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(JmeExporter ex) throws IOException{
|
|
||||||
super.write(ex);
|
|
||||||
OutputCapsule oc = ex.getCapsule(this);
|
|
||||||
oc.write(shaderName.get(Shader.ShaderType.Fragment), "fragment_name", null);
|
|
||||||
oc.write(shaderName.get(Shader.ShaderType.Geometry), "geometry_name", null);
|
|
||||||
oc.write(shaderName.get(Shader.ShaderType.TessellationControl), "tessControl_name", null);
|
|
||||||
oc.write(shaderName.get(Shader.ShaderType.TessellationEvaluation), "tessEval_name", null);
|
|
||||||
oc.write(shaderLanguage.get(Shader.ShaderType.Vertex), "language", null);
|
|
||||||
oc.write(shaderLanguage.get(Shader.ShaderType.Fragment), "frag_language", null);
|
|
||||||
oc.write(shaderLanguage.get(Shader.ShaderType.Geometry), "geom_language", null);
|
|
||||||
oc.write(shaderLanguage.get(Shader.ShaderType.TessellationControl), "tsctrl_language", null);
|
|
||||||
oc.write(shaderLanguage.get(Shader.ShaderType.TessellationEvaluation), "tseval_language", null);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void read(JmeImporter im) throws IOException{
|
|
||||||
super.read(im);
|
|
||||||
InputCapsule ic = im.getCapsule(this);
|
|
||||||
shaderName.put(Shader.ShaderType.Vertex,name);
|
|
||||||
shaderName.put(Shader.ShaderType.Fragment,ic.readString("fragment_name", null));
|
|
||||||
shaderName.put(Shader.ShaderType.Geometry,ic.readString("geometry_name", null));
|
|
||||||
shaderName.put(Shader.ShaderType.TessellationControl,ic.readString("tessControl_name", null));
|
|
||||||
shaderName.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tessEval_name", null));
|
|
||||||
shaderLanguage.put(Shader.ShaderType.Vertex,ic.readString("language", null));
|
|
||||||
shaderLanguage.put(Shader.ShaderType.Fragment,ic.readString("frag_language", null));
|
|
||||||
shaderLanguage.put(Shader.ShaderType.Geometry,ic.readString("geom_language", null));
|
|
||||||
shaderLanguage.put(Shader.ShaderType.TessellationControl,ic.readString("tsctrl_language", null));
|
|
||||||
shaderLanguage.put(Shader.ShaderType.TessellationEvaluation,ic.readString("tseval_language", null));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -70,6 +70,30 @@ public class Uniform extends ShaderVariable {
|
|||||||
*/
|
*/
|
||||||
protected boolean setByCurrentMaterial = false;
|
protected boolean setByCurrentMaterial = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 5;
|
||||||
|
hash = 31 * hash + (this.value != null ? this.value.hashCode() : 0);
|
||||||
|
hash = 31 * hash + (this.varType != null ? this.varType.hashCode() : 0);
|
||||||
|
hash = 31 * hash + (this.binding != null ? this.binding.hashCode() : 0);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final Uniform other = (Uniform) obj;
|
||||||
|
if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.binding == other.binding && this.varType == other.varType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public String toString(){
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@ -103,6 +127,10 @@ public class Uniform extends ShaderVariable {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FloatBuffer getMultiData() {
|
||||||
|
return multiData;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSetByCurrentMaterial() {
|
public boolean isSetByCurrentMaterial() {
|
||||||
return setByCurrentMaterial;
|
return setByCurrentMaterial;
|
||||||
}
|
}
|
||||||
@ -111,21 +139,6 @@ public class Uniform extends ShaderVariable {
|
|||||||
setByCurrentMaterial = false;
|
setByCurrentMaterial = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setVector4(Vector4f vec, Object value) {
|
|
||||||
if (value instanceof ColorRGBA) {
|
|
||||||
ColorRGBA color = (ColorRGBA) value;
|
|
||||||
vec.set(color.r, color.g, color.b, color.a);
|
|
||||||
} else if (value instanceof Quaternion) {
|
|
||||||
Quaternion quat = (Quaternion) value;
|
|
||||||
vec.set(quat.getX(), quat.getY(), quat.getZ(), quat.getW());
|
|
||||||
} else if (value instanceof Vector4f) {
|
|
||||||
Vector4f vec4 = (Vector4f) value;
|
|
||||||
vec.set(vec4);
|
|
||||||
} else{
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearValue(){
|
public void clearValue(){
|
||||||
updateNeeded = true;
|
updateNeeded = true;
|
||||||
|
|
||||||
@ -182,27 +195,43 @@ public class Uniform extends ShaderVariable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new NullPointerException();
|
throw new IllegalArgumentException("for uniform " + name + ": value cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
setByCurrentMaterial = true;
|
setByCurrentMaterial = true;
|
||||||
|
|
||||||
switch (type){
|
switch (type){
|
||||||
case Matrix3:
|
case Matrix3:
|
||||||
|
if (value.equals(this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Matrix3f m3 = (Matrix3f) value;
|
Matrix3f m3 = (Matrix3f) value;
|
||||||
if (multiData == null) {
|
if (multiData == null) {
|
||||||
multiData = BufferUtils.createFloatBuffer(9);
|
multiData = BufferUtils.createFloatBuffer(9);
|
||||||
}
|
}
|
||||||
m3.fillFloatBuffer(multiData, true);
|
m3.fillFloatBuffer(multiData, true);
|
||||||
multiData.clear();
|
multiData.clear();
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = new Matrix3f(m3);
|
||||||
|
} else {
|
||||||
|
((Matrix3f)this.value).set(m3);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Matrix4:
|
case Matrix4:
|
||||||
|
if (value.equals(this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Matrix4f m4 = (Matrix4f) value;
|
Matrix4f m4 = (Matrix4f) value;
|
||||||
if (multiData == null) {
|
if (multiData == null) {
|
||||||
multiData = BufferUtils.createFloatBuffer(16);
|
multiData = BufferUtils.createFloatBuffer(16);
|
||||||
}
|
}
|
||||||
m4.fillFloatBuffer(multiData, true);
|
m4.fillFloatBuffer(multiData, true);
|
||||||
multiData.clear();
|
multiData.clear();
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = new Matrix4f(m4);
|
||||||
|
} else {
|
||||||
|
((Matrix4f)this.value).copy(m4);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case IntArray:
|
case IntArray:
|
||||||
int[] ia = (int[]) value;
|
int[] ia = (int[]) value;
|
||||||
@ -283,11 +312,32 @@ public class Uniform extends ShaderVariable {
|
|||||||
}
|
}
|
||||||
multiData.clear();
|
multiData.clear();
|
||||||
break;
|
break;
|
||||||
|
case Vector4:
|
||||||
|
if (value.equals(this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof ColorRGBA) {
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = new ColorRGBA();
|
||||||
|
}
|
||||||
|
((ColorRGBA) this.value).set((ColorRGBA) value);
|
||||||
|
} else if (value instanceof Vector4f) {
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = new Vector4f();
|
||||||
|
}
|
||||||
|
((Vector4f) this.value).set((Vector4f) value);
|
||||||
|
} else {
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = new Quaternion();
|
||||||
|
}
|
||||||
|
((Quaternion) this.value).set((Quaternion) value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
// Only use check if equals optimization for primitive values
|
// Only use check if equals optimization for primitive values
|
||||||
case Int:
|
case Int:
|
||||||
case Float:
|
case Float:
|
||||||
case Boolean:
|
case Boolean:
|
||||||
if (this.value != null && this.value.equals(value)) {
|
if (value.equals(this.value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@ -297,39 +347,38 @@ public class Uniform extends ShaderVariable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multiData != null) {
|
// if (multiData != null) {
|
||||||
this.value = multiData;
|
// this.value = multiData;
|
||||||
}
|
// }
|
||||||
|
|
||||||
varType = type;
|
varType = type;
|
||||||
updateNeeded = true;
|
updateNeeded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVector4Length(int length){
|
public void setVector4Length(int length){
|
||||||
if (location == -1)
|
if (location == -1) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FloatBuffer fb = (FloatBuffer) value;
|
|
||||||
if (fb == null || fb.capacity() < length * 4) {
|
|
||||||
value = BufferUtils.createFloatBuffer(length * 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
multiData = BufferUtils.ensureLargeEnough(multiData, length * 4);
|
||||||
|
value = multiData;
|
||||||
varType = VarType.Vector4Array;
|
varType = VarType.Vector4Array;
|
||||||
updateNeeded = true;
|
updateNeeded = true;
|
||||||
setByCurrentMaterial = true;
|
setByCurrentMaterial = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVector4InArray(float x, float y, float z, float w, int index){
|
public void setVector4InArray(float x, float y, float z, float w, int index){
|
||||||
if (location == -1)
|
if (location == -1) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (varType != null && varType != VarType.Vector4Array)
|
if (varType != null && varType != VarType.Vector4Array) {
|
||||||
throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
|
throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
|
||||||
|
}
|
||||||
|
|
||||||
FloatBuffer fb = (FloatBuffer) value;
|
multiData.position(index * 4);
|
||||||
fb.position(index * 4);
|
multiData.put(x).put(y).put(z).put(w);
|
||||||
fb.put(x).put(y).put(z).put(w);
|
multiData.rewind();
|
||||||
fb.rewind();
|
|
||||||
updateNeeded = true;
|
updateNeeded = true;
|
||||||
setByCurrentMaterial = true;
|
setByCurrentMaterial = true;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ import com.jme3.math.*;
|
|||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
import com.jme3.system.Timer;
|
import com.jme3.system.Timer;
|
||||||
import java.util.List;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>UniformBindingManager</code> helps {@link RenderManager} to manage
|
* <code>UniformBindingManager</code> helps {@link RenderManager} to manage
|
||||||
@ -84,7 +84,8 @@ public class UniformBindingManager {
|
|||||||
* Updates the given list of uniforms with {@link UniformBinding uniform bindings}
|
* Updates the given list of uniforms with {@link UniformBinding uniform bindings}
|
||||||
* based on the current world state.
|
* based on the current world state.
|
||||||
*/
|
*/
|
||||||
public void updateUniformBindings(List<Uniform> params) {
|
public void updateUniformBindings(Shader shader) {
|
||||||
|
ArrayList<Uniform> params = shader.getBoundUniforms();
|
||||||
for (int i = 0; i < params.size(); i++) {
|
for (int i = 0; i < params.size(); i++) {
|
||||||
Uniform u = params.get(i);
|
Uniform u = params.get(i);
|
||||||
switch (u.getBinding()) {
|
switch (u.getBinding()) {
|
||||||
|
@ -330,12 +330,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
|
|||||||
public void initialize(RenderManager rm, ViewPort vp) {
|
public void initialize(RenderManager rm, ViewPort vp) {
|
||||||
renderManager = rm;
|
renderManager = rm;
|
||||||
viewPort = vp;
|
viewPort = vp;
|
||||||
//checking for caps to chosse the appropriate post material technique
|
|
||||||
if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) {
|
|
||||||
postTechniqueName = "PostShadow15";
|
|
||||||
} else {
|
|
||||||
postTechniqueName = "PostShadow";
|
postTechniqueName = "PostShadow";
|
||||||
}
|
|
||||||
if(zFarOverride>0 && frustumCam == null){
|
if(zFarOverride>0 && frustumCam == null){
|
||||||
initFrustumCam();
|
initFrustumCam();
|
||||||
}
|
}
|
||||||
@ -587,7 +582,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
|
|||||||
for (int i = 0; i < l.size(); i++) {
|
for (int i = 0; i < l.size(); i++) {
|
||||||
Material mat = l.get(i).getMaterial();
|
Material mat = l.get(i).getMaterial();
|
||||||
//checking if the material has the post technique and adding it to the material cache
|
//checking if the material has the post technique and adding it to the material cache
|
||||||
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) {
|
if (mat.getMaterialDef().getTechniqueDefs(postTechniqueName) != null) {
|
||||||
if (!matCache.contains(mat)) {
|
if (!matCache.contains(mat)) {
|
||||||
matCache.add(mat);
|
matCache.add(mat);
|
||||||
}
|
}
|
||||||
|
@ -355,13 +355,8 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
public void initialize(RenderManager rm, ViewPort vp) {
|
public void initialize(RenderManager rm, ViewPort vp) {
|
||||||
renderManager = rm;
|
renderManager = rm;
|
||||||
viewPort = vp;
|
viewPort = vp;
|
||||||
//checking for caps to chosse the appropriate post material technique
|
|
||||||
if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) {
|
|
||||||
postTechniqueName = "PostShadow15";
|
|
||||||
} else {
|
|
||||||
postTechniqueName = "PostShadow";
|
postTechniqueName = "PostShadow";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isInitialized() {
|
public boolean isInitialized() {
|
||||||
return viewPort != null;
|
return viewPort != null;
|
||||||
@ -533,7 +528,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
for (int i = 0; i < l.size(); i++) {
|
for (int i = 0; i < l.size(); i++) {
|
||||||
Material mat = l.get(i).getMaterial();
|
Material mat = l.get(i).getMaterial();
|
||||||
//checking if the material has the post technique and adding it to the material cache
|
//checking if the material has the post technique and adding it to the material cache
|
||||||
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) {
|
if (mat.getMaterialDef().getTechniqueDefs(postTechniqueName) != null) {
|
||||||
if (!matCache.contains(mat)) {
|
if (!matCache.contains(mat)) {
|
||||||
matCache.add(mat);
|
matCache.add(mat);
|
||||||
}
|
}
|
||||||
|
@ -128,12 +128,13 @@ public class NullContext implements JmeContext, Runnable {
|
|||||||
public void run(){
|
public void run(){
|
||||||
initInThread();
|
initInThread();
|
||||||
|
|
||||||
while (!needClose.get()){
|
do {
|
||||||
listener.update();
|
listener.update();
|
||||||
|
|
||||||
if (frameRate > 0)
|
if (frameRate > 0) {
|
||||||
sync(frameRate);
|
sync(frameRate);
|
||||||
}
|
}
|
||||||
|
} while (!needClose.get());
|
||||||
|
|
||||||
deinitInThread();
|
deinitInThread();
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ import com.jme3.material.RenderState;
|
|||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.math.Matrix4f;
|
import com.jme3.math.Matrix4f;
|
||||||
import com.jme3.renderer.Caps;
|
import com.jme3.renderer.Caps;
|
||||||
|
import com.jme3.renderer.Limits;
|
||||||
import com.jme3.renderer.Renderer;
|
import com.jme3.renderer.Renderer;
|
||||||
import com.jme3.renderer.Statistics;
|
import com.jme3.renderer.Statistics;
|
||||||
import com.jme3.scene.Mesh;
|
import com.jme3.scene.Mesh;
|
||||||
@ -48,13 +49,23 @@ import com.jme3.shader.Shader.ShaderSource;
|
|||||||
import com.jme3.texture.FrameBuffer;
|
import com.jme3.texture.FrameBuffer;
|
||||||
import com.jme3.texture.Image;
|
import com.jme3.texture.Image;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
|
||||||
public class NullRenderer implements Renderer {
|
public class NullRenderer implements Renderer {
|
||||||
|
|
||||||
private static final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
|
private final EnumSet<Caps> caps = EnumSet.allOf(Caps.class);
|
||||||
private static final Statistics stats = new Statistics();
|
private final EnumMap<Limits, Integer> limits = new EnumMap<>(Limits.class);
|
||||||
|
private final Statistics stats = new Statistics();
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
for (Limits limit : Limits.values()) {
|
||||||
|
limits.put(limit, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnumMap<Limits, Integer> getLimits() {
|
||||||
|
return limits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EnumSet<Caps> getCaps() {
|
public EnumSet<Caps> getCaps() {
|
||||||
@ -164,4 +175,7 @@ public class NullRenderer implements Renderer {
|
|||||||
public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
|
public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDefaultAnisotropicFilter(int level) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ public final class LastTextureState {
|
|||||||
rWrap = null;
|
rWrap = null;
|
||||||
magFilter = null;
|
magFilter = null;
|
||||||
minFilter = null;
|
minFilter = null;
|
||||||
anisoFilter = 0;
|
anisoFilter = 1;
|
||||||
|
|
||||||
// The default in OpenGL is OFF, so we avoid setting this per texture
|
// The default in OpenGL is OFF, so we avoid setting this per texture
|
||||||
// if its not used.
|
// if its not used.
|
||||||
|
@ -207,10 +207,10 @@ public class Cloner {
|
|||||||
|
|
||||||
// Check the index to see if we already have it
|
// Check the index to see if we already have it
|
||||||
Object clone = index.get(object);
|
Object clone = index.get(object);
|
||||||
if( clone != null ) {
|
if( clone != null || index.containsKey(object) ) {
|
||||||
if( log.isLoggable(Level.FINER) ) {
|
if( log.isLoggable(Level.FINER) ) {
|
||||||
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
|
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object)
|
||||||
+ " as cached:" + clone.getClass() + "@" + System.identityHashCode(clone));
|
+ " as cached:" + (clone == null ? "null" : (clone.getClass() + "@" + System.identityHashCode(clone))));
|
||||||
}
|
}
|
||||||
return type.cast(clone);
|
return type.cast(clone);
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ MaterialDef Phong Lighting {
|
|||||||
SEPARATE_TEXCOORD : SeparateTexCoord
|
SEPARATE_TEXCOORD : SeparateTexCoord
|
||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
USE_REFLECTION : EnvMap
|
USE_REFLECTION : EnvMap
|
||||||
SPHERE_MAP : SphereMap
|
SPHERE_MAP : EnvMapAsSphereMap
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ MaterialDef Phong Lighting {
|
|||||||
SEPARATE_TEXCOORD : SeparateTexCoord
|
SEPARATE_TEXCOORD : SeparateTexCoord
|
||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
USE_REFLECTION : EnvMap
|
USE_REFLECTION : EnvMap
|
||||||
SPHERE_MAP : SphereMap
|
SPHERE_MAP : EnvMapAsSphereMap
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
}
|
}
|
||||||
@ -209,7 +209,6 @@ MaterialDef Phong Lighting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
COLOR_MAP : ColorMap
|
|
||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
NUM_BONES : NumberOfBones
|
NUM_BONES : NumberOfBones
|
||||||
INSTANCING : UseInstancing
|
INSTANCING : UseInstancing
|
||||||
@ -218,7 +217,7 @@ MaterialDef Phong Lighting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Technique PostShadow15{
|
Technique PostShadow {
|
||||||
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
|
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
|
||||||
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag
|
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag
|
||||||
|
|
||||||
@ -235,7 +234,6 @@ MaterialDef Phong Lighting {
|
|||||||
FILTER_MODE : FilterMode
|
FILTER_MODE : FilterMode
|
||||||
PCFEDGE : PCFEdge
|
PCFEDGE : PCFEdge
|
||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
COLOR_MAP : ColorMap
|
|
||||||
SHADOWMAP_SIZE : ShadowMapSize
|
SHADOWMAP_SIZE : ShadowMapSize
|
||||||
FADE : FadeInfo
|
FADE : FadeInfo
|
||||||
PSSM : Splits
|
PSSM : Splits
|
||||||
@ -269,7 +267,6 @@ MaterialDef Phong Lighting {
|
|||||||
FILTER_MODE : FilterMode
|
FILTER_MODE : FilterMode
|
||||||
PCFEDGE : PCFEdge
|
PCFEDGE : PCFEdge
|
||||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||||
COLOR_MAP : ColorMap
|
|
||||||
SHADOWMAP_SIZE : ShadowMapSize
|
SHADOWMAP_SIZE : ShadowMapSize
|
||||||
FADE : FadeInfo
|
FADE : FadeInfo
|
||||||
PSSM : Splits
|
PSSM : Splits
|
||||||
@ -343,10 +340,6 @@ MaterialDef Phong Lighting {
|
|||||||
Defines {
|
Defines {
|
||||||
VERTEX_COLOR : UseVertexColor
|
VERTEX_COLOR : UseVertexColor
|
||||||
MATERIAL_COLORS : UseMaterialColors
|
MATERIAL_COLORS : UseMaterialColors
|
||||||
V_TANGENT : VTangent
|
|
||||||
MINNAERT : Minnaert
|
|
||||||
WARDISO : WardIso
|
|
||||||
|
|
||||||
DIFFUSEMAP : DiffuseMap
|
DIFFUSEMAP : DiffuseMap
|
||||||
NORMALMAP : NormalMap
|
NORMALMAP : NormalMap
|
||||||
SPECULARMAP : SpecularMap
|
SPECULARMAP : SpecularMap
|
||||||
|
@ -148,7 +148,7 @@ MaterialDef Unshaded {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Technique PostShadow15{
|
Technique PostShadow {
|
||||||
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
|
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
|
||||||
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag
|
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow.frag
|
||||||
|
|
||||||
|
@ -22,5 +22,4 @@ LOADER com.jme3.scene.plugins.ogre.MaterialLoader : material
|
|||||||
LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene
|
LOADER com.jme3.scene.plugins.ogre.SceneLoader : scene
|
||||||
LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend
|
LOADER com.jme3.scene.plugins.blender.BlenderModelLoader : blend
|
||||||
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, geom, tsctrl, tseval, glsl, glsllib
|
LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, geom, tsctrl, tseval, glsl, glsllib
|
||||||
LOADER com.jme3.scene.plugins.fbx.SceneLoader : fbx
|
LOADER com.jme3.scene.plugins.fbx.FbxLoader : fbx
|
||||||
LOADER com.jme3.scene.plugins.fbx.SceneWithAnimationLoader : fba
|
|
||||||
|
@ -31,8 +31,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.material.plugins;
|
package com.jme3.material.plugins;
|
||||||
|
|
||||||
|
import com.jme3.material.logic.MultiPassLightingLogic;
|
||||||
|
import com.jme3.material.logic.SinglePassLightingLogic;
|
||||||
|
import com.jme3.material.logic.DefaultTechniqueDefLogic;
|
||||||
import com.jme3.asset.*;
|
import com.jme3.asset.*;
|
||||||
import com.jme3.material.*;
|
import com.jme3.material.*;
|
||||||
|
import com.jme3.material.RenderState.BlendEquation;
|
||||||
import com.jme3.material.RenderState.BlendMode;
|
import com.jme3.material.RenderState.BlendMode;
|
||||||
import com.jme3.material.RenderState.FaceCullMode;
|
import com.jme3.material.RenderState.FaceCullMode;
|
||||||
import com.jme3.material.TechniqueDef.LightMode;
|
import com.jme3.material.TechniqueDef.LightMode;
|
||||||
@ -40,6 +44,7 @@ import com.jme3.material.TechniqueDef.ShadowMode;
|
|||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.math.Vector2f;
|
import com.jme3.math.Vector2f;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.shader.DefineList;
|
||||||
import com.jme3.shader.Shader;
|
import com.jme3.shader.Shader;
|
||||||
import com.jme3.shader.VarType;
|
import com.jme3.shader.VarType;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
@ -73,15 +78,16 @@ public class J3MLoader implements AssetLoader {
|
|||||||
private Material material;
|
private Material material;
|
||||||
private TechniqueDef technique;
|
private TechniqueDef technique;
|
||||||
private RenderState renderState;
|
private RenderState renderState;
|
||||||
|
private ArrayList<String> presetDefines = new ArrayList<String>();
|
||||||
|
|
||||||
private EnumMap<Shader.ShaderType, String> shaderLanguage;
|
private EnumMap<Shader.ShaderType, String> shaderLanguages;
|
||||||
private EnumMap<Shader.ShaderType, String> shaderName;
|
private EnumMap<Shader.ShaderType, String> shaderNames;
|
||||||
|
|
||||||
private static final String whitespacePattern = "\\p{javaWhitespace}+";
|
private static final String whitespacePattern = "\\p{javaWhitespace}+";
|
||||||
|
|
||||||
public J3MLoader() {
|
public J3MLoader() {
|
||||||
shaderLanguage = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
shaderLanguages = new EnumMap<>(Shader.ShaderType.class);
|
||||||
shaderName = new EnumMap<Shader.ShaderType, String>(Shader.ShaderType.class);
|
shaderNames = new EnumMap<>(Shader.ShaderType.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -104,8 +110,8 @@ public class J3MLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readShaderDefinition(Shader.ShaderType shaderType, String name, String language) {
|
private void readShaderDefinition(Shader.ShaderType shaderType, String name, String language) {
|
||||||
shaderName.put(shaderType, name);
|
shaderNames.put(shaderType, name);
|
||||||
shaderLanguage.put(shaderType, language);
|
shaderLanguages.put(shaderType, language);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LightMode <MODE>
|
// LightMode <MODE>
|
||||||
@ -443,9 +449,12 @@ public class J3MLoader implements AssetLoader {
|
|||||||
renderState.setDepthTest(parseBoolean(split[1]));
|
renderState.setDepthTest(parseBoolean(split[1]));
|
||||||
}else if (split[0].equals("Blend")){
|
}else if (split[0].equals("Blend")){
|
||||||
renderState.setBlendMode(BlendMode.valueOf(split[1]));
|
renderState.setBlendMode(BlendMode.valueOf(split[1]));
|
||||||
|
}else if (split[0].equals("BlendEquation")){
|
||||||
|
renderState.setBlendEquation(BlendEquation.valueOf(split[1]));
|
||||||
|
}else if (split[0].equals("BlendEquationAlpha")){
|
||||||
|
renderState.setBlendEquationAlpha(RenderState.BlendEquationAlpha.valueOf(split[1]));
|
||||||
}else if (split[0].equals("AlphaTestFalloff")){
|
}else if (split[0].equals("AlphaTestFalloff")){
|
||||||
renderState.setAlphaTest(true);
|
// Ignore for backwards compatbility
|
||||||
renderState.setAlphaFallOff(Float.parseFloat(split[1]));
|
|
||||||
}else if (split[0].equals("PolyOffset")){
|
}else if (split[0].equals("PolyOffset")){
|
||||||
float factor = Float.parseFloat(split[1]);
|
float factor = Float.parseFloat(split[1]);
|
||||||
float units = Float.parseFloat(split[2]);
|
float units = Float.parseFloat(split[2]);
|
||||||
@ -453,7 +462,7 @@ public class J3MLoader implements AssetLoader {
|
|||||||
}else if (split[0].equals("ColorWrite")){
|
}else if (split[0].equals("ColorWrite")){
|
||||||
renderState.setColorWrite(parseBoolean(split[1]));
|
renderState.setColorWrite(parseBoolean(split[1]));
|
||||||
}else if (split[0].equals("PointSprite")){
|
}else if (split[0].equals("PointSprite")){
|
||||||
renderState.setPointSprite(parseBoolean(split[1]));
|
// Ignore for backwards compatbility
|
||||||
}else if (split[0].equals("DepthFunc")){
|
}else if (split[0].equals("DepthFunc")){
|
||||||
renderState.setDepthFunc(RenderState.TestFunction.valueOf(split[1]));
|
renderState.setDepthFunc(RenderState.TestFunction.valueOf(split[1]));
|
||||||
}else if (split[0].equals("AlphaFunc")){
|
}else if (split[0].equals("AlphaFunc")){
|
||||||
@ -495,10 +504,22 @@ public class J3MLoader implements AssetLoader {
|
|||||||
private void readDefine(String statement) throws IOException{
|
private void readDefine(String statement) throws IOException{
|
||||||
String[] split = statement.split(":");
|
String[] split = statement.split(":");
|
||||||
if (split.length == 1){
|
if (split.length == 1){
|
||||||
// add preset define
|
String defineName = split[0].trim();
|
||||||
technique.addShaderPresetDefine(split[0].trim(), VarType.Boolean, true);
|
presetDefines.add(defineName);
|
||||||
}else if (split.length == 2){
|
}else if (split.length == 2){
|
||||||
technique.addShaderParamDefine(split[1].trim(), split[0].trim());
|
String defineName = split[0].trim();
|
||||||
|
String paramName = split[1].trim();
|
||||||
|
MatParam param = materialDef.getMaterialParam(paramName);
|
||||||
|
if (param == null) {
|
||||||
|
logger.log(Level.WARNING, "In technique ''{0}'':\n"
|
||||||
|
+ "Define ''{1}'' mapped to non-existent"
|
||||||
|
+ " material parameter ''{2}'', ignoring.",
|
||||||
|
new Object[]{technique.getName(), defineName, paramName});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VarType paramType = param.getVarType();
|
||||||
|
technique.addShaderParamDefine(paramName, paramType, defineName);
|
||||||
}else{
|
}else{
|
||||||
throw new IOException("Define syntax incorrect");
|
throw new IOException("Define syntax incorrect");
|
||||||
}
|
}
|
||||||
@ -561,36 +582,79 @@ public class J3MLoader implements AssetLoader {
|
|||||||
material.setTransparent(parseBoolean(split[1]));
|
material.setTransparent(parseBoolean(split[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String createShaderPrologue(List<String> presetDefines) {
|
||||||
|
DefineList dl = new DefineList(presetDefines.size());
|
||||||
|
for (int i = 0; i < presetDefines.size(); i++) {
|
||||||
|
dl.set(i, 1);
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
dl.generateSource(sb, presetDefines, null);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private void readTechnique(Statement techStat) throws IOException{
|
private void readTechnique(Statement techStat) throws IOException{
|
||||||
isUseNodes = false;
|
isUseNodes = false;
|
||||||
String[] split = techStat.getLine().split(whitespacePattern);
|
String[] split = techStat.getLine().split(whitespacePattern);
|
||||||
|
|
||||||
|
String name;
|
||||||
if (split.length == 1) {
|
if (split.length == 1) {
|
||||||
technique = new TechniqueDef(null);
|
name = TechniqueDef.DEFAULT_TECHNIQUE_NAME;
|
||||||
} else if (split.length == 2) {
|
} else if (split.length == 2) {
|
||||||
String techName = split[1];
|
name = split[1];
|
||||||
technique = new TechniqueDef(techName);
|
|
||||||
} else {
|
} else {
|
||||||
throw new IOException("Technique statement syntax incorrect");
|
throw new IOException("Technique statement syntax incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String techniqueUniqueName = materialDef.getAssetName() + "@" + name;
|
||||||
|
technique = new TechniqueDef(name, techniqueUniqueName.hashCode());
|
||||||
|
|
||||||
for (Statement statement : techStat.getContents()){
|
for (Statement statement : techStat.getContents()){
|
||||||
readTechniqueStatement(statement);
|
readTechniqueStatement(statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isUseNodes){
|
if(isUseNodes){
|
||||||
nodesLoaderDelegate.computeConditions();
|
nodesLoaderDelegate.computeConditions();
|
||||||
|
|
||||||
//used for caching later, the shader here is not a file.
|
//used for caching later, the shader here is not a file.
|
||||||
|
|
||||||
|
// KIRILL 9/19/2015
|
||||||
|
// Not sure if this is needed anymore, since shader caching
|
||||||
|
// is now done by TechniqueDef.
|
||||||
technique.setShaderFile(technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100");
|
technique.setShaderFile(technique.hashCode() + "", technique.hashCode() + "", "GLSL100", "GLSL100");
|
||||||
|
}else if (shaderNames.containsKey(Shader.ShaderType.Vertex) && shaderNames.containsKey(Shader.ShaderType.Fragment)) {
|
||||||
|
technique.setShaderFile(shaderNames, shaderLanguages);
|
||||||
|
} 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.setShaderPrologue(createShaderPrologue(presetDefines));
|
||||||
technique.setShaderFile(shaderName, shaderLanguage);
|
|
||||||
|
switch (technique.getLightMode()) {
|
||||||
|
case Disable:
|
||||||
|
technique.setLogic(new DefaultTechniqueDefLogic(technique));
|
||||||
|
break;
|
||||||
|
case MultiPass:
|
||||||
|
technique.setLogic(new MultiPassLightingLogic(technique));
|
||||||
|
break;
|
||||||
|
case SinglePass:
|
||||||
|
technique.setLogic(new SinglePassLightingLogic(technique));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
materialDef.addTechniqueDef(technique);
|
materialDef.addTechniqueDef(technique);
|
||||||
technique = null;
|
technique = null;
|
||||||
shaderLanguage.clear();
|
shaderLanguages.clear();
|
||||||
shaderName.clear();
|
shaderNames.clear();
|
||||||
|
presetDefines.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadFromRoot(List<Statement> roots) throws IOException{
|
private void loadFromRoot(List<Statement> roots) throws IOException{
|
||||||
@ -711,7 +775,7 @@ public class J3MLoader implements AssetLoader {
|
|||||||
|
|
||||||
protected void initNodesLoader() {
|
protected void initNodesLoader() {
|
||||||
if (!isUseNodes) {
|
if (!isUseNodes) {
|
||||||
isUseNodes = shaderName.get(Shader.ShaderType.Vertex) == null && shaderName.get(Shader.ShaderType.Fragment) == null;
|
isUseNodes = shaderNames.get(Shader.ShaderType.Vertex) == null && shaderNames.get(Shader.ShaderType.Fragment) == null;
|
||||||
if (isUseNodes) {
|
if (isUseNodes) {
|
||||||
if (nodesLoaderDelegate == null) {
|
if (nodesLoaderDelegate == null) {
|
||||||
nodesLoaderDelegate = new ShaderNodeLoaderDelegate();
|
nodesLoaderDelegate = new ShaderNodeLoaderDelegate();
|
||||||
|
@ -44,6 +44,7 @@ import com.jme3.shader.ShaderNodeDefinition;
|
|||||||
import com.jme3.shader.ShaderNodeVariable;
|
import com.jme3.shader.ShaderNodeVariable;
|
||||||
import com.jme3.shader.ShaderUtils;
|
import com.jme3.shader.ShaderUtils;
|
||||||
import com.jme3.shader.UniformBinding;
|
import com.jme3.shader.UniformBinding;
|
||||||
|
import com.jme3.shader.VarType;
|
||||||
import com.jme3.shader.VariableMapping;
|
import com.jme3.shader.VariableMapping;
|
||||||
import com.jme3.util.blockparser.Statement;
|
import com.jme3.util.blockparser.Statement;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -583,7 +584,7 @@ public class ShaderNodeLoaderDelegate {
|
|||||||
//multiplicity is not an int attempting to find for a material parameter.
|
//multiplicity is not an int attempting to find for a material parameter.
|
||||||
MatParam mp = findMatParam(multiplicity);
|
MatParam mp = findMatParam(multiplicity);
|
||||||
if (mp != null) {
|
if (mp != null) {
|
||||||
addDefine(multiplicity);
|
addDefine(multiplicity, VarType.Int);
|
||||||
multiplicity = multiplicity.toUpperCase();
|
multiplicity = multiplicity.toUpperCase();
|
||||||
} else {
|
} else {
|
||||||
throw new MatParseException("Wrong multiplicity for variable" + mapping.getLeftVariable().getName() + ". " + multiplicity + " should be an int or a declared material parameter.", statement);
|
throw new MatParseException("Wrong multiplicity for variable" + mapping.getLeftVariable().getName() + ". " + multiplicity + " should be an int or a declared material parameter.", statement);
|
||||||
@ -625,9 +626,9 @@ public class ShaderNodeLoaderDelegate {
|
|||||||
*
|
*
|
||||||
* @param paramName
|
* @param paramName
|
||||||
*/
|
*/
|
||||||
public void addDefine(String paramName) {
|
public void addDefine(String paramName, VarType paramType) {
|
||||||
if (techniqueDef.getShaderParamDefine(paramName) == null) {
|
if (techniqueDef.getShaderParamDefine(paramName) == null) {
|
||||||
techniqueDef.addShaderParamDefine(paramName, paramName.toUpperCase());
|
techniqueDef.addShaderParamDefine(paramName, paramType, paramName.toUpperCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,7 +661,7 @@ public class ShaderNodeLoaderDelegate {
|
|||||||
for (String string : defines) {
|
for (String string : defines) {
|
||||||
MatParam param = findMatParam(string);
|
MatParam param = findMatParam(string);
|
||||||
if (param != null) {
|
if (param != null) {
|
||||||
addDefine(param.getName());
|
addDefine(param.getName(), param.getVarType());
|
||||||
} else {
|
} else {
|
||||||
throw new MatParseException("Invalid condition, condition must match a Material Parameter named " + cond, statement);
|
throw new MatParseException("Invalid condition, condition must match a Material Parameter named " + cond, statement);
|
||||||
}
|
}
|
||||||
|
@ -149,8 +149,7 @@ public class MTLLoader implements AssetLoader {
|
|||||||
if (transparent){
|
if (transparent){
|
||||||
material.setTransparent(true);
|
material.setTransparent(true);
|
||||||
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
||||||
material.getAdditionalRenderState().setAlphaTest(true);
|
material.setFloat("AlphaDiscardThreshold", 0.01f);
|
||||||
material.getAdditionalRenderState().setAlphaFallOff(0.01f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
matList.put(matName, material);
|
matList.put(matName, material);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -29,16 +29,24 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
package com.jme3.animation;
|
package com.jme3.asset;
|
||||||
|
|
||||||
/**
|
import com.jme3.asset.plugins.ClasspathLocator;
|
||||||
* @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack)
|
import com.jme3.shader.plugins.GLSLLoader;
|
||||||
*/
|
import com.jme3.system.JmeSystem;
|
||||||
@Deprecated
|
import com.jme3.system.MockJmeSystemDelegate;
|
||||||
public final class BoneAnimation extends Animation {
|
import org.junit.Test;
|
||||||
|
|
||||||
@Deprecated
|
public class LoadShaderSourceTest {
|
||||||
public BoneAnimation(String name, float length) {
|
|
||||||
super(name, length);
|
@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.MatParamTexture;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.material.MaterialDef;
|
import com.jme3.material.MaterialDef;
|
||||||
|
import com.jme3.renderer.Caps;
|
||||||
import com.jme3.shader.VarType;
|
import com.jme3.shader.VarType;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.EnumSet;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -18,6 +21,7 @@ import org.mockito.runners.MockitoJUnitRunner;
|
|||||||
|
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,6 +55,30 @@ public class J3MLoaderTest {
|
|||||||
j3MLoader = new J3MLoader();
|
j3MLoader = new J3MLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noDefaultTechnique_shouldBeSupported() throws IOException {
|
||||||
|
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/no-default-technique.j3md"));
|
||||||
|
MaterialDef def = (MaterialDef) j3MLoader.load(assetInfo);
|
||||||
|
assertEquals(1, def.getTechniqueDefs("Test").size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fixedPipelineTechnique_shouldBeIgnored() throws IOException {
|
||||||
|
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/no-shader-specified.j3md"));
|
||||||
|
MaterialDef def = (MaterialDef) j3MLoader.load(assetInfo);
|
||||||
|
assertEquals(null, def.getTechniqueDefs("A"));
|
||||||
|
assertEquals(1, def.getTechniqueDefs("B").size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleSameNamedTechniques_shouldBeSupported() throws IOException {
|
||||||
|
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/same-name-technique.j3md"));
|
||||||
|
MaterialDef def = (MaterialDef) j3MLoader.load(assetInfo);
|
||||||
|
assertEquals(2, def.getTechniqueDefs("Test").size());
|
||||||
|
assertEquals(EnumSet.of(Caps.GLSL150), def.getTechniqueDefs("Test").get(0).getRequiredCaps());
|
||||||
|
assertEquals(EnumSet.of(Caps.GLSL100), def.getTechniqueDefs("Test").get(1).getRequiredCaps());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void oldStyleTextureParameters_shouldBeSupported() throws Exception {
|
public void oldStyleTextureParameters_shouldBeSupported() throws Exception {
|
||||||
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-oldstyle.j3m"));
|
when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-oldstyle.j3m"));
|
||||||
@ -107,7 +135,7 @@ public class J3MLoaderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private TextureKey setupMockForTexture(final String paramName, final String path, final boolean flipY, final Texture texture) {
|
private TextureKey setupMockForTexture(final String paramName, final String path, final boolean flipY, final Texture texture) {
|
||||||
when(materialDef.getMaterialParam(paramName)).thenReturn(new MatParamTexture(VarType.Texture2D, paramName, texture, 0, null));
|
when(materialDef.getMaterialParam(paramName)).thenReturn(new MatParamTexture(VarType.Texture2D, paramName, texture, null));
|
||||||
|
|
||||||
final TextureKey textureKey = new TextureKey(path, flipY);
|
final TextureKey textureKey = new TextureKey(path, flipY);
|
||||||
textureKey.setGenerateMips(true);
|
textureKey.setGenerateMips(true);
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.material.plugins;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.material.*;
|
||||||
|
import com.jme3.renderer.*;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.shape.Box;
|
||||||
|
import com.jme3.shader.Shader;
|
||||||
|
import com.jme3.system.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class LoadJ3mdTest {
|
||||||
|
|
||||||
|
private Material material;
|
||||||
|
private final Geometry geometry = new Geometry("Geometry", new Box(1, 1, 1));
|
||||||
|
private final EnumSet<Caps> myCaps = EnumSet.noneOf(Caps.class);
|
||||||
|
private final RenderManager renderManager = new RenderManager(new NullRenderer() {
|
||||||
|
@Override
|
||||||
|
public EnumSet<Caps> getCaps() {
|
||||||
|
return LoadJ3mdTest.this.myCaps;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShaderNodesMaterialDefLoading() {
|
||||||
|
supportGlsl(100);
|
||||||
|
material("testMatDef.j3md");
|
||||||
|
material.selectTechnique("Default", renderManager);
|
||||||
|
|
||||||
|
assertEquals(material.getActiveTechnique().getDef().getShaderNodes().size(), 2);
|
||||||
|
Shader s = material.getActiveTechnique().getDef().getShader(TestUtil.createAssetManager(), myCaps, material.getActiveTechnique().getDynamicDefines());
|
||||||
|
assertEquals(s.getSources().size(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void supportGlsl(int version) {
|
||||||
|
switch (version) {
|
||||||
|
case 150:
|
||||||
|
myCaps.add(Caps.GLSL150);
|
||||||
|
case 140:
|
||||||
|
myCaps.add(Caps.GLSL140);
|
||||||
|
case 130:
|
||||||
|
myCaps.add(Caps.GLSL130);
|
||||||
|
case 120:
|
||||||
|
myCaps.add(Caps.GLSL120);
|
||||||
|
case 110:
|
||||||
|
myCaps.add(Caps.GLSL110);
|
||||||
|
case 100:
|
||||||
|
myCaps.add(Caps.GLSL100);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void caps(Caps... caps) {
|
||||||
|
myCaps.addAll(Arrays.asList(caps));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void material(String path) {
|
||||||
|
AssetManager assetManager = TestUtil.createAssetManager();
|
||||||
|
material = new Material(assetManager, path);
|
||||||
|
geometry.setMaterial(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,6 +33,9 @@ package com.jme3.math;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies that algorithms in {@link FastMath} are working correctly.
|
* Verifies that algorithms in {@link FastMath} are working correctly.
|
||||||
*
|
*
|
||||||
@ -56,4 +59,39 @@ public class FastMathTest {
|
|||||||
assert nextPowerOf2 == nearestPowerOfTwoSlow(i);
|
assert nextPowerOf2 == nearestPowerOfTwoSlow(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int fastCounterClockwise(Vector2f p0, Vector2f p1, Vector2f p2) {
|
||||||
|
float result = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
|
||||||
|
return (int) Math.signum(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector2f randomVector() {
|
||||||
|
return new Vector2f(FastMath.nextRandomFloat(),
|
||||||
|
FastMath.nextRandomFloat());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
@Test
|
||||||
|
public void testCounterClockwise() {
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
Vector2f p0 = randomVector();
|
||||||
|
Vector2f p1 = randomVector();
|
||||||
|
Vector2f p2 = randomVector();
|
||||||
|
|
||||||
|
int fastResult = fastCounterClockwise(p0, p1, p2);
|
||||||
|
int slowResult = FastMath.counterClockwise(p0, p1, p2);
|
||||||
|
|
||||||
|
assert fastResult == slowResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// duplicate test
|
||||||
|
Vector2f p0 = new Vector2f(0,0);
|
||||||
|
Vector2f p1 = new Vector2f(0,0);
|
||||||
|
Vector2f p2 = new Vector2f(0,1);
|
||||||
|
|
||||||
|
int fastResult = fastCounterClockwise(p0, p1, p2);
|
||||||
|
int slowResult = FastMath.counterClockwise(p0, p1, p2);
|
||||||
|
|
||||||
|
assertEquals(slowResult, fastResult);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,343 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.renderer;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.material.TechniqueDef;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.renderer.queue.GeometryList;
|
||||||
|
import com.jme3.renderer.queue.OpaqueComparator;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.shape.Box;
|
||||||
|
import com.jme3.system.TestUtil;
|
||||||
|
import com.jme3.texture.Image;
|
||||||
|
import com.jme3.texture.Image.Format;
|
||||||
|
import com.jme3.texture.Texture;
|
||||||
|
import com.jme3.texture.Texture2D;
|
||||||
|
import com.jme3.texture.image.ColorSpace;
|
||||||
|
import com.jme3.util.BufferUtils;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class OpaqueComparatorTest {
|
||||||
|
|
||||||
|
private final Mesh mesh = new Box(1,1,1);
|
||||||
|
private Camera cam = new Camera(1, 1);
|
||||||
|
private RenderManager renderManager;
|
||||||
|
private AssetManager assetManager;
|
||||||
|
private OpaqueComparator comparator = new OpaqueComparator();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
assetManager = TestUtil.createAssetManager();
|
||||||
|
renderManager = TestUtil.createRenderManager();
|
||||||
|
comparator.setCamera(cam);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a correctly sorted list of materials, check if the
|
||||||
|
* opaque comparator can sort a reversed list of them.
|
||||||
|
*
|
||||||
|
* Each material will be cloned so that none of them will be equal to each other
|
||||||
|
* in reference, forcing the comparator to compare the material sort ID.
|
||||||
|
*
|
||||||
|
* E.g. for a list of materials A, B, C, the following list will be generated:
|
||||||
|
* <pre>C, B, A, C, B, A, C, B, A</pre>, it should result in
|
||||||
|
* <pre>A, A, A, B, B, B, C, C, C</pre>.
|
||||||
|
*
|
||||||
|
* @param materials The pre-sorted list of materials to check sorting for.
|
||||||
|
*/
|
||||||
|
private void testSort(Material ... materials) {
|
||||||
|
GeometryList gl = new GeometryList(comparator);
|
||||||
|
for (int g = 0; g < 5; g++) {
|
||||||
|
for (int i = materials.length - 1; i >= 0; i--) {
|
||||||
|
Geometry geom = new Geometry("geom", mesh);
|
||||||
|
Material clonedMaterial = materials[i].clone();
|
||||||
|
|
||||||
|
if (materials[i].getActiveTechnique() != null) {
|
||||||
|
String techniqueName = materials[i].getActiveTechnique().getDef().getName();
|
||||||
|
clonedMaterial.selectTechnique(techniqueName, renderManager);
|
||||||
|
} else {
|
||||||
|
clonedMaterial.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
geom.setMaterial(clonedMaterial);
|
||||||
|
gl.add(geom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gl.sort();
|
||||||
|
|
||||||
|
for (int i = 0; i < gl.size(); i++) {
|
||||||
|
Material mat = gl.get(i).getMaterial();
|
||||||
|
String sortId = Integer.toHexString(mat.getSortId()).toUpperCase();
|
||||||
|
System.out.print(sortId + "\t");
|
||||||
|
System.out.println(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> alreadySeen = new HashSet<String>();
|
||||||
|
Material current = null;
|
||||||
|
for (int i = 0; i < gl.size(); i++) {
|
||||||
|
Material mat = gl.get(i).getMaterial();
|
||||||
|
if (current == null) {
|
||||||
|
current = mat;
|
||||||
|
} else if (!current.getName().equals(mat.getName())) {
|
||||||
|
assert !alreadySeen.contains(mat.getName());
|
||||||
|
alreadySeen.add(current.getName());
|
||||||
|
current = mat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < materials.length; i++) {
|
||||||
|
for (int g = 0; g < 5; g++) {
|
||||||
|
int index = i * 5 + g;
|
||||||
|
Material mat1 = gl.get(index).getMaterial();
|
||||||
|
Material mat2 = materials[i];
|
||||||
|
assert mat1.getName().equals(mat2.getName()) :
|
||||||
|
mat1.getName() + " != " + mat2.getName() + " for index " + index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSortByMaterialDef() {
|
||||||
|
Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material particleMat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
Material unshadedMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md");
|
||||||
|
|
||||||
|
lightingMat.setName("MatLight");
|
||||||
|
particleMat.setName("MatParticle");
|
||||||
|
unshadedMat.setName("MatUnshaded");
|
||||||
|
skyMat.setName("MatSky");
|
||||||
|
testSort(skyMat, lightingMat, particleMat, unshadedMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSortByTechnique() {
|
||||||
|
Material lightingMatDefault = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingPreShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingPostShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingMatPreNormalPass = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingMatGBuf = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingMatGlow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
|
||||||
|
lightingMatDefault.setName("TechDefault");
|
||||||
|
lightingMatDefault.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
|
||||||
|
|
||||||
|
lightingPostShadow.setName("TechPostShad");
|
||||||
|
lightingPostShadow.selectTechnique("PostShadow", renderManager);
|
||||||
|
|
||||||
|
lightingPreShadow.setName("TechPreShad");
|
||||||
|
lightingPreShadow.selectTechnique("PreShadow", renderManager);
|
||||||
|
|
||||||
|
lightingMatPreNormalPass.setName("TechNorm");
|
||||||
|
lightingMatPreNormalPass.selectTechnique("PreNormalPass", renderManager);
|
||||||
|
|
||||||
|
lightingMatGBuf.setName("TechGBuf");
|
||||||
|
lightingMatGBuf.selectTechnique("GBuf", renderManager);
|
||||||
|
|
||||||
|
lightingMatGlow.setName("TechGlow");
|
||||||
|
lightingMatGlow.selectTechnique("Glow", renderManager);
|
||||||
|
|
||||||
|
testSort(lightingMatGlow, lightingPreShadow, lightingMatPreNormalPass,
|
||||||
|
lightingMatDefault, lightingPostShadow, lightingMatGBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AssertionError.class)
|
||||||
|
public void testNoSortByParam() {
|
||||||
|
Material sameMat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
Material sameMat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
|
||||||
|
sameMat1.setName("MatRed");
|
||||||
|
sameMat1.setColor("Color", ColorRGBA.Red);
|
||||||
|
|
||||||
|
sameMat2.setName("MatBlue");
|
||||||
|
sameMat2.setColor("Color", ColorRGBA.Blue);
|
||||||
|
|
||||||
|
testSort(sameMat1, sameMat2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Texture createTexture(String name) {
|
||||||
|
ByteBuffer bb = BufferUtils.createByteBuffer(3);
|
||||||
|
Image image = new Image(Format.RGB8, 1, 1, bb, ColorSpace.sRGB);
|
||||||
|
Texture2D texture = new Texture2D(image);
|
||||||
|
texture.setName(name);
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSortByTexture() {
|
||||||
|
Material texture1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
Material texture2Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
Material texture3Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
|
||||||
|
Texture tex1 = createTexture("A");
|
||||||
|
tex1.getImage().setId(1);
|
||||||
|
|
||||||
|
Texture tex2 = createTexture("B");
|
||||||
|
tex2.getImage().setId(2);
|
||||||
|
|
||||||
|
Texture tex3 = createTexture("C");
|
||||||
|
tex3.getImage().setId(3);
|
||||||
|
|
||||||
|
texture1Mat.setName("TexA");
|
||||||
|
texture1Mat.setTexture("ColorMap", tex1);
|
||||||
|
|
||||||
|
texture2Mat.setName("TexB");
|
||||||
|
texture2Mat.setTexture("ColorMap", tex2);
|
||||||
|
|
||||||
|
texture3Mat.setName("TexC");
|
||||||
|
texture3Mat.setTexture("ColorMap", tex3);
|
||||||
|
|
||||||
|
testSort(texture1Mat, texture2Mat, texture3Mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSortByShaderDefines() {
|
||||||
|
Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingMatVColor = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingMatVLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingMatTC = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingMatVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material lightingMatTCVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
|
||||||
|
lightingMat.setName("DefNone");
|
||||||
|
|
||||||
|
lightingMatVColor.setName("DefVC");
|
||||||
|
lightingMatVColor.setBoolean("UseVertexColor", true);
|
||||||
|
|
||||||
|
lightingMatVLight.setName("DefVL");
|
||||||
|
lightingMatVLight.setBoolean("VertexLighting", true);
|
||||||
|
|
||||||
|
lightingMatTC.setName("DefTC");
|
||||||
|
lightingMatTC.setBoolean("SeparateTexCoord", true);
|
||||||
|
|
||||||
|
lightingMatVColorLight.setName("DefVCVL");
|
||||||
|
lightingMatVColorLight.setBoolean("UseVertexColor", true);
|
||||||
|
lightingMatVColorLight.setBoolean("VertexLighting", true);
|
||||||
|
|
||||||
|
lightingMatTCVColorLight.setName("DefVCVLTC");
|
||||||
|
lightingMatTCVColorLight.setBoolean("UseVertexColor", true);
|
||||||
|
lightingMatTCVColorLight.setBoolean("VertexLighting", true);
|
||||||
|
lightingMatTCVColorLight.setBoolean("SeparateTexCoord", true);
|
||||||
|
|
||||||
|
testSort(lightingMat, lightingMatVColor, lightingMatVLight,
|
||||||
|
lightingMatVColorLight, lightingMatTC, lightingMatTCVColorLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSortByAll() {
|
||||||
|
Material matBase1 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
|
Material matBase2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
|
||||||
|
Texture texBase = createTexture("BASE");
|
||||||
|
texBase.getImage().setId(1);
|
||||||
|
Texture tex1 = createTexture("1");
|
||||||
|
tex1.getImage().setId(2);
|
||||||
|
Texture tex2 = createTexture("2");
|
||||||
|
tex2.getImage().setId(3);
|
||||||
|
|
||||||
|
matBase1.setName("BASE");
|
||||||
|
matBase1.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
|
||||||
|
matBase1.setBoolean("UseVertexColor", true);
|
||||||
|
matBase1.setTexture("DiffuseMap", texBase);
|
||||||
|
|
||||||
|
Material mat1100 = matBase1.clone();
|
||||||
|
mat1100.setName("1100");
|
||||||
|
mat1100.selectTechnique("PreShadow", renderManager);
|
||||||
|
|
||||||
|
Material mat1101 = matBase1.clone();
|
||||||
|
mat1101.setName("1101");
|
||||||
|
mat1101.selectTechnique("PreShadow", renderManager);
|
||||||
|
mat1101.setTexture("DiffuseMap", tex1);
|
||||||
|
|
||||||
|
Material mat1102 = matBase1.clone();
|
||||||
|
mat1102.setName("1102");
|
||||||
|
mat1102.selectTechnique("PreShadow", renderManager);
|
||||||
|
mat1102.setTexture("DiffuseMap", tex2);
|
||||||
|
|
||||||
|
Material mat1110 = matBase1.clone();
|
||||||
|
mat1110.setName("1110");
|
||||||
|
mat1110.selectTechnique("PreShadow", renderManager);
|
||||||
|
mat1110.setFloat("AlphaDiscardThreshold", 2f);
|
||||||
|
|
||||||
|
Material mat1120 = matBase1.clone();
|
||||||
|
mat1120.setName("1120");
|
||||||
|
mat1120.selectTechnique("PreShadow", renderManager);
|
||||||
|
mat1120.setBoolean("UseInstancing", true);
|
||||||
|
|
||||||
|
Material mat1121 = matBase1.clone();
|
||||||
|
mat1121.setName("1121");
|
||||||
|
mat1121.selectTechnique("PreShadow", renderManager);
|
||||||
|
mat1121.setBoolean("UseInstancing", true);
|
||||||
|
mat1121.setTexture("DiffuseMap", tex1);
|
||||||
|
|
||||||
|
Material mat1122 = matBase1.clone();
|
||||||
|
mat1122.setName("1122");
|
||||||
|
mat1122.selectTechnique("PreShadow", renderManager);
|
||||||
|
mat1122.setBoolean("UseInstancing", true);
|
||||||
|
mat1122.setTexture("DiffuseMap", tex2);
|
||||||
|
|
||||||
|
Material mat1140 = matBase1.clone();
|
||||||
|
mat1140.setName("1140");
|
||||||
|
mat1140.selectTechnique("PreShadow", renderManager);
|
||||||
|
mat1140.setFloat("AlphaDiscardThreshold", 2f);
|
||||||
|
mat1140.setBoolean("UseInstancing", true);
|
||||||
|
|
||||||
|
Material mat1200 = matBase1.clone();
|
||||||
|
mat1200.setName("1200");
|
||||||
|
mat1200.selectTechnique("PostShadow", renderManager);
|
||||||
|
|
||||||
|
Material mat1210 = matBase1.clone();
|
||||||
|
mat1210.setName("1210");
|
||||||
|
mat1210.selectTechnique("PostShadow", renderManager);
|
||||||
|
mat1210.setFloat("AlphaDiscardThreshold", 2f);
|
||||||
|
|
||||||
|
Material mat1220 = matBase1.clone();
|
||||||
|
mat1220.setName("1220");
|
||||||
|
mat1220.selectTechnique("PostShadow", renderManager);
|
||||||
|
mat1220.setBoolean("UseInstancing", true);
|
||||||
|
|
||||||
|
Material mat2000 = matBase2.clone();
|
||||||
|
mat2000.setName("2000");
|
||||||
|
|
||||||
|
testSort(mat1100, mat1101, mat1102, mat1110,
|
||||||
|
mat1120, mat1121, mat1122, mat1140,
|
||||||
|
mat1200, mat1210, mat1220, mat2000);
|
||||||
|
}
|
||||||
|
}
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -29,48 +29,50 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
package com.jme3.effect;
|
package com.jme3.system;
|
||||||
|
|
||||||
import com.jme3.renderer.Camera;
|
import com.jme3.audio.AudioRenderer;
|
||||||
import java.util.Comparator;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
@Deprecated
|
public class MockJmeSystemDelegate extends JmeSystemDelegate {
|
||||||
class ParticleComparator implements Comparator<Particle> {
|
|
||||||
|
|
||||||
private Camera cam;
|
@Override
|
||||||
|
public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException {
|
||||||
public void setCamera(Camera cam){
|
|
||||||
this.cam = cam;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int compare(Particle p1, Particle p2) {
|
@Override
|
||||||
return 0; // unused
|
public void showErrorDialog(String message) {
|
||||||
/*
|
|
||||||
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)
|
@Override
|
||||||
return 1;
|
public boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry) {
|
||||||
else if (d1 > d2)
|
return false;
|
||||||
return -1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -29,27 +29,27 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
package com.jme3.terrain.heightmap;
|
package com.jme3.system;
|
||||||
|
|
||||||
/**
|
import com.jme3.asset.AssetConfig;
|
||||||
* A heightmap that is built off an image.
|
import com.jme3.asset.AssetManager;
|
||||||
* If you want to be able to supply different Image types to
|
import com.jme3.asset.DesktopAssetManager;
|
||||||
* ImageBaseHeightMapGrid, you need to implement this interface,
|
import com.jme3.renderer.RenderManager;
|
||||||
* and have that class extend Abstract heightmap.
|
import java.util.logging.Level;
|
||||||
*
|
import java.util.logging.Logger;
|
||||||
* @author bowens
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
public interface ImageHeightmap {
|
|
||||||
|
|
||||||
/**
|
public class TestUtil {
|
||||||
* Set the image to use for this heightmap
|
|
||||||
*/
|
|
||||||
//public void setImage(Image image);
|
|
||||||
|
|
||||||
/**
|
static {
|
||||||
* The BufferedImage.TYPE_ that is supported
|
JmeSystem.setSystemDelegate(new MockJmeSystemDelegate());
|
||||||
* 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);
|
geom.setMesh(mesh);
|
||||||
|
|
||||||
Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md");
|
Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md");
|
||||||
mat.getAdditionalRenderState().setAlphaTest(true);
|
|
||||||
Texture diffuseMap = atlas.getAtlasTexture("DiffuseMap");
|
Texture diffuseMap = atlas.getAtlasTexture("DiffuseMap");
|
||||||
Texture normalMap = atlas.getAtlasTexture("NormalMap");
|
Texture normalMap = atlas.getAtlasTexture("NormalMap");
|
||||||
Texture specularMap = atlas.getAtlasTexture("SpecularMap");
|
Texture specularMap = atlas.getAtlasTexture("SpecularMap");
|
||||||
|
@ -6,11 +6,12 @@ import com.jme3.asset.plugins.FileLocator;
|
|||||||
import com.jme3.material.MaterialDef;
|
import com.jme3.material.MaterialDef;
|
||||||
import com.jme3.material.TechniqueDef;
|
import com.jme3.material.TechniqueDef;
|
||||||
import com.jme3.material.plugins.J3MLoader;
|
import com.jme3.material.plugins.J3MLoader;
|
||||||
|
import com.jme3.renderer.Caps;
|
||||||
import com.jme3.shader.DefineList;
|
import com.jme3.shader.DefineList;
|
||||||
import com.jme3.shader.Shader;
|
import com.jme3.shader.Shader;
|
||||||
import com.jme3.shader.ShaderKey;
|
|
||||||
import com.jme3.shader.plugins.GLSLLoader;
|
import com.jme3.shader.plugins.GLSLLoader;
|
||||||
import com.jme3.system.JmeSystem;
|
import com.jme3.system.JmeSystem;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -35,21 +36,20 @@ public class ShaderCheck {
|
|||||||
|
|
||||||
private static void checkMatDef(String matdefName) {
|
private static void checkMatDef(String matdefName) {
|
||||||
MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName);
|
MaterialDef def = (MaterialDef) assetManager.loadAsset(matdefName);
|
||||||
for (TechniqueDef techDef : def.getDefaultTechniques()){
|
EnumSet<Caps> rendererCaps = EnumSet.noneOf(Caps.class);
|
||||||
DefineList dl = new DefineList();
|
rendererCaps.add(Caps.GLSL100);
|
||||||
dl.addFrom(techDef.getShaderPresetDefines());
|
for (TechniqueDef techDef : def.getTechniqueDefs(TechniqueDef.DEFAULT_TECHNIQUE_NAME)) {
|
||||||
ShaderKey shaderKey = new ShaderKey(dl,techDef.getShaderProgramLanguages(),techDef.getShaderProgramNames());
|
DefineList defines = techDef.createDefineList();
|
||||||
|
Shader shader = techDef.getShader(assetManager, rendererCaps, defines);
|
||||||
Shader shader = assetManager.loadShader(shaderKey);
|
|
||||||
|
|
||||||
for (Validator validator : validators) {
|
for (Validator validator : validators) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
validator.validate(shader, sb);
|
validator.validate(shader, sb);
|
||||||
System.out.println("==== Validator: " + validator.getName() + " " +
|
System.out.println("==== Validator: " + validator.getName() + " "
|
||||||
validator.getInstalledVersion() + " ====");
|
+ validator.getInstalledVersion() + " ====");
|
||||||
System.out.println(sb.toString());
|
System.out.println(sb.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args){
|
public static void main(String[] args){
|
||||||
|
@ -43,6 +43,7 @@ import com.jme3.system.JmeContext.Type;
|
|||||||
import com.jme3.util.Screenshots;
|
import com.jme3.util.Screenshots;
|
||||||
import java.awt.EventQueue;
|
import java.awt.EventQueue;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.image.AffineTransformOp;
|
import java.awt.image.AffineTransformOp;
|
||||||
@ -116,12 +117,16 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showErrorDialog(String message) {
|
public void showErrorDialog(String message) {
|
||||||
|
if (!GraphicsEnvironment.isHeadless()) {
|
||||||
final String msg = message;
|
final String msg = message;
|
||||||
EventQueue.invokeLater(new Runnable() {
|
EventQueue.invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
ErrorDialog.showDialog(msg);
|
ErrorDialog.showDialog(msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
System.err.println("[JME ERROR] " + message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -129,6 +134,9 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
|
|||||||
if (SwingUtilities.isEventDispatchThread()) {
|
if (SwingUtilities.isEventDispatchThread()) {
|
||||||
throw new IllegalStateException("Cannot run from EDT");
|
throw new IllegalStateException("Cannot run from EDT");
|
||||||
}
|
}
|
||||||
|
if (GraphicsEnvironment.isHeadless()) {
|
||||||
|
throw new IllegalStateException("Cannot show dialog in headless environment");
|
||||||
|
}
|
||||||
|
|
||||||
final AppSettings settings = new AppSettings(false);
|
final AppSettings settings = new AppSettings(false);
|
||||||
settings.copyFrom(sourceSettings);
|
settings.copyFrom(sourceSettings);
|
||||||
@ -333,27 +341,13 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
|
|||||||
if (initialized) {
|
if (initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
try {
|
|
||||||
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());
|
logger.log(Level.INFO, getBuildInfo());
|
||||||
|
if (!lowPermissions) {
|
||||||
|
if (NativeLibraryLoader.isUsingNativeBullet()) {
|
||||||
|
NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,359 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
package com.jme3.system;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class for extracting the natives (dll, so) from the jars.
|
|
||||||
* This class should only be used internally.
|
|
||||||
*
|
|
||||||
* @deprecated Use {@link NativeLibraryLoader} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public final class Natives {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Natives.class.getName());
|
|
||||||
private static final byte[] buf = new byte[1024 * 100];
|
|
||||||
private static File extractionDirOverride = null;
|
|
||||||
private static File extractionDir = null;
|
|
||||||
|
|
||||||
public static void setExtractionDir(String name) {
|
|
||||||
extractionDirOverride = new File(name).getAbsoluteFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getExtractionDir() {
|
|
||||||
if (extractionDirOverride != null) {
|
|
||||||
return extractionDirOverride;
|
|
||||||
}
|
|
||||||
if (extractionDir == null) {
|
|
||||||
File workingFolder = new File("").getAbsoluteFile();
|
|
||||||
if (!workingFolder.canWrite()) {
|
|
||||||
setStorageExtractionDir();
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
File file = new File(workingFolder.getAbsolutePath() + File.separator + ".jmetestwrite");
|
|
||||||
file.createNewFile();
|
|
||||||
file.delete();
|
|
||||||
extractionDir = workingFolder;
|
|
||||||
} catch (Exception e) {
|
|
||||||
setStorageExtractionDir();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return extractionDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setStorageExtractionDir() {
|
|
||||||
logger.log(Level.WARNING, "Working directory is not writable. Using home directory instead.");
|
|
||||||
extractionDir = new File(JmeSystem.getStorageFolder(),
|
|
||||||
"natives_" + Integer.toHexString(computeNativesHash()));
|
|
||||||
if (!extractionDir.exists()) {
|
|
||||||
extractionDir.mkdir();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int computeNativesHash() {
|
|
||||||
URLConnection conn = null;
|
|
||||||
try {
|
|
||||||
String classpath = System.getProperty("java.class.path");
|
|
||||||
URL url = Thread.currentThread().getContextClassLoader().getResource("com/jme3/system/Natives.class");
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(url.toString());
|
|
||||||
if (sb.indexOf("jar:") == 0) {
|
|
||||||
sb.delete(0, 4);
|
|
||||||
sb.delete(sb.indexOf("!"), sb.length());
|
|
||||||
sb.delete(sb.lastIndexOf("/") + 1, sb.length());
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
url = new URL(sb.toString());
|
|
||||||
} catch (MalformedURLException ex) {
|
|
||||||
throw new UnsupportedOperationException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
conn = url.openConnection();
|
|
||||||
int hash = classpath.hashCode() ^ (int) conn.getLastModified();
|
|
||||||
return hash;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new UnsupportedOperationException(ex);
|
|
||||||
} finally {
|
|
||||||
if (conn != null) {
|
|
||||||
try {
|
|
||||||
conn.getInputStream().close();
|
|
||||||
conn.getOutputStream().close();
|
|
||||||
} catch (IOException ex) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void extractNativeLib(String sysName, String name) throws IOException {
|
|
||||||
extractNativeLib(sysName, name, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void extractNativeLib(String sysName, String name, boolean load) throws IOException {
|
|
||||||
extractNativeLib(sysName, name, load, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void extractNativeLib(String sysName, String name, boolean load, boolean warning) throws IOException {
|
|
||||||
String fullname;
|
|
||||||
String path;
|
|
||||||
//XXX: hack to allow specifying the extension via supplying an extension in the name (e.g. "blah.dylib")
|
|
||||||
// this is needed on osx where the openal.dylib always needs to be extracted as dylib
|
|
||||||
// and never as jnilib, even if that is the platform JNI lib suffix (OpenAL is no JNI library)
|
|
||||||
if(!name.contains(".")){
|
|
||||||
// automatic name mapping
|
|
||||||
fullname = System.mapLibraryName(name);
|
|
||||||
path = "native/" + sysName + "/" + fullname;
|
|
||||||
//XXX: Hack to extract jnilib to dylib on OSX Java 1.7+
|
|
||||||
// This assumes all jni libs for osx are stored as "jnilib" in the jar file.
|
|
||||||
// It will be extracted with the name required for the platform.
|
|
||||||
// At a later stage this should probably inverted so that dylib is the default name.
|
|
||||||
if(sysName.equals("macosx")){
|
|
||||||
path = path.replaceAll("dylib","jnilib");
|
|
||||||
}
|
|
||||||
} else{
|
|
||||||
fullname = name;
|
|
||||||
path = "native/" + sysName + "/" + fullname;
|
|
||||||
}
|
|
||||||
|
|
||||||
URL url = Thread.currentThread().getContextClassLoader().getResource(path);
|
|
||||||
|
|
||||||
// Also check for binaries that are not packed in folders by jme team, e.g. maven artifacts
|
|
||||||
if(url == null){
|
|
||||||
path = fullname;
|
|
||||||
if(sysName.equals("macosx") && !name.contains(".")){
|
|
||||||
path = path.replaceAll("dylib","jnilib");
|
|
||||||
}
|
|
||||||
url = Thread.currentThread().getContextClassLoader().getResource(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(url == null){
|
|
||||||
if (!warning) {
|
|
||||||
logger.log(Level.WARNING, "Cannot locate native library in classpath: {0}/{1}",
|
|
||||||
new String[]{sysName, fullname});
|
|
||||||
}
|
|
||||||
// Still try loading the library without a filename, maybe it is
|
|
||||||
// accessible otherwise
|
|
||||||
try{
|
|
||||||
System.loadLibrary(name);
|
|
||||||
} catch(UnsatisfiedLinkError e){
|
|
||||||
if (!warning) {
|
|
||||||
logger.log(Level.WARNING, "Cannot load native library: {0}/{1}",
|
|
||||||
new String[]{sysName, fullname});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
URLConnection conn = url.openConnection();
|
|
||||||
InputStream in = conn.getInputStream();
|
|
||||||
File targetFile = new File(getExtractionDir(), fullname);
|
|
||||||
OutputStream out = null;
|
|
||||||
try {
|
|
||||||
if (targetFile.exists()) {
|
|
||||||
// OK, compare last modified date of this file to
|
|
||||||
// file in jar
|
|
||||||
long targetLastModified = targetFile.lastModified();
|
|
||||||
long sourceLastModified = conn.getLastModified();
|
|
||||||
|
|
||||||
// Allow ~1 second range for OSes that only support low precision
|
|
||||||
if (targetLastModified + 1000 > sourceLastModified) {
|
|
||||||
logger.log(Level.FINE, "Not copying library {0}. Latest already extracted.", fullname);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out = new FileOutputStream(targetFile);
|
|
||||||
int len;
|
|
||||||
while ((len = in.read(buf)) > 0) {
|
|
||||||
out.write(buf, 0, len);
|
|
||||||
}
|
|
||||||
in.close();
|
|
||||||
in = null;
|
|
||||||
out.close();
|
|
||||||
out = null;
|
|
||||||
|
|
||||||
// NOTE: On OSes that support "Date Created" property,
|
|
||||||
// this will cause the last modified date to be lower than
|
|
||||||
// date created which makes no sense
|
|
||||||
targetFile.setLastModified(conn.getLastModified());
|
|
||||||
} catch (FileNotFoundException ex) {
|
|
||||||
if (ex.getMessage().contains("used by another process")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ex;
|
|
||||||
} finally {
|
|
||||||
if (load) {
|
|
||||||
System.load(targetFile.getAbsolutePath());
|
|
||||||
}
|
|
||||||
if(in != null){
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
if(out != null){
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.log(Level.FINE, "Copied {0} to {1}", new Object[]{fullname, targetFile});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static boolean isUsingNativeBullet() {
|
|
||||||
try {
|
|
||||||
Class clazz = Class.forName("com.jme3.bullet.util.NativeMeshUtil");
|
|
||||||
return clazz != null;
|
|
||||||
} catch (ClassNotFoundException ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void extractNativeLibs(Platform platform, AppSettings settings) throws IOException {
|
|
||||||
if (true) {
|
|
||||||
throw new UnsupportedEncodingException("Now, why would you EVER want to do that?");
|
|
||||||
}
|
|
||||||
|
|
||||||
String renderer = settings.getRenderer();
|
|
||||||
String audioRenderer = settings.getAudioRenderer();
|
|
||||||
boolean needLWJGL = false;
|
|
||||||
boolean needOAL = false;
|
|
||||||
boolean needJInput = false;
|
|
||||||
boolean needNativeBullet = isUsingNativeBullet();
|
|
||||||
|
|
||||||
if (renderer != null) {
|
|
||||||
if (renderer.startsWith("LWJGL")) {
|
|
||||||
needLWJGL = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (audioRenderer != null) {
|
|
||||||
if (audioRenderer.equals("LWJGL")) {
|
|
||||||
needLWJGL = true;
|
|
||||||
needOAL = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
needJInput = settings.useJoysticks();
|
|
||||||
|
|
||||||
String libraryPath = getExtractionDir().toString();
|
|
||||||
if (needLWJGL) {
|
|
||||||
logger.log(Level.INFO, "Extraction Directory: {0}", getExtractionDir().toString());
|
|
||||||
|
|
||||||
// LWJGL supports this feature where
|
|
||||||
// it can load libraries from this path.
|
|
||||||
System.setProperty("org.lwjgl.librarypath", libraryPath);
|
|
||||||
}
|
|
||||||
if (needJInput) {
|
|
||||||
// AND Luckily enough JInput supports the same feature.
|
|
||||||
System.setProperty("net.java.games.input.librarypath", libraryPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (platform) {
|
|
||||||
case Windows64:
|
|
||||||
if (needLWJGL) {
|
|
||||||
extractNativeLib("windows", "lwjgl64");
|
|
||||||
}
|
|
||||||
if (needOAL) {
|
|
||||||
extractNativeLib("windows", "OpenAL64", true, false);
|
|
||||||
}
|
|
||||||
if (needJInput) {
|
|
||||||
extractNativeLib("windows", "jinput-dx8_64");
|
|
||||||
extractNativeLib("windows", "jinput-raw_64");
|
|
||||||
}
|
|
||||||
if (needNativeBullet) {
|
|
||||||
extractNativeLib("windows", "bulletjme64", true, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Windows32:
|
|
||||||
if (needLWJGL) {
|
|
||||||
extractNativeLib("windows", "lwjgl");
|
|
||||||
}
|
|
||||||
if (needOAL) {
|
|
||||||
extractNativeLib("windows", "OpenAL32", true, false);
|
|
||||||
}
|
|
||||||
if (needJInput) {
|
|
||||||
extractNativeLib("windows", "jinput-dx8");
|
|
||||||
extractNativeLib("windows", "jinput-raw");
|
|
||||||
}
|
|
||||||
if (needNativeBullet) {
|
|
||||||
extractNativeLib("windows", "bulletjme", true, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Linux64:
|
|
||||||
if (needLWJGL) {
|
|
||||||
extractNativeLib("linux", "lwjgl64");
|
|
||||||
}
|
|
||||||
if (needJInput) {
|
|
||||||
extractNativeLib("linux", "jinput-linux64");
|
|
||||||
}
|
|
||||||
if (needOAL) {
|
|
||||||
extractNativeLib("linux", "openal64");
|
|
||||||
}
|
|
||||||
if (needNativeBullet) {
|
|
||||||
extractNativeLib("linux", "bulletjme64", true, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Linux32:
|
|
||||||
if (needLWJGL) {
|
|
||||||
extractNativeLib("linux", "lwjgl");
|
|
||||||
}
|
|
||||||
if (needJInput) {
|
|
||||||
extractNativeLib("linux", "jinput-linux");
|
|
||||||
}
|
|
||||||
if (needOAL) {
|
|
||||||
extractNativeLib("linux", "openal");
|
|
||||||
}
|
|
||||||
if (needNativeBullet) {
|
|
||||||
extractNativeLib("linux", "bulletjme", true, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MacOSX_PPC32:
|
|
||||||
case MacOSX32:
|
|
||||||
case MacOSX_PPC64:
|
|
||||||
case MacOSX64:
|
|
||||||
if (needLWJGL) {
|
|
||||||
extractNativeLib("macosx", "lwjgl");
|
|
||||||
}
|
|
||||||
if (needOAL){
|
|
||||||
extractNativeLib("macosx", "openal.dylib");
|
|
||||||
}
|
|
||||||
if (needJInput) {
|
|
||||||
extractNativeLib("macosx", "jinput-osx");
|
|
||||||
}
|
|
||||||
if (needNativeBullet) {
|
|
||||||
extractNativeLib("macosx", "bulletjme", true, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,9 @@ task run(dependsOn: 'build', type:JavaExec) {
|
|||||||
jvmArgs "-Djava.awt.headless=true"
|
jvmArgs "-Djava.awt.headless=true"
|
||||||
}
|
}
|
||||||
|
|
||||||
systemProperty "java.util.logging.config.file", System.getProperty("java.util.logging.config.file")
|
if (System.properties['java.util.logging.config.file'] != null) {
|
||||||
|
systemProperty "java.util.logging.config.file", System.properties['java.util.logging.config.file']
|
||||||
|
}
|
||||||
|
|
||||||
if( assertions == "true" ){
|
if( assertions == "true" ){
|
||||||
enableAssertions = true;
|
enableAssertions = true;
|
||||||
|
@ -139,7 +139,8 @@ public class TestCameraMotionPath extends SimpleApplication {
|
|||||||
|
|
||||||
|
|
||||||
rootNode.attachChild(teapot);
|
rootNode.attachChild(teapot);
|
||||||
Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -1.0f, 0), 50, 1, 50));
|
Geometry soil = new Geometry("soil", new Box(50, 1, 50));
|
||||||
|
soil.setLocalTranslation(0, -1, 0);
|
||||||
soil.setMaterial(matSoil);
|
soil.setMaterial(matSoil);
|
||||||
rootNode.attachChild(soil);
|
rootNode.attachChild(soil);
|
||||||
DirectionalLight light = new DirectionalLight();
|
DirectionalLight light = new DirectionalLight();
|
||||||
|
@ -222,7 +222,8 @@ public class TestCinematic extends SimpleApplication {
|
|||||||
matSoil.setColor("Diffuse", ColorRGBA.Green);
|
matSoil.setColor("Diffuse", ColorRGBA.Green);
|
||||||
matSoil.setColor("Specular", ColorRGBA.Black);
|
matSoil.setColor("Specular", ColorRGBA.Black);
|
||||||
|
|
||||||
Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -6.0f, 0), 50, 1, 50));
|
Geometry soil = new Geometry("soil", new Box(50, 1, 50));
|
||||||
|
soil.setLocalTranslation(0, -6, 0);
|
||||||
soil.setMaterial(matSoil);
|
soil.setMaterial(matSoil);
|
||||||
soil.setShadowMode(ShadowMode.Receive);
|
soil.setShadowMode(ShadowMode.Receive);
|
||||||
rootNode.attachChild(soil);
|
rootNode.attachChild(soil);
|
||||||
|
@ -136,7 +136,8 @@ public class TestMotionPath extends SimpleApplication {
|
|||||||
|
|
||||||
|
|
||||||
rootNode.attachChild(teapot);
|
rootNode.attachChild(teapot);
|
||||||
Geometry soil = new Geometry("soil", new Box(new Vector3f(0, -1.0f, 0), 50, 1, 50));
|
Geometry soil = new Geometry("soil", new Box(50, 1, 50));
|
||||||
|
soil.setLocalTranslation(0, -1, 0);
|
||||||
soil.setMaterial(matSoil);
|
soil.setMaterial(matSoil);
|
||||||
|
|
||||||
rootNode.attachChild(soil);
|
rootNode.attachChild(soil);
|
||||||
|
@ -57,7 +57,7 @@ public class TestAppStateLifeCycle extends SimpleApplication {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
Box b = new Box(Vector3f.ZERO, 1, 1, 1);
|
Box b = new Box(1, 1, 1);
|
||||||
Geometry geom = new Geometry("Box", b);
|
Geometry geom = new Geometry("Box", b);
|
||||||
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
|
mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
|
||||||
|
@ -56,7 +56,7 @@ public class TestBareBonesApp extends LegacyApplication {
|
|||||||
System.out.println("Initialize");
|
System.out.println("Initialize");
|
||||||
|
|
||||||
// create a box
|
// create a box
|
||||||
boxGeom = new Geometry("Box", new Box(Vector3f.ZERO, 2, 2, 2));
|
boxGeom = new Geometry("Box", new Box(2, 2, 2));
|
||||||
|
|
||||||
// load some default material
|
// load some default material
|
||||||
boxGeom.setMaterial(assetManager.loadMaterial("Interface/Logo/Logo.j3m"));
|
boxGeom.setMaterial(assetManager.loadMaterial("Interface/Logo/Logo.j3m"));
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
package jme3test.app;
|
|
||||||
|
|
||||||
import com.jme3.system.NativeLibraryLoader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to load some natives.
|
|
||||||
*
|
|
||||||
* @author Kirill Vainer
|
|
||||||
*/
|
|
||||||
public class TestNativeLoader {
|
|
||||||
|
|
||||||
private static final File WORKING_FOLDER = new File(System.getProperty("user.dir"));
|
|
||||||
|
|
||||||
private static void tryLoadLwjgl() {
|
|
||||||
NativeLibraryLoader.loadNativeLibrary("lwjgl", true);
|
|
||||||
System.out.println("Succeeded in loading LWJGL.\n\tVersion: " +
|
|
||||||
org.lwjgl.Sys.getVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void tryLoadJinput() {
|
|
||||||
NativeLibraryLoader.loadNativeLibrary("jinput", true);
|
|
||||||
NativeLibraryLoader.loadNativeLibrary("jinput-dx8", true);
|
|
||||||
|
|
||||||
net.java.games.input.ControllerEnvironment ce =
|
|
||||||
net.java.games.input.ControllerEnvironment.getDefaultEnvironment();
|
|
||||||
if (ce.isSupported()) {
|
|
||||||
net.java.games.input.Controller[] c =
|
|
||||||
ce.getControllers();
|
|
||||||
|
|
||||||
System.out.println("Succeeded in loading JInput.\n\tVersion: " +
|
|
||||||
net.java.games.util.Version.getVersion());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void tryLoadOpenAL() {
|
|
||||||
NativeLibraryLoader.loadNativeLibrary("openal", true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
org.lwjgl.openal.AL.create();
|
|
||||||
String renderer = org.lwjgl.openal.AL10.alGetString(org.lwjgl.openal.AL10.AL_RENDERER);
|
|
||||||
String vendor = org.lwjgl.openal.AL10.alGetString(org.lwjgl.openal.AL10.AL_VENDOR);
|
|
||||||
String version = org.lwjgl.openal.AL10.alGetString(org.lwjgl.openal.AL10.AL_VERSION);
|
|
||||||
System.out.println("Succeeded in loading OpenAL.");
|
|
||||||
System.out.println("\tVersion: " + version);
|
|
||||||
} catch (org.lwjgl.LWJGLException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
} finally {
|
|
||||||
if (org.lwjgl.openal.AL.isCreated()) {
|
|
||||||
org.lwjgl.openal.AL.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void tryLoadOpenGL() {
|
|
||||||
org.lwjgl.opengl.Pbuffer pb = null;
|
|
||||||
try {
|
|
||||||
pb = new org.lwjgl.opengl.Pbuffer(1, 1, new org.lwjgl.opengl.PixelFormat(0, 0, 0), null);
|
|
||||||
pb.makeCurrent();
|
|
||||||
String version = org.lwjgl.opengl.GL11.glGetString(org.lwjgl.opengl.GL11.GL_VERSION);
|
|
||||||
System.out.println("Succeeded in loading OpenGL.\n\tVersion: " + version);
|
|
||||||
} catch (org.lwjgl.LWJGLException ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
} finally {
|
|
||||||
if (pb != null) {
|
|
||||||
pb.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void tryLoadBulletJme() {
|
|
||||||
if (NativeLibraryLoader.isUsingNativeBullet()) {
|
|
||||||
NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
|
|
||||||
|
|
||||||
com.jme3.bullet.PhysicsSpace physSpace = new com.jme3.bullet.PhysicsSpace();
|
|
||||||
|
|
||||||
System.out.println("Succeeded in loading BulletJme.");
|
|
||||||
} else {
|
|
||||||
System.out.println("Native bullet not included. Cannot test loading.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void cleanupNativesFolder(File folder) {
|
|
||||||
for (File file : folder.listFiles()) {
|
|
||||||
String lowerCaseName = file.getName().toLowerCase();
|
|
||||||
if (lowerCaseName.contains("lwjgl") ||
|
|
||||||
lowerCaseName.contains("jinput") ||
|
|
||||||
lowerCaseName.contains("openal") ||
|
|
||||||
lowerCaseName.contains("bulletjme")) {
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
Logger.getLogger("").getHandlers()[0].setLevel(Level.WARNING);
|
|
||||||
Logger.getLogger(NativeLibraryLoader.class.getName()).setLevel(Level.ALL);
|
|
||||||
|
|
||||||
// Get a bit more output from LWJGL about issues.
|
|
||||||
// System.setProperty("org.lwjgl.util.Debug", "true");
|
|
||||||
|
|
||||||
// Extracting to working folder is no brainer.
|
|
||||||
// Choose some random path, then load LWJGL.
|
|
||||||
File customNativesFolder = new File("CustomNativesFolder");
|
|
||||||
customNativesFolder.mkdirs();
|
|
||||||
|
|
||||||
if (!customNativesFolder.isDirectory()) {
|
|
||||||
throw new IllegalStateException("Failed to make custom natives folder");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let's cleanup our folders first.
|
|
||||||
cleanupNativesFolder(WORKING_FOLDER);
|
|
||||||
cleanupNativesFolder(customNativesFolder);
|
|
||||||
|
|
||||||
NativeLibraryLoader.setCustomExtractionFolder(customNativesFolder.getAbsolutePath());
|
|
||||||
|
|
||||||
tryLoadLwjgl();
|
|
||||||
tryLoadOpenGL();
|
|
||||||
tryLoadOpenAL();
|
|
||||||
tryLoadJinput();
|
|
||||||
tryLoadBulletJme();
|
|
||||||
}
|
|
||||||
}
|
|
@ -50,7 +50,7 @@ public class TestReleaseDirectMemory extends SimpleApplication {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
Box b = new Box(Vector3f.ZERO, 1, 1, 1);
|
Box b = new Box(1, 1, 1);
|
||||||
Geometry geom = new Geometry("Box", b);
|
Geometry geom = new Geometry("Box", b);
|
||||||
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
|
mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
|
||||||
|
@ -69,7 +69,7 @@ public class TestAmbient extends SimpleApplication {
|
|||||||
nature.play();
|
nature.play();
|
||||||
|
|
||||||
// just a blue box to mark the spot
|
// just a blue box to mark the spot
|
||||||
Box box1 = new Box(Vector3f.ZERO, .5f, .5f, .5f);
|
Box box1 = new Box(.5f, .5f, .5f);
|
||||||
Geometry player = new Geometry("Player", box1);
|
Geometry player = new Geometry("Player", box1);
|
||||||
Material mat1 = new Material(assetManager,
|
Material mat1 = new Material(assetManager,
|
||||||
"Common/MatDefs/Misc/Unshaded.j3md");
|
"Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user