commit
e837336116
@ -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."); |
|
||||||
} |
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
@ -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() { |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -1,286 +1,179 @@ |
|||||||
/* |
/* |
||||||
* Copyright (c) 2009-2012 jMonkeyEngine |
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
* All rights reserved. |
* All rights reserved. |
||||||
* |
* |
||||||
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
||||||
* modification, are permitted provided that the following conditions are |
* modification, are permitted provided that the following conditions are |
||||||
* met: |
* met: |
||||||
* |
* |
||||||
* * Redistributions of source code must retain the above copyright |
* * Redistributions of source code must retain the above copyright |
||||||
* notice, this list of conditions and the following disclaimer. |
* notice, this list of conditions and the following disclaimer. |
||||||
* |
* |
||||||
* * Redistributions in binary form must reproduce the above copyright |
* * Redistributions in binary form must reproduce the above copyright |
||||||
* notice, this list of conditions and the following disclaimer in the |
* notice, this list of conditions and the following disclaimer in the |
||||||
* documentation and/or other materials provided with the distribution. |
* documentation and/or other materials provided with the distribution. |
||||||
* |
* |
||||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
* may be used to endorse or promote products derived from this software |
* may be used to endorse or promote products derived from this software |
||||||
* without specific prior written permission. |
* without specific prior written permission. |
||||||
* |
* |
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
*/ |
*/ |
||||||
package com.jme3.shader; |
package com.jme3.shader; |
||||||
|
|
||||||
import com.jme3.export.*; |
import java.util.Arrays; |
||||||
import com.jme3.material.MatParam; |
import java.util.List; |
||||||
import com.jme3.material.TechniqueDef; |
|
||||||
import com.jme3.util.ListMap; |
/** |
||||||
|
* The new define list. |
||||||
import java.io.IOException; |
* |
||||||
import java.util.Map; |
* @author Kirill Vainer |
||||||
import java.util.TreeMap; |
*/ |
||||||
|
public final class DefineList { |
||||||
public final class DefineList implements Savable, Cloneable { |
|
||||||
|
public static final int MAX_DEFINES = 64; |
||||||
private static final String ONE = "1"; |
|
||||||
|
private long hash; |
||||||
private TreeMap<String, String> defines = new TreeMap<String, String>(); |
private final int[] vals; |
||||||
private String compiled = null; |
|
||||||
private int cachedHashCode = 0; |
public DefineList(int numValues) { |
||||||
|
if (numValues < 0 || numValues > MAX_DEFINES) { |
||||||
public void write(JmeExporter ex) throws IOException{ |
throw new IllegalArgumentException("numValues must be between 0 and 64"); |
||||||
OutputCapsule oc = ex.getCapsule(this); |
} |
||||||
|
vals = new int[numValues]; |
||||||
String[] keys = new String[defines.size()]; |
} |
||||||
String[] vals = new String[defines.size()]; |
|
||||||
|
private DefineList(DefineList original) { |
||||||
int i = 0; |
this.hash = original.hash; |
||||||
for (Map.Entry<String, String> define : defines.entrySet()){ |
this.vals = new int[original.vals.length]; |
||||||
keys[i] = define.getKey(); |
System.arraycopy(original.vals, 0, vals, 0, vals.length); |
||||||
vals[i] = define.getValue(); |
} |
||||||
i++; |
|
||||||
} |
public void set(int id, int val) { |
||||||
|
assert 0 <= id && id < 64; |
||||||
oc.write(keys, "keys", null); |
if (val != 0) { |
||||||
oc.write(vals, "vals", null); |
hash |= (1L << id); |
||||||
} |
} else { |
||||||
|
hash &= ~(1L << id); |
||||||
public void read(JmeImporter im) throws IOException{ |
} |
||||||
InputCapsule ic = im.getCapsule(this); |
vals[id] = val; |
||||||
|
} |
||||||
String[] keys = ic.readStringArray("keys", null); |
|
||||||
String[] vals = ic.readStringArray("vals", null); |
public void set(int id, float val) { |
||||||
for (int i = 0; i < keys.length; i++){ |
set(id, Float.floatToIntBits(val)); |
||||||
defines.put(keys[i], vals[i]); |
} |
||||||
} |
|
||||||
} |
public void set(int id, boolean val) { |
||||||
|
set(id, val ? 1 : 0); |
||||||
public void clear() { |
} |
||||||
defines.clear(); |
|
||||||
compiled = ""; |
public void set(int id, VarType type, Object value) { |
||||||
cachedHashCode = 0; |
if (value == null) { |
||||||
} |
set(id, 0); |
||||||
|
return; |
||||||
public String get(String key){ |
} |
||||||
return defines.get(key); |
|
||||||
} |
switch (type) { |
||||||
|
case Int: |
||||||
@Override |
set(id, (Integer) value); |
||||||
public DefineList clone() { |
break; |
||||||
try { |
case Float: |
||||||
DefineList clone = (DefineList) super.clone(); |
set(id, (Float) value); |
||||||
clone.cachedHashCode = 0; |
break; |
||||||
clone.compiled = null; |
case Boolean: |
||||||
clone.defines = (TreeMap<String, String>) defines.clone(); |
set(id, ((Boolean) value)); |
||||||
return clone; |
break; |
||||||
} catch (CloneNotSupportedException ex) { |
default: |
||||||
throw new AssertionError(); |
set(id, 1); |
||||||
} |
break; |
||||||
} |
} |
||||||
|
} |
||||||
public boolean set(String key, VarType type, Object val){ |
|
||||||
if (val == null){ |
public void setAll(DefineList other) { |
||||||
defines.remove(key); |
for (int i = 0; i < other.vals.length; i++) { |
||||||
compiled = null; |
if (other.vals[i] != 0) { |
||||||
cachedHashCode = 0; |
vals[i] = other.vals[i]; |
||||||
return true; |
} |
||||||
} |
} |
||||||
|
} |
||||||
switch (type){ |
|
||||||
case Boolean: |
public void clear() { |
||||||
if (((Boolean) val).booleanValue()) { |
hash = 0; |
||||||
// same literal, != will work
|
Arrays.fill(vals, 0); |
||||||
if (defines.put(key, ONE) != ONE) { |
} |
||||||
compiled = null; |
|
||||||
cachedHashCode = 0; |
public boolean getBoolean(int id) { |
||||||
return true; |
return vals[id] != 0; |
||||||
} |
} |
||||||
} else if (defines.containsKey(key)) { |
|
||||||
defines.remove(key); |
public float getFloat(int id) { |
||||||
compiled = null; |
return Float.intBitsToFloat(vals[id]); |
||||||
cachedHashCode = 0; |
} |
||||||
return true; |
|
||||||
} |
public int getInt(int id) { |
||||||
|
return vals[id]; |
||||||
break; |
} |
||||||
case Float: |
|
||||||
case Int: |
@Override |
||||||
String newValue = val.toString(); |
public int hashCode() { |
||||||
String original = defines.put(key, newValue); |
return (int)((hash >> 32) ^ hash); |
||||||
if (!val.equals(original)) { |
} |
||||||
compiled = null; |
|
||||||
cachedHashCode = 0; |
@Override |
||||||
return true; |
public boolean equals(Object other) { |
||||||
} |
DefineList o = (DefineList) other; |
||||||
break; |
if (hash == o.hash) { |
||||||
default: |
for (int i = 0; i < vals.length; i++) { |
||||||
// same literal, != will work
|
if (vals[i] != o.vals[i]) return false; |
||||||
if (defines.put(key, ONE) != ONE) { |
} |
||||||
compiled = null; |
return true; |
||||||
cachedHashCode = 0; |
} |
||||||
return true; |
return false; |
||||||
} |
} |
||||||
break; |
|
||||||
} |
public DefineList deepClone() { |
||||||
|
return new DefineList(this); |
||||||
return false; |
} |
||||||
} |
|
||||||
|
public void generateSource(StringBuilder sb, List<String> defineNames, List<VarType> defineTypes) { |
||||||
public boolean remove(String key){ |
for (int i = 0; i < vals.length; i++) { |
||||||
if (defines.remove(key) != null) { |
if (vals[i] != 0) { |
||||||
compiled = null; |
String defineName = defineNames.get(i); |
||||||
cachedHashCode = 0; |
|
||||||
return true; |
sb.append("#define "); |
||||||
} |
sb.append(defineName); |
||||||
return false; |
sb.append(" "); |
||||||
} |
|
||||||
|
if (defineTypes != null && defineTypes.get(i) == VarType.Float) { |
||||||
public void addFrom(DefineList other){ |
float val = Float.intBitsToFloat(vals[i]); |
||||||
if (other == null) { |
if (Float.isInfinite(val) || Float.isNaN(val)) { |
||||||
return; |
throw new IllegalArgumentException( |
||||||
} |
"GLSL does not support NaN " |
||||||
compiled = null; |
+ "or Infinite float literals"); |
||||||
cachedHashCode = 0; |
} |
||||||
defines.putAll(other.defines); |
sb.append(val); |
||||||
} |
} else { |
||||||
|
sb.append(vals[i]); |
||||||
public String getCompiled(){ |
} |
||||||
if (compiled == null){ |
|
||||||
StringBuilder sb = new StringBuilder(); |
sb.append("\n"); |
||||||
for (Map.Entry<String, String> entry : defines.entrySet()){ |
} |
||||||
sb.append("#define ").append(entry.getKey()).append(" "); |
} |
||||||
sb.append(entry.getValue()).append('\n'); |
} |
||||||
} |
|
||||||
compiled = sb.toString(); |
public String generateSource(List<String> defineNames, List<VarType> defineTypes) { |
||||||
} |
StringBuilder sb = new StringBuilder(); |
||||||
return compiled; |
generateSource(sb, defineNames, defineTypes); |
||||||
} |
return sb.toString(); |
||||||
|
} |
||||||
@Override |
} |
||||||
public boolean equals(Object obj) { |
|
||||||
final DefineList other = (DefineList) obj; |
|
||||||
return defines.equals(other.defines); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Update defines if the define list changed based on material parameters. |
|
||||||
* @param params |
|
||||||
* @param def |
|
||||||
* @return true if defines was updated |
|
||||||
*/ |
|
||||||
public boolean update(ListMap params, TechniqueDef def){ |
|
||||||
if(equalsParams(params, def)){ |
|
||||||
return false; |
|
||||||
} |
|
||||||
// Defines were changed, update define list
|
|
||||||
clear(); |
|
||||||
for(int i=0;i<params.size();i++) { |
|
||||||
MatParam param = (MatParam)params.getValue(i); |
|
||||||
String defineName = def.getShaderParamDefine(param.getName()); |
|
||||||
if (defineName != null) { |
|
||||||
set(defineName, param.getVarType(), param.getValue()); |
|
||||||
} |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
private boolean equalsParams(ListMap params, TechniqueDef def) { |
|
||||||
|
|
||||||
int size = 0; |
|
||||||
|
|
||||||
for(int i = 0; i < params.size() ; i++ ) { |
|
||||||
MatParam param = (MatParam)params.getValue(i); |
|
||||||
String key = def.getShaderParamDefine(param.getName()); |
|
||||||
if (key != null) { |
|
||||||
Object val = param.getValue(); |
|
||||||
if (val != null) { |
|
||||||
|
|
||||||
switch (param.getVarType()) { |
|
||||||
case Boolean: { |
|
||||||
String current = defines.get(key); |
|
||||||
if (((Boolean) val).booleanValue()) { |
|
||||||
if (current == null || current != ONE) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
size++; |
|
||||||
} else { |
|
||||||
if (current != null) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
break; |
|
||||||
case Float: |
|
||||||
case Int: { |
|
||||||
String newValue = val.toString(); |
|
||||||
String current = defines.get(key); |
|
||||||
if (!newValue.equals(current)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
size++; |
|
||||||
} |
|
||||||
break; |
|
||||||
default: { |
|
||||||
if (!defines.containsKey(key)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
size++; |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (size != defines.size()) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int hashCode() { |
|
||||||
if (cachedHashCode == 0) { |
|
||||||
cachedHashCode = defines.hashCode(); |
|
||||||
} |
|
||||||
return cachedHashCode; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String toString(){ |
|
||||||
StringBuilder sb = new StringBuilder(); |
|
||||||
int i = 0; |
|
||||||
for (Map.Entry<String, String> entry : defines.entrySet()) { |
|
||||||
sb.append(entry.getKey()).append("=").append(entry.getValue()); |
|
||||||
if (i != defines.size() - 1) { |
|
||||||
sb.append(", "); |
|
||||||
} |
|
||||||
i++; |
|
||||||
} |
|
||||||
return sb.toString(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -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)); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,600 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2016 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.material; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.light.LightList; |
||||||
|
import com.jme3.math.Matrix4f; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.shader.Shader; |
||||||
|
import com.jme3.shader.Uniform; |
||||||
|
import com.jme3.shader.VarType; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashSet; |
||||||
|
import org.junit.Assert; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import static com.jme3.scene.MPOTestUtils.*; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.shader.DefineList; |
||||||
|
import com.jme3.system.NullRenderer; |
||||||
|
import com.jme3.system.TestUtil; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.texture.Texture; |
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import org.junit.Before; |
||||||
|
|
||||||
|
/** |
||||||
|
* Validates how {@link MatParamOverride MPOs} work on the material level. |
||||||
|
* |
||||||
|
* @author Kirill Vainer |
||||||
|
*/ |
||||||
|
public class MaterialMatParamOverrideTest { |
||||||
|
|
||||||
|
private static final HashSet<String> IGNORED_UNIFORMS = new HashSet<String>( |
||||||
|
Arrays.asList(new String[]{"m_ParallaxHeight", "m_Shininess", "m_BackfaceShadows"})); |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testBoolMpoOnly() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMpo(mpoBool("UseMaterialColors", true)); |
||||||
|
outDefines(def("MATERIAL_COLORS", VarType.Boolean, true)); |
||||||
|
outUniforms(uniform("UseMaterialColors", VarType.Boolean, true)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testBoolMpOnly() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMp(mpoBool("UseMaterialColors", true)); |
||||||
|
outDefines(def("MATERIAL_COLORS", VarType.Boolean, true)); |
||||||
|
outUniforms(uniform("UseMaterialColors", VarType.Boolean, true)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testBoolOverrideFalse() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMp(mpoBool("UseMaterialColors", true)); |
||||||
|
inputMpo(mpoBool("UseMaterialColors", false)); |
||||||
|
outDefines(); |
||||||
|
outUniforms(uniform("UseMaterialColors", VarType.Boolean, false)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testBoolOverrideTrue() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMp(mpoBool("UseMaterialColors", false)); |
||||||
|
inputMpo(mpoBool("UseMaterialColors", true)); |
||||||
|
outDefines(def("MATERIAL_COLORS", VarType.Boolean, true)); |
||||||
|
outUniforms(uniform("UseMaterialColors", VarType.Boolean, true)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testFloatMpoOnly() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMpo(mpoFloat("AlphaDiscardThreshold", 3.12f)); |
||||||
|
outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f)); |
||||||
|
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testFloatMpOnly() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f)); |
||||||
|
outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f)); |
||||||
|
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testFloatOverride() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f)); |
||||||
|
inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f)); |
||||||
|
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f)); |
||||||
|
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testForcedOverride() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f)); |
||||||
|
inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f)); |
||||||
|
inputFpo(mpoFloat("AlphaDiscardThreshold", 1.23f)); |
||||||
|
outDefines(def("DISCARD_ALPHA", VarType.Float, 1.23f)); |
||||||
|
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 1.23f)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
root.clearMatParamOverrides(); |
||||||
|
root.updateGeometricState(); |
||||||
|
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f)); |
||||||
|
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testChildOverridesParent() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
|
||||||
|
inputParentMpo(mpoFloat("AlphaDiscardThreshold", 3.12f)); |
||||||
|
inputMpo(mpoFloat("AlphaDiscardThreshold", 2.79f)); |
||||||
|
|
||||||
|
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); |
||||||
|
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testMpoDisable() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMp(mpoFloat("AlphaDiscardThreshold", 3.12f)); |
||||||
|
|
||||||
|
MatParamOverride override = mpoFloat("AlphaDiscardThreshold", 2.79f); |
||||||
|
inputMpo(override); |
||||||
|
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f)); |
||||||
|
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
override.setEnabled(false); |
||||||
|
outDefines(def("DISCARD_ALPHA", VarType.Float, 3.12f)); |
||||||
|
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 3.12f)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
override.setEnabled(true); |
||||||
|
outDefines(def("DISCARD_ALPHA", VarType.Float, 2.79f)); |
||||||
|
outUniforms(uniform("AlphaDiscardThreshold", VarType.Float, 2.79f)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testIntMpoOnly() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMpo(mpoInt("NumberOfBones", 1234)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 1234)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testIntMpOnly() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMp(mpoInt("NumberOfBones", 1234)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 1234)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testIntOverride() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMp(mpoInt("NumberOfBones", 1234)); |
||||||
|
inputMpo(mpoInt("NumberOfBones", 4321)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 4321)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testMatrixArray() { |
||||||
|
Matrix4f[] matrices = new Matrix4f[]{ |
||||||
|
new Matrix4f() |
||||||
|
}; |
||||||
|
|
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMpo(mpoMatrix4Array("BoneMatrices", matrices)); |
||||||
|
outDefines(); |
||||||
|
outUniforms(uniform("BoneMatrices", VarType.Matrix4Array, matrices)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testNonExistentParameter() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMpo(mpoInt("NonExistent", 4321)); |
||||||
|
outDefines(); |
||||||
|
outUniforms(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testWrongType() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMpo(mpoInt("UseMaterialColors", 4321)); |
||||||
|
outDefines(); |
||||||
|
outUniforms(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testParamOnly() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
inputMpo(mpoFloat("ShadowMapSize", 3.12f)); |
||||||
|
outDefines(); |
||||||
|
outUniforms(uniform("ShadowMapSize", VarType.Float, 3.12f)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testRemove() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMp(mpoInt("NumberOfBones", 1234)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 1234)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMpo(mpoInt("NumberOfBones", 4321)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 4321)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
geometry.clearMatParamOverrides(); |
||||||
|
root.updateGeometricState(); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 1234)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
geometry.getMaterial().clearParam("NumberOfBones"); |
||||||
|
outDefines(); |
||||||
|
outUniforms(); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMpo(mpoInt("NumberOfBones", 4321)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 4321)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMp(mpoInt("NumberOfBones", 1234)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 4321)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); |
||||||
|
} |
||||||
|
|
||||||
|
public void testRemoveOverride() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMp(mpoInt("NumberOfBones", 1234)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 1234)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMpo(mpoInt("NumberOfBones", 4321)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 4321)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
geometry.clearMatParamOverrides(); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 1234)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 1234)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testRemoveMpoOnly() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMpo(mpoInt("NumberOfBones", 4321)); |
||||||
|
outDefines(def("NUM_BONES", VarType.Int, 4321)); |
||||||
|
outUniforms(uniform("NumberOfBones", VarType.Int, 4321)); |
||||||
|
|
||||||
|
reset(); |
||||||
|
geometry.clearMatParamOverrides(); |
||||||
|
root.updateGeometricState(); |
||||||
|
outDefines(); |
||||||
|
outUniforms(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testTextureMpoOnly() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Texture2D tex = new Texture2D(128, 128, Format.RGBA8); |
||||||
|
|
||||||
|
inputMpo(mpoTexture2D("DiffuseMap", tex)); |
||||||
|
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex)); |
||||||
|
outUniforms(uniform("DiffuseMap", VarType.Int, 0)); |
||||||
|
outTextures(tex); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testTextureOverride() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Texture2D tex1 = new Texture2D(128, 128, Format.RGBA8); |
||||||
|
Texture2D tex2 = new Texture2D(128, 128, Format.RGBA8); |
||||||
|
|
||||||
|
inputMp(mpoTexture2D("DiffuseMap", tex1)); |
||||||
|
inputMpo(mpoTexture2D("DiffuseMap", tex2)); |
||||||
|
|
||||||
|
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex2)); |
||||||
|
outUniforms(uniform("DiffuseMap", VarType.Int, 0)); |
||||||
|
outTextures(tex2); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testRemoveTexture() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Texture2D tex = new Texture2D(128, 128, Format.RGBA8); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMpo(mpoTexture2D("DiffuseMap", tex)); |
||||||
|
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex)); |
||||||
|
outUniforms(uniform("DiffuseMap", VarType.Int, 0)); |
||||||
|
outTextures(tex); |
||||||
|
|
||||||
|
reset(); |
||||||
|
geometry.clearMatParamOverrides(); |
||||||
|
root.updateGeometricState(); |
||||||
|
outDefines(); |
||||||
|
outUniforms(); |
||||||
|
outTextures(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testRemoveTextureOverride() { |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Texture2D tex1 = new Texture2D(128, 128, Format.RGBA8); |
||||||
|
Texture2D tex2 = new Texture2D(128, 128, Format.RGBA8); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMp(mpoTexture2D("DiffuseMap", tex1)); |
||||||
|
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1)); |
||||||
|
outUniforms(uniform("DiffuseMap", VarType.Int, 0)); |
||||||
|
outTextures(tex1); |
||||||
|
|
||||||
|
reset(); |
||||||
|
inputMpo(mpoTexture2D("DiffuseMap", tex2)); |
||||||
|
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex2)); |
||||||
|
outUniforms(uniform("DiffuseMap", VarType.Int, 0)); |
||||||
|
outTextures(tex2); |
||||||
|
|
||||||
|
reset(); |
||||||
|
geometry.clearMatParamOverrides(); |
||||||
|
root.updateGeometricState(); |
||||||
|
outDefines(def("DIFFUSEMAP", VarType.Texture2D, tex1)); |
||||||
|
outUniforms(uniform("DiffuseMap", VarType.Int, 0)); |
||||||
|
outTextures(tex1); |
||||||
|
} |
||||||
|
|
||||||
|
private static final class Define { |
||||||
|
|
||||||
|
public String name; |
||||||
|
public VarType type; |
||||||
|
public Object value; |
||||||
|
|
||||||
|
@Override |
||||||
|
public int hashCode() { |
||||||
|
int hash = 3; |
||||||
|
hash = 89 * hash + this.name.hashCode(); |
||||||
|
hash = 89 * hash + this.type.hashCode(); |
||||||
|
hash = 89 * hash + this.value.hashCode(); |
||||||
|
return hash; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object obj) { |
||||||
|
final Define other = (Define) obj; |
||||||
|
return this.name.equals(other.name) && this.type.equals(other.type) && this.value.equals(other.value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private final Geometry geometry = new Geometry("Geometry", new Box(1, 1, 1)); |
||||||
|
private final Node root = new Node("Root Node"); |
||||||
|
private final LightList lightList = new LightList(geometry); |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() { |
||||||
|
root.attachChild(geometry); |
||||||
|
} |
||||||
|
|
||||||
|
private final NullRenderer renderer = new NullRenderer() { |
||||||
|
@Override |
||||||
|
public void setShader(Shader shader) { |
||||||
|
MaterialMatParamOverrideTest.this.usedShader = shader; |
||||||
|
evaluated = true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setTexture(int unit, Texture texture) { |
||||||
|
MaterialMatParamOverrideTest.this.usedTextures[unit] = texture; |
||||||
|
} |
||||||
|
}; |
||||||
|
private final RenderManager renderManager = new RenderManager(renderer); |
||||||
|
|
||||||
|
private boolean evaluated = false; |
||||||
|
private Shader usedShader = null; |
||||||
|
private final Texture[] usedTextures = new Texture[32]; |
||||||
|
|
||||||
|
private void inputMp(MatParam... params) { |
||||||
|
if (evaluated) { |
||||||
|
throw new IllegalStateException(); |
||||||
|
} |
||||||
|
Material mat = geometry.getMaterial(); |
||||||
|
for (MatParam param : params) { |
||||||
|
mat.setParam(param.getName(), param.getVarType(), param.getValue()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void inputMpo(MatParamOverride... overrides) { |
||||||
|
if (evaluated) { |
||||||
|
throw new IllegalStateException(); |
||||||
|
} |
||||||
|
for (MatParamOverride override : overrides) { |
||||||
|
geometry.addMatParamOverride(override); |
||||||
|
} |
||||||
|
root.updateGeometricState(); |
||||||
|
} |
||||||
|
|
||||||
|
private void inputParentMpo(MatParamOverride... overrides) { |
||||||
|
if (evaluated) { |
||||||
|
throw new IllegalStateException(); |
||||||
|
} |
||||||
|
for (MatParamOverride override : overrides) { |
||||||
|
root.addMatParamOverride(override); |
||||||
|
} |
||||||
|
root.updateGeometricState(); |
||||||
|
} |
||||||
|
|
||||||
|
private void inputFpo(MatParamOverride... overrides) { |
||||||
|
if (evaluated) { |
||||||
|
throw new IllegalStateException(); |
||||||
|
} |
||||||
|
for (MatParamOverride override : overrides) { |
||||||
|
renderManager.addForcedMatParam(override); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void reset() { |
||||||
|
evaluated = false; |
||||||
|
usedShader = null; |
||||||
|
Arrays.fill(usedTextures, null); |
||||||
|
for (MatParamOverride override : new ArrayList<>(renderManager.getForcedMatParams())) { |
||||||
|
renderManager.removeForcedMatParam(override); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private Define def(String name, VarType type, Object value) { |
||||||
|
Define d = new Define(); |
||||||
|
d.name = name; |
||||||
|
d.type = type; |
||||||
|
d.value = value; |
||||||
|
return d; |
||||||
|
} |
||||||
|
|
||||||
|
private Uniform uniform(String name, VarType type, Object value) { |
||||||
|
Uniform u = new Uniform(); |
||||||
|
u.setName("m_" + name); |
||||||
|
u.setValue(type, value); |
||||||
|
return u; |
||||||
|
} |
||||||
|
|
||||||
|
private void material(String path) { |
||||||
|
AssetManager assetManager = TestUtil.createAssetManager(); |
||||||
|
geometry.setMaterial(new Material(assetManager, path)); |
||||||
|
} |
||||||
|
|
||||||
|
private void evaluateTechniqueDef() { |
||||||
|
Assert.assertFalse(evaluated); |
||||||
|
Material mat = geometry.getMaterial(); |
||||||
|
mat.render(geometry, lightList, renderManager); |
||||||
|
Assert.assertTrue(evaluated); |
||||||
|
} |
||||||
|
|
||||||
|
private void outTextures(Texture... textures) { |
||||||
|
for (int i = 0; i < usedTextures.length; i++) { |
||||||
|
if (i < textures.length) { |
||||||
|
Assert.assertSame(textures[i], usedTextures[i]); |
||||||
|
} else { |
||||||
|
Assert.assertNull(usedTextures[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void outDefines(Define... expectedDefinesArray) { |
||||||
|
Map<String, Define> nameToDefineMap = new HashMap<String, Define>(); |
||||||
|
for (Define define : expectedDefinesArray) { |
||||||
|
nameToDefineMap.put(define.name, define); |
||||||
|
} |
||||||
|
|
||||||
|
if (!evaluated) { |
||||||
|
evaluateTechniqueDef(); |
||||||
|
} |
||||||
|
|
||||||
|
Material mat = geometry.getMaterial(); |
||||||
|
Technique tech = mat.getActiveTechnique(); |
||||||
|
TechniqueDef def = tech.getDef(); |
||||||
|
DefineList actualDefines = tech.getDynamicDefines(); |
||||||
|
|
||||||
|
String[] defineNames = def.getDefineNames(); |
||||||
|
VarType[] defineTypes = def.getDefineTypes(); |
||||||
|
|
||||||
|
Assert.assertEquals(defineNames.length, defineTypes.length); |
||||||
|
|
||||||
|
for (int index = 0; index < defineNames.length; index++) { |
||||||
|
String name = defineNames[index]; |
||||||
|
VarType type = defineTypes[index]; |
||||||
|
Define expectedDefine = nameToDefineMap.remove(name); |
||||||
|
Object expectedValue = null; |
||||||
|
|
||||||
|
if (expectedDefine != null) { |
||||||
|
Assert.assertEquals(expectedDefine.type, type); |
||||||
|
expectedValue = expectedDefine.value; |
||||||
|
} |
||||||
|
|
||||||
|
switch (type) { |
||||||
|
case Boolean: |
||||||
|
if (expectedValue != null) { |
||||||
|
Assert.assertEquals((boolean) (Boolean) expectedValue, actualDefines.getBoolean(index)); |
||||||
|
} else { |
||||||
|
Assert.assertEquals(false, actualDefines.getBoolean(index)); |
||||||
|
} |
||||||
|
break; |
||||||
|
case Int: |
||||||
|
if (expectedValue != null) { |
||||||
|
Assert.assertEquals((int) (Integer) expectedValue, actualDefines.getInt(index)); |
||||||
|
} else { |
||||||
|
Assert.assertEquals(0, actualDefines.getInt(index)); |
||||||
|
} |
||||||
|
break; |
||||||
|
case Float: |
||||||
|
if (expectedValue != null) { |
||||||
|
Assert.assertEquals((float) (Float) expectedValue, actualDefines.getFloat(index), 0f); |
||||||
|
} else { |
||||||
|
Assert.assertEquals(0f, actualDefines.getFloat(index), 0f); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
if (expectedValue != null) { |
||||||
|
Assert.assertEquals(1, actualDefines.getInt(index)); |
||||||
|
} else { |
||||||
|
Assert.assertEquals(0, actualDefines.getInt(index)); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Assert.assertTrue(nameToDefineMap.isEmpty()); |
||||||
|
} |
||||||
|
|
||||||
|
private void outUniforms(Uniform... uniforms) { |
||||||
|
if (!evaluated) { |
||||||
|
evaluateTechniqueDef(); |
||||||
|
} |
||||||
|
|
||||||
|
HashSet<Uniform> actualUniforms = new HashSet<>(); |
||||||
|
|
||||||
|
for (Uniform uniform : usedShader.getUniformMap().values()) { |
||||||
|
if (uniform.getName().startsWith("m_") |
||||||
|
&& !IGNORED_UNIFORMS.contains(uniform.getName())) { |
||||||
|
actualUniforms.add(uniform); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
HashSet<Uniform> expectedUniforms = new HashSet<>(Arrays.asList(uniforms)); |
||||||
|
|
||||||
|
if (!expectedUniforms.equals(actualUniforms)) { |
||||||
|
Assert.fail("Uniform lists must match: " + expectedUniforms + " != " + actualUniforms); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,171 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2016 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.material; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.light.LightList; |
||||||
|
import com.jme3.renderer.Caps; |
||||||
|
import com.jme3.renderer.RenderManager; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.system.NullRenderer; |
||||||
|
import com.jme3.system.TestUtil; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.EnumSet; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.runner.RunWith; |
||||||
|
import org.mockito.runners.MockitoJUnitRunner; |
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class) |
||||||
|
public class MaterialTest { |
||||||
|
|
||||||
|
private Material material; |
||||||
|
private final Geometry geometry = new Geometry("Geometry", new Box(1, 1, 1)); |
||||||
|
private final EnumSet<Caps> myCaps = EnumSet.noneOf(Caps.class); |
||||||
|
private final RenderManager renderManager = new RenderManager(new NullRenderer() { |
||||||
|
@Override |
||||||
|
public EnumSet<Caps> getCaps() { |
||||||
|
return MaterialTest.this.myCaps; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void testSelectNonExistentTechnique() { |
||||||
|
material("Common/MatDefs/Gui/Gui.j3md"); |
||||||
|
material.selectTechnique("Doesn't Exist", renderManager); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = UnsupportedOperationException.class) |
||||||
|
public void testSelectDefaultTechnique_NoCaps() { |
||||||
|
material("Common/MatDefs/Gui/Gui.j3md"); |
||||||
|
material.selectTechnique("Default", renderManager); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSelectDefaultTechnique_GLSL100Cap() { |
||||||
|
supportGlsl(100); |
||||||
|
material("Common/MatDefs/Gui/Gui.j3md"); |
||||||
|
|
||||||
|
material.selectTechnique("Default", renderManager); |
||||||
|
|
||||||
|
checkRequiredCaps(Caps.GLSL100); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSelectDefaultTechnique_GLSL150Cap() { |
||||||
|
supportGlsl(150); |
||||||
|
material("Common/MatDefs/Gui/Gui.j3md"); |
||||||
|
|
||||||
|
material.selectTechnique("Default", renderManager); |
||||||
|
|
||||||
|
checkRequiredCaps(Caps.GLSL150); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSelectDefaultTechnique_GLSL120Cap_MultipleLangs() { |
||||||
|
supportGlsl(120); |
||||||
|
material("Common/MatDefs/Misc/Particle.j3md"); |
||||||
|
|
||||||
|
material.selectTechnique("Default", renderManager); |
||||||
|
|
||||||
|
checkRequiredCaps(Caps.GLSL100, Caps.GLSL120); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSelectDefaultTechnique_GLSL100Cap_MultipleLangs() { |
||||||
|
supportGlsl(100); |
||||||
|
material("Common/MatDefs/Misc/Particle.j3md"); |
||||||
|
|
||||||
|
material.selectTechnique("Default", renderManager); |
||||||
|
|
||||||
|
checkRequiredCaps(Caps.GLSL100); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSelectNamedTechnique_GLSL150Cap() { |
||||||
|
supportGlsl(150); |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
|
||||||
|
material.selectTechnique("PostShadow", renderManager); |
||||||
|
|
||||||
|
checkRequiredCaps(Caps.GLSL150); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSelectNamedTechnique_GLSL100Cap() { |
||||||
|
supportGlsl(100); |
||||||
|
material("Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
|
||||||
|
material.selectTechnique("PostShadow", renderManager); |
||||||
|
|
||||||
|
checkRequiredCaps(Caps.GLSL100); |
||||||
|
} |
||||||
|
|
||||||
|
private void checkRequiredCaps(Caps... caps) { |
||||||
|
EnumSet<Caps> expected = EnumSet.noneOf(Caps.class); |
||||||
|
expected.addAll(Arrays.asList(caps)); |
||||||
|
|
||||||
|
Technique tech = material.getActiveTechnique(); |
||||||
|
|
||||||
|
assertEquals(expected, tech.getDef().getRequiredCaps()); |
||||||
|
} |
||||||
|
|
||||||
|
private void supportGlsl(int version) { |
||||||
|
switch (version) { |
||||||
|
case 150: |
||||||
|
myCaps.add(Caps.GLSL150); |
||||||
|
case 140: |
||||||
|
myCaps.add(Caps.GLSL140); |
||||||
|
case 130: |
||||||
|
myCaps.add(Caps.GLSL130); |
||||||
|
case 120: |
||||||
|
myCaps.add(Caps.GLSL120); |
||||||
|
case 110: |
||||||
|
myCaps.add(Caps.GLSL110); |
||||||
|
case 100: |
||||||
|
myCaps.add(Caps.GLSL100); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void caps(Caps... caps) { |
||||||
|
myCaps.addAll(Arrays.asList(caps)); |
||||||
|
} |
||||||
|
|
||||||
|
private void material(String path) { |
||||||
|
AssetManager assetManager = TestUtil.createAssetManager(); |
||||||
|
material = new Material(assetManager, path); |
||||||
|
geometry.setMaterial(material); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,343 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.renderer; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.material.TechniqueDef; |
||||||
|
import com.jme3.math.ColorRGBA; |
||||||
|
import com.jme3.renderer.queue.GeometryList; |
||||||
|
import com.jme3.renderer.queue.OpaqueComparator; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Mesh; |
||||||
|
import com.jme3.scene.shape.Box; |
||||||
|
import com.jme3.system.TestUtil; |
||||||
|
import com.jme3.texture.Image; |
||||||
|
import com.jme3.texture.Image.Format; |
||||||
|
import com.jme3.texture.Texture; |
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import com.jme3.texture.image.ColorSpace; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
import java.nio.ByteBuffer; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Ignore; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
public class OpaqueComparatorTest { |
||||||
|
|
||||||
|
private final Mesh mesh = new Box(1,1,1); |
||||||
|
private Camera cam = new Camera(1, 1); |
||||||
|
private RenderManager renderManager; |
||||||
|
private AssetManager assetManager; |
||||||
|
private OpaqueComparator comparator = new OpaqueComparator(); |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() { |
||||||
|
assetManager = TestUtil.createAssetManager(); |
||||||
|
renderManager = TestUtil.createRenderManager(); |
||||||
|
comparator.setCamera(cam); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Given a correctly sorted list of materials, check if the |
||||||
|
* opaque comparator can sort a reversed list of them. |
||||||
|
* |
||||||
|
* Each material will be cloned so that none of them will be equal to each other |
||||||
|
* in reference, forcing the comparator to compare the material sort ID. |
||||||
|
* |
||||||
|
* E.g. for a list of materials A, B, C, the following list will be generated: |
||||||
|
* <pre>C, B, A, C, B, A, C, B, A</pre>, it should result in |
||||||
|
* <pre>A, A, A, B, B, B, C, C, C</pre>. |
||||||
|
* |
||||||
|
* @param materials The pre-sorted list of materials to check sorting for. |
||||||
|
*/ |
||||||
|
private void testSort(Material ... materials) { |
||||||
|
GeometryList gl = new GeometryList(comparator); |
||||||
|
for (int g = 0; g < 5; g++) { |
||||||
|
for (int i = materials.length - 1; i >= 0; i--) { |
||||||
|
Geometry geom = new Geometry("geom", mesh); |
||||||
|
Material clonedMaterial = materials[i].clone(); |
||||||
|
|
||||||
|
if (materials[i].getActiveTechnique() != null) { |
||||||
|
String techniqueName = materials[i].getActiveTechnique().getDef().getName(); |
||||||
|
clonedMaterial.selectTechnique(techniqueName, renderManager); |
||||||
|
} else { |
||||||
|
clonedMaterial.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); |
||||||
|
} |
||||||
|
|
||||||
|
geom.setMaterial(clonedMaterial); |
||||||
|
gl.add(geom); |
||||||
|
} |
||||||
|
} |
||||||
|
gl.sort(); |
||||||
|
|
||||||
|
for (int i = 0; i < gl.size(); i++) { |
||||||
|
Material mat = gl.get(i).getMaterial(); |
||||||
|
String sortId = Integer.toHexString(mat.getSortId()).toUpperCase(); |
||||||
|
System.out.print(sortId + "\t"); |
||||||
|
System.out.println(mat); |
||||||
|
} |
||||||
|
|
||||||
|
Set<String> alreadySeen = new HashSet<String>(); |
||||||
|
Material current = null; |
||||||
|
for (int i = 0; i < gl.size(); i++) { |
||||||
|
Material mat = gl.get(i).getMaterial(); |
||||||
|
if (current == null) { |
||||||
|
current = mat; |
||||||
|
} else if (!current.getName().equals(mat.getName())) { |
||||||
|
assert !alreadySeen.contains(mat.getName()); |
||||||
|
alreadySeen.add(current.getName()); |
||||||
|
current = mat; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < materials.length; i++) { |
||||||
|
for (int g = 0; g < 5; g++) { |
||||||
|
int index = i * 5 + g; |
||||||
|
Material mat1 = gl.get(index).getMaterial(); |
||||||
|
Material mat2 = materials[i]; |
||||||
|
assert mat1.getName().equals(mat2.getName()) : |
||||||
|
mat1.getName() + " != " + mat2.getName() + " for index " + index; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSortByMaterialDef() { |
||||||
|
Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material particleMat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md"); |
||||||
|
Material unshadedMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md"); |
||||||
|
|
||||||
|
lightingMat.setName("MatLight"); |
||||||
|
particleMat.setName("MatParticle"); |
||||||
|
unshadedMat.setName("MatUnshaded"); |
||||||
|
skyMat.setName("MatSky"); |
||||||
|
testSort(skyMat, lightingMat, particleMat, unshadedMat); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSortByTechnique() { |
||||||
|
Material lightingMatDefault = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingPreShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingPostShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingMatPreNormalPass = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingMatGBuf = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingMatGlow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
|
||||||
|
lightingMatDefault.setName("TechDefault"); |
||||||
|
lightingMatDefault.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); |
||||||
|
|
||||||
|
lightingPostShadow.setName("TechPostShad"); |
||||||
|
lightingPostShadow.selectTechnique("PostShadow", renderManager); |
||||||
|
|
||||||
|
lightingPreShadow.setName("TechPreShad"); |
||||||
|
lightingPreShadow.selectTechnique("PreShadow", renderManager); |
||||||
|
|
||||||
|
lightingMatPreNormalPass.setName("TechNorm"); |
||||||
|
lightingMatPreNormalPass.selectTechnique("PreNormalPass", renderManager); |
||||||
|
|
||||||
|
lightingMatGBuf.setName("TechGBuf"); |
||||||
|
lightingMatGBuf.selectTechnique("GBuf", renderManager); |
||||||
|
|
||||||
|
lightingMatGlow.setName("TechGlow"); |
||||||
|
lightingMatGlow.selectTechnique("Glow", renderManager); |
||||||
|
|
||||||
|
testSort(lightingMatGlow, lightingPreShadow, lightingMatPreNormalPass, |
||||||
|
lightingMatDefault, lightingPostShadow, lightingMatGBuf); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = AssertionError.class) |
||||||
|
public void testNoSortByParam() { |
||||||
|
Material sameMat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
Material sameMat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
|
||||||
|
sameMat1.setName("MatRed"); |
||||||
|
sameMat1.setColor("Color", ColorRGBA.Red); |
||||||
|
|
||||||
|
sameMat2.setName("MatBlue"); |
||||||
|
sameMat2.setColor("Color", ColorRGBA.Blue); |
||||||
|
|
||||||
|
testSort(sameMat1, sameMat2); |
||||||
|
} |
||||||
|
|
||||||
|
private Texture createTexture(String name) { |
||||||
|
ByteBuffer bb = BufferUtils.createByteBuffer(3); |
||||||
|
Image image = new Image(Format.RGB8, 1, 1, bb, ColorSpace.sRGB); |
||||||
|
Texture2D texture = new Texture2D(image); |
||||||
|
texture.setName(name); |
||||||
|
return texture; |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSortByTexture() { |
||||||
|
Material texture1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
Material texture2Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
Material texture3Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
|
||||||
|
Texture tex1 = createTexture("A"); |
||||||
|
tex1.getImage().setId(1); |
||||||
|
|
||||||
|
Texture tex2 = createTexture("B"); |
||||||
|
tex2.getImage().setId(2); |
||||||
|
|
||||||
|
Texture tex3 = createTexture("C"); |
||||||
|
tex3.getImage().setId(3); |
||||||
|
|
||||||
|
texture1Mat.setName("TexA"); |
||||||
|
texture1Mat.setTexture("ColorMap", tex1); |
||||||
|
|
||||||
|
texture2Mat.setName("TexB"); |
||||||
|
texture2Mat.setTexture("ColorMap", tex2); |
||||||
|
|
||||||
|
texture3Mat.setName("TexC"); |
||||||
|
texture3Mat.setTexture("ColorMap", tex3); |
||||||
|
|
||||||
|
testSort(texture1Mat, texture2Mat, texture3Mat); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSortByShaderDefines() { |
||||||
|
Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingMatVColor = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingMatVLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingMatTC = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingMatVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material lightingMatTCVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
|
||||||
|
lightingMat.setName("DefNone"); |
||||||
|
|
||||||
|
lightingMatVColor.setName("DefVC"); |
||||||
|
lightingMatVColor.setBoolean("UseVertexColor", true); |
||||||
|
|
||||||
|
lightingMatVLight.setName("DefVL"); |
||||||
|
lightingMatVLight.setBoolean("VertexLighting", true); |
||||||
|
|
||||||
|
lightingMatTC.setName("DefTC"); |
||||||
|
lightingMatTC.setBoolean("SeparateTexCoord", true); |
||||||
|
|
||||||
|
lightingMatVColorLight.setName("DefVCVL"); |
||||||
|
lightingMatVColorLight.setBoolean("UseVertexColor", true); |
||||||
|
lightingMatVColorLight.setBoolean("VertexLighting", true); |
||||||
|
|
||||||
|
lightingMatTCVColorLight.setName("DefVCVLTC"); |
||||||
|
lightingMatTCVColorLight.setBoolean("UseVertexColor", true); |
||||||
|
lightingMatTCVColorLight.setBoolean("VertexLighting", true); |
||||||
|
lightingMatTCVColorLight.setBoolean("SeparateTexCoord", true); |
||||||
|
|
||||||
|
testSort(lightingMat, lightingMatVColor, lightingMatVLight, |
||||||
|
lightingMatVColorLight, lightingMatTC, lightingMatTCVColorLight); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSortByAll() { |
||||||
|
Material matBase1 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); |
||||||
|
Material matBase2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||||
|
|
||||||
|
Texture texBase = createTexture("BASE"); |
||||||
|
texBase.getImage().setId(1); |
||||||
|
Texture tex1 = createTexture("1"); |
||||||
|
tex1.getImage().setId(2); |
||||||
|
Texture tex2 = createTexture("2"); |
||||||
|
tex2.getImage().setId(3); |
||||||
|
|
||||||
|
matBase1.setName("BASE"); |
||||||
|
matBase1.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); |
||||||
|
matBase1.setBoolean("UseVertexColor", true); |
||||||
|
matBase1.setTexture("DiffuseMap", texBase); |
||||||
|
|
||||||
|
Material mat1100 = matBase1.clone(); |
||||||
|
mat1100.setName("1100"); |
||||||
|
mat1100.selectTechnique("PreShadow", renderManager); |
||||||
|
|
||||||
|
Material mat1101 = matBase1.clone(); |
||||||
|
mat1101.setName("1101"); |
||||||
|
mat1101.selectTechnique("PreShadow", renderManager); |
||||||
|
mat1101.setTexture("DiffuseMap", tex1); |
||||||
|
|
||||||
|
Material mat1102 = matBase1.clone(); |
||||||
|
mat1102.setName("1102"); |
||||||
|
mat1102.selectTechnique("PreShadow", renderManager); |
||||||
|
mat1102.setTexture("DiffuseMap", tex2); |
||||||
|
|
||||||
|
Material mat1110 = matBase1.clone(); |
||||||
|
mat1110.setName("1110"); |
||||||
|
mat1110.selectTechnique("PreShadow", renderManager); |
||||||
|
mat1110.setFloat("AlphaDiscardThreshold", 2f); |
||||||
|
|
||||||
|
Material mat1120 = matBase1.clone(); |
||||||
|
mat1120.setName("1120"); |
||||||
|
mat1120.selectTechnique("PreShadow", renderManager); |
||||||
|
mat1120.setBoolean("UseInstancing", true); |
||||||
|
|
||||||
|
Material mat1121 = matBase1.clone(); |
||||||
|
mat1121.setName("1121"); |
||||||
|
mat1121.selectTechnique("PreShadow", renderManager); |
||||||
|
mat1121.setBoolean("UseInstancing", true); |
||||||
|
mat1121.setTexture("DiffuseMap", tex1); |
||||||
|
|
||||||
|
Material mat1122 = matBase1.clone(); |
||||||
|
mat1122.setName("1122"); |
||||||
|
mat1122.selectTechnique("PreShadow", renderManager); |
||||||
|
mat1122.setBoolean("UseInstancing", true); |
||||||
|
mat1122.setTexture("DiffuseMap", tex2); |
||||||
|
|
||||||
|
Material mat1140 = matBase1.clone(); |
||||||
|
mat1140.setName("1140"); |
||||||
|
mat1140.selectTechnique("PreShadow", renderManager); |
||||||
|
mat1140.setFloat("AlphaDiscardThreshold", 2f); |
||||||
|
mat1140.setBoolean("UseInstancing", true); |
||||||
|
|
||||||
|
Material mat1200 = matBase1.clone(); |
||||||
|
mat1200.setName("1200"); |
||||||
|
mat1200.selectTechnique("PostShadow", renderManager); |
||||||
|
|
||||||
|
Material mat1210 = matBase1.clone(); |
||||||
|
mat1210.setName("1210"); |
||||||
|
mat1210.selectTechnique("PostShadow", renderManager); |
||||||
|
mat1210.setFloat("AlphaDiscardThreshold", 2f); |
||||||
|
|
||||||
|
Material mat1220 = matBase1.clone(); |
||||||
|
mat1220.setName("1220"); |
||||||
|
mat1220.selectTechnique("PostShadow", renderManager); |
||||||
|
mat1220.setBoolean("UseInstancing", true); |
||||||
|
|
||||||
|
Material mat2000 = matBase2.clone(); |
||||||
|
mat2000.setName("2000"); |
||||||
|
|
||||||
|
testSort(mat1100, mat1101, mat1102, mat1110, |
||||||
|
mat1120, mat1121, mat1122, mat1140, |
||||||
|
mat1200, mat1210, mat1220, mat2000); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,173 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2016 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.scene; |
||||||
|
|
||||||
|
import com.jme3.material.MatParamOverride; |
||||||
|
import com.jme3.math.Matrix4f; |
||||||
|
import com.jme3.renderer.Camera; |
||||||
|
import com.jme3.shader.VarType; |
||||||
|
import com.jme3.texture.Texture2D; |
||||||
|
import java.lang.reflect.Field; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
import static org.junit.Assert.assertEquals; |
||||||
|
|
||||||
|
public class MPOTestUtils { |
||||||
|
|
||||||
|
private static final Camera DUMMY_CAM = new Camera(640, 480); |
||||||
|
|
||||||
|
private static final SceneGraphVisitor VISITOR = new SceneGraphVisitor() { |
||||||
|
@Override |
||||||
|
public void visit(Spatial spatial) { |
||||||
|
validateSubScene(spatial); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
private static void validateSubScene(Spatial scene) { |
||||||
|
scene.checkCulling(DUMMY_CAM); |
||||||
|
|
||||||
|
Set<MatParamOverride> actualOverrides = new HashSet<MatParamOverride>(); |
||||||
|
for (MatParamOverride override : scene.getWorldMatParamOverrides()) { |
||||||
|
actualOverrides.add(override); |
||||||
|
} |
||||||
|
|
||||||
|
Set<MatParamOverride> expectedOverrides = new HashSet<MatParamOverride>(); |
||||||
|
Spatial current = scene; |
||||||
|
while (current != null) { |
||||||
|
for (MatParamOverride override : current.getLocalMatParamOverrides()) { |
||||||
|
expectedOverrides.add(override); |
||||||
|
} |
||||||
|
current = current.getParent(); |
||||||
|
} |
||||||
|
|
||||||
|
assertEquals("For " + scene, expectedOverrides, actualOverrides); |
||||||
|
} |
||||||
|
|
||||||
|
public static void validateScene(Spatial scene) { |
||||||
|
scene.updateGeometricState(); |
||||||
|
scene.depthFirstTraversal(VISITOR); |
||||||
|
} |
||||||
|
|
||||||
|
public static MatParamOverride mpoInt(String name, int value) { |
||||||
|
return new MatParamOverride(VarType.Int, name, value); |
||||||
|
} |
||||||
|
|
||||||
|
public static MatParamOverride mpoBool(String name, boolean value) { |
||||||
|
return new MatParamOverride(VarType.Boolean, name, value); |
||||||
|
} |
||||||
|
|
||||||
|
public static MatParamOverride mpoFloat(String name, float value) { |
||||||
|
return new MatParamOverride(VarType.Float, name, value); |
||||||
|
} |
||||||
|
|
||||||
|
public static MatParamOverride mpoMatrix4Array(String name, Matrix4f[] value) { |
||||||
|
return new MatParamOverride(VarType.Matrix4Array, name, value); |
||||||
|
} |
||||||
|
|
||||||
|
public static MatParamOverride mpoTexture2D(String name, Texture2D texture) { |
||||||
|
return new MatParamOverride(VarType.Texture2D, name, texture); |
||||||
|
} |
||||||
|
|
||||||
|
private static int getRefreshFlags(Spatial scene) { |
||||||
|
try { |
||||||
|
Field refreshFlagsField = Spatial.class.getDeclaredField("refreshFlags"); |
||||||
|
refreshFlagsField.setAccessible(true); |
||||||
|
return (Integer) refreshFlagsField.get(scene); |
||||||
|
} catch (NoSuchFieldException ex) { |
||||||
|
throw new AssertionError(ex); |
||||||
|
} catch (SecurityException ex) { |
||||||
|
throw new AssertionError(ex); |
||||||
|
} catch (IllegalArgumentException ex) { |
||||||
|
throw new AssertionError(ex); |
||||||
|
} catch (IllegalAccessException ex) { |
||||||
|
throw new AssertionError(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void dumpSceneRF(Spatial scene, String indent, boolean last, int refreshFlagsMask) { |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
|
||||||
|
sb.append(indent); |
||||||
|
if (last) { |
||||||
|
if (!indent.isEmpty()) { |
||||||
|
sb.append("└─"); |
||||||
|
} else { |
||||||
|
sb.append(" "); |
||||||
|
} |
||||||
|
indent += " "; |
||||||
|
} else { |
||||||
|
sb.append("├─"); |
||||||
|
indent += "│ "; |
||||||
|
} |
||||||
|
sb.append(scene.getName()); |
||||||
|
int rf = getRefreshFlags(scene) & refreshFlagsMask; |
||||||
|
if (rf != 0) { |
||||||
|
sb.append("("); |
||||||
|
if ((rf & 0x1) != 0) { |
||||||
|
sb.append("T"); |
||||||
|
} |
||||||
|
if ((rf & 0x2) != 0) { |
||||||
|
sb.append("B"); |
||||||
|
} |
||||||
|
if ((rf & 0x4) != 0) { |
||||||
|
sb.append("L"); |
||||||
|
} |
||||||
|
if ((rf & 0x8) != 0) { |
||||||
|
sb.append("l"); |
||||||
|
} |
||||||
|
if ((rf & 0x10) != 0) { |
||||||
|
sb.append("O"); |
||||||
|
} |
||||||
|
sb.append(")"); |
||||||
|
} |
||||||
|
|
||||||
|
if (!scene.getLocalMatParamOverrides().isEmpty()) { |
||||||
|
sb.append(" [MPO]"); |
||||||
|
} |
||||||
|
|
||||||
|
System.out.println(sb); |
||||||
|
|
||||||
|
if (scene instanceof Node) { |
||||||
|
Node node = (Node) scene; |
||||||
|
int childIndex = 0; |
||||||
|
for (Spatial child : node.getChildren()) { |
||||||
|
boolean childLast = childIndex == node.getQuantity() - 1; |
||||||
|
dumpSceneRF(child, indent, childLast, refreshFlagsMask); |
||||||
|
childIndex++; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void dumpSceneRF(Spatial scene, int refreshFlagsMask) { |
||||||
|
dumpSceneRF(scene, "", true, refreshFlagsMask); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,278 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2016 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.scene; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.export.binary.BinaryExporter; |
||||||
|
import com.jme3.material.MatParamOverride; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import static com.jme3.scene.MPOTestUtils.*; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
import com.jme3.system.TestUtil; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Validates how {@link MatParamOverride MPOs} work on the scene level. |
||||||
|
* |
||||||
|
* @author Kirill Vainer |
||||||
|
*/ |
||||||
|
public class SceneMatParamOverrideTest { |
||||||
|
|
||||||
|
private static Node createDummyScene() { |
||||||
|
Node scene = new Node("Scene Node"); |
||||||
|
|
||||||
|
Node a = new Node("A"); |
||||||
|
Node b = new Node("B"); |
||||||
|
|
||||||
|
Node c = new Node("C"); |
||||||
|
Node d = new Node("D"); |
||||||
|
|
||||||
|
Node e = new Node("E"); |
||||||
|
Node f = new Node("F"); |
||||||
|
|
||||||
|
Node g = new Node("G"); |
||||||
|
Node h = new Node("H"); |
||||||
|
Node j = new Node("J"); |
||||||
|
|
||||||
|
scene.attachChild(a); |
||||||
|
scene.attachChild(b); |
||||||
|
|
||||||
|
a.attachChild(c); |
||||||
|
a.attachChild(d); |
||||||
|
|
||||||
|
b.attachChild(e); |
||||||
|
b.attachChild(f); |
||||||
|
|
||||||
|
c.attachChild(g); |
||||||
|
c.attachChild(h); |
||||||
|
c.attachChild(j); |
||||||
|
|
||||||
|
return scene; |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_Empty() { |
||||||
|
Node n = new Node("Node"); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertTrue(n.getWorldMatParamOverrides().isEmpty()); |
||||||
|
|
||||||
|
n.updateGeometricState(); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertTrue(n.getWorldMatParamOverrides().isEmpty()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_AddRemove() { |
||||||
|
MatParamOverride override = mpoBool("Test", true); |
||||||
|
Node n = new Node("Node"); |
||||||
|
|
||||||
|
n.removeMatParamOverride(override); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertTrue(n.getWorldMatParamOverrides().isEmpty()); |
||||||
|
|
||||||
|
n.addMatParamOverride(override); |
||||||
|
|
||||||
|
assertSame(n.getLocalMatParamOverrides().get(0), override); |
||||||
|
assertTrue(n.getWorldMatParamOverrides().isEmpty()); |
||||||
|
n.updateGeometricState(); |
||||||
|
|
||||||
|
assertSame(n.getLocalMatParamOverrides().get(0), override); |
||||||
|
assertSame(n.getWorldMatParamOverrides().get(0), override); |
||||||
|
|
||||||
|
n.removeMatParamOverride(override); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertSame(n.getWorldMatParamOverrides().get(0), override); |
||||||
|
|
||||||
|
n.updateGeometricState(); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertTrue(n.getWorldMatParamOverrides().isEmpty()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_Clear() { |
||||||
|
MatParamOverride override = mpoBool("Test", true); |
||||||
|
Node n = new Node("Node"); |
||||||
|
|
||||||
|
n.clearMatParamOverrides(); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertTrue(n.getWorldMatParamOverrides().isEmpty()); |
||||||
|
|
||||||
|
n.addMatParamOverride(override); |
||||||
|
n.clearMatParamOverrides(); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertTrue(n.getWorldMatParamOverrides().isEmpty()); |
||||||
|
|
||||||
|
n.addMatParamOverride(override); |
||||||
|
n.updateGeometricState(); |
||||||
|
n.clearMatParamOverrides(); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertSame(n.getWorldMatParamOverrides().get(0), override); |
||||||
|
|
||||||
|
n.updateGeometricState(); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertTrue(n.getWorldMatParamOverrides().isEmpty()); |
||||||
|
|
||||||
|
n.addMatParamOverride(override); |
||||||
|
n.clearMatParamOverrides(); |
||||||
|
n.updateGeometricState(); |
||||||
|
assertTrue(n.getLocalMatParamOverrides().isEmpty()); |
||||||
|
assertTrue(n.getWorldMatParamOverrides().isEmpty()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_AddAfterAttach() { |
||||||
|
Node scene = createDummyScene(); |
||||||
|
scene.updateGeometricState(); |
||||||
|
|
||||||
|
Node root = new Node("Root Node"); |
||||||
|
root.updateGeometricState(); |
||||||
|
|
||||||
|
root.attachChild(scene); |
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5)); |
||||||
|
|
||||||
|
validateScene(root); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_AddBeforeAttach() { |
||||||
|
Node scene = createDummyScene(); |
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5)); |
||||||
|
scene.updateGeometricState(); |
||||||
|
|
||||||
|
Node root = new Node("Root Node"); |
||||||
|
root.updateGeometricState(); |
||||||
|
|
||||||
|
root.attachChild(scene); |
||||||
|
|
||||||
|
validateScene(root); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_RemoveBeforeAttach() { |
||||||
|
Node scene = createDummyScene(); |
||||||
|
scene.updateGeometricState(); |
||||||
|
|
||||||
|
Node root = new Node("Root Node"); |
||||||
|
root.updateGeometricState(); |
||||||
|
|
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5)); |
||||||
|
validateScene(scene); |
||||||
|
|
||||||
|
scene.getChild("A").clearMatParamOverrides(); |
||||||
|
validateScene(scene); |
||||||
|
|
||||||
|
root.attachChild(scene); |
||||||
|
validateScene(root); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_RemoveAfterAttach() { |
||||||
|
Node scene = createDummyScene(); |
||||||
|
scene.updateGeometricState(); |
||||||
|
|
||||||
|
Node root = new Node("Root Node"); |
||||||
|
root.updateGeometricState(); |
||||||
|
|
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5)); |
||||||
|
|
||||||
|
root.attachChild(scene); |
||||||
|
validateScene(root); |
||||||
|
|
||||||
|
scene.getChild("A").clearMatParamOverrides(); |
||||||
|
validateScene(root); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_IdenticalNames() { |
||||||
|
Node scene = createDummyScene(); |
||||||
|
|
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5)); |
||||||
|
scene.getChild("C").addMatParamOverride(mpoInt("val", 7)); |
||||||
|
|
||||||
|
validateScene(scene); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_CloningScene_DoesntCloneMPO() { |
||||||
|
Node originalScene = createDummyScene(); |
||||||
|
|
||||||
|
originalScene.getChild("A").addMatParamOverride(mpoInt("int", 5)); |
||||||
|
originalScene.getChild("A").addMatParamOverride(mpoBool("bool", true)); |
||||||
|
originalScene.getChild("A").addMatParamOverride(mpoFloat("float", 3.12f)); |
||||||
|
|
||||||
|
Node clonedScene = originalScene.clone(false); |
||||||
|
|
||||||
|
validateScene(clonedScene); |
||||||
|
validateScene(originalScene); |
||||||
|
|
||||||
|
List<MatParamOverride> clonedOverrides = clonedScene.getChild("A").getLocalMatParamOverrides(); |
||||||
|
List<MatParamOverride> originalOverrides = originalScene.getChild("A").getLocalMatParamOverrides(); |
||||||
|
|
||||||
|
assertNotSame(clonedOverrides, originalOverrides); |
||||||
|
assertEquals(clonedOverrides, originalOverrides); |
||||||
|
|
||||||
|
for (int i = 0; i < clonedOverrides.size(); i++) { |
||||||
|
assertNotSame(clonedOverrides.get(i), originalOverrides.get(i)); |
||||||
|
assertEquals(clonedOverrides.get(i), originalOverrides.get(i)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testOverrides_SaveAndLoad_KeepsMPOs() { |
||||||
|
MatParamOverride override = mpoInt("val", 5); |
||||||
|
Node scene = createDummyScene(); |
||||||
|
scene.getChild("A").addMatParamOverride(override); |
||||||
|
|
||||||
|
AssetManager assetManager = TestUtil.createAssetManager(); |
||||||
|
Node loadedScene = BinaryExporter.saveAndLoad(assetManager, scene); |
||||||
|
|
||||||
|
Node root = new Node("Root Node"); |
||||||
|
root.attachChild(loadedScene); |
||||||
|
validateScene(root); |
||||||
|
validateScene(scene); |
||||||
|
|
||||||
|
assertNotSame(override, loadedScene.getChild("A").getLocalMatParamOverrides().get(0)); |
||||||
|
assertEquals(override, loadedScene.getChild("A").getLocalMatParamOverrides().get(0)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testEquals() { |
||||||
|
assertEquals(mpoInt("val", 5), mpoInt("val", 5)); |
||||||
|
assertEquals(mpoBool("val", true), mpoBool("val", true)); |
||||||
|
assertNotEquals(mpoInt("val", 5), mpoInt("val", 6)); |
||||||
|
assertNotEquals(mpoInt("val1", 5), mpoInt("val2", 5)); |
||||||
|
assertNotEquals(mpoBool("val", true), mpoInt("val", 1)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,300 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2015 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package com.jme3.shader; |
||||||
|
|
||||||
|
import com.jme3.math.FastMath; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
public class DefineListTest { |
||||||
|
|
||||||
|
private static final List<String> DEFINE_NAMES = Arrays.asList("BOOL_VAR", "INT_VAR", "FLOAT_VAR"); |
||||||
|
private static final List<VarType> DEFINE_TYPES = Arrays.asList(VarType.Boolean, VarType.Int, VarType.Float); |
||||||
|
private static final int NUM_DEFINES = DEFINE_NAMES.size(); |
||||||
|
private static final int BOOL_VAR = 0; |
||||||
|
private static final int INT_VAR = 1; |
||||||
|
private static final int FLOAT_VAR = 2; |
||||||
|
private static final DefineList EMPTY = new DefineList(NUM_DEFINES); |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testHashCollision() { |
||||||
|
DefineList dl1 = new DefineList(64); |
||||||
|
DefineList dl2 = new DefineList(64); |
||||||
|
|
||||||
|
// Try to cause a hash collision
|
||||||
|
// (since bit #32 is aliased to bit #1 in 32-bit ints)
|
||||||
|
dl1.set(0, 123); |
||||||
|
dl1.set(32, 0); |
||||||
|
|
||||||
|
dl2.set(32, 0); |
||||||
|
dl2.set(0, 123); |
||||||
|
|
||||||
|
assert dl1.hashCode() == dl2.hashCode(); |
||||||
|
assert dl1.equals(dl2); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testGetSet() { |
||||||
|
DefineList dl = new DefineList(NUM_DEFINES); |
||||||
|
|
||||||
|
assertFalse(dl.getBoolean(BOOL_VAR)); |
||||||
|
assertEquals(dl.getInt(INT_VAR), 0); |
||||||
|
assertEquals(dl.getFloat(FLOAT_VAR), 0f, 0f); |
||||||
|
|
||||||
|
dl.set(BOOL_VAR, true); |
||||||
|
dl.set(INT_VAR, -1); |
||||||
|
dl.set(FLOAT_VAR, Float.NaN); |
||||||
|
|
||||||
|
assertTrue(dl.getBoolean(BOOL_VAR)); |
||||||
|
assertEquals(dl.getInt(INT_VAR), -1); |
||||||
|
assertTrue(Float.isNaN(dl.getFloat(FLOAT_VAR))); |
||||||
|
} |
||||||
|
|
||||||
|
private String generateSource(DefineList dl) { |
||||||
|
StringBuilder sb = new StringBuilder(); |
||||||
|
dl.generateSource(sb, DEFINE_NAMES, DEFINE_TYPES); |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSourceInitial() { |
||||||
|
DefineList dl = new DefineList(NUM_DEFINES); |
||||||
|
assert dl.hashCode() == 0; |
||||||
|
assert generateSource(dl).equals(""); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSourceBooleanDefine() { |
||||||
|
DefineList dl = new DefineList(NUM_DEFINES); |
||||||
|
|
||||||
|
dl.set(BOOL_VAR, true); |
||||||
|
assert dl.hashCode() == 1; |
||||||
|
assert generateSource(dl).equals("#define BOOL_VAR 1\n"); |
||||||
|
|
||||||
|
dl.set(BOOL_VAR, false); |
||||||
|
assert dl.hashCode() == 0; |
||||||
|
assert generateSource(dl).equals(""); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSourceIntDefine() { |
||||||
|
DefineList dl = new DefineList(NUM_DEFINES); |
||||||
|
|
||||||
|
int hashCodeWithInt = 1 << INT_VAR; |
||||||
|
|
||||||
|
dl.set(INT_VAR, 123); |
||||||
|
assert dl.hashCode() == hashCodeWithInt; |
||||||
|
assert generateSource(dl).equals("#define INT_VAR 123\n"); |
||||||
|
|
||||||
|
dl.set(INT_VAR, 0); |
||||||
|
assert dl.hashCode() == 0; |
||||||
|
assert generateSource(dl).equals(""); |
||||||
|
|
||||||
|
dl.set(INT_VAR, -99); |
||||||
|
assert dl.hashCode() == hashCodeWithInt; |
||||||
|
assert generateSource(dl).equals("#define INT_VAR -99\n"); |
||||||
|
|
||||||
|
dl.set(INT_VAR, Integer.MAX_VALUE); |
||||||
|
assert dl.hashCode() == hashCodeWithInt; |
||||||
|
assert generateSource(dl).equals("#define INT_VAR 2147483647\n"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testSourceFloatDefine() { |
||||||
|
DefineList dl = new DefineList(NUM_DEFINES); |
||||||
|
|
||||||
|
dl.set(FLOAT_VAR, 1f); |
||||||
|
assert dl.hashCode() == (1 << FLOAT_VAR); |
||||||
|
assert generateSource(dl).equals("#define FLOAT_VAR 1.0\n"); |
||||||
|
|
||||||
|
dl.set(FLOAT_VAR, 0f); |
||||||
|
assert dl.hashCode() == 0; |
||||||
|
assert generateSource(dl).equals(""); |
||||||
|
|
||||||
|
dl.set(FLOAT_VAR, -1f); |
||||||
|
assert generateSource(dl).equals("#define FLOAT_VAR -1.0\n"); |
||||||
|
|
||||||
|
dl.set(FLOAT_VAR, FastMath.FLT_EPSILON); |
||||||
|
assert generateSource(dl).equals("#define FLOAT_VAR 1.1920929E-7\n"); |
||||||
|
|
||||||
|
dl.set(FLOAT_VAR, FastMath.PI); |
||||||
|
assert generateSource(dl).equals("#define FLOAT_VAR 3.1415927\n"); |
||||||
|
|
||||||
|
try { |
||||||
|
dl.set(FLOAT_VAR, Float.NaN); |
||||||
|
generateSource(dl); |
||||||
|
assert false; |
||||||
|
} catch (IllegalArgumentException ex) { } |
||||||
|
|
||||||
|
try { |
||||||
|
dl.set(FLOAT_VAR, Float.POSITIVE_INFINITY); |
||||||
|
generateSource(dl); |
||||||
|
assert false; |
||||||
|
} catch (IllegalArgumentException ex) { } |
||||||
|
|
||||||
|
try { |
||||||
|
dl.set(FLOAT_VAR, Float.NEGATIVE_INFINITY); |
||||||
|
generateSource(dl); |
||||||
|
assert false; |
||||||
|
} catch (IllegalArgumentException ex) { } |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testEqualsAndHashCode() { |
||||||
|
DefineList dl1 = new DefineList(NUM_DEFINES); |
||||||
|
DefineList dl2 = new DefineList(NUM_DEFINES); |
||||||
|
|
||||||
|
assertTrue(dl1.hashCode() == 0); |
||||||
|
assertEquals(dl1, dl2); |
||||||
|
|
||||||
|
dl1.set(BOOL_VAR, true); |
||||||
|
|
||||||
|
assertTrue(dl1.hashCode() == 1); |
||||||
|
assertNotSame(dl1, dl2); |
||||||
|
|
||||||
|
dl2.set(BOOL_VAR, true); |
||||||
|
|
||||||
|
assertEquals(dl1, dl2); |
||||||
|
|
||||||
|
dl1.set(INT_VAR, 2); |
||||||
|
|
||||||
|
assertTrue(dl1.hashCode() == (1|2)); |
||||||
|
assertNotSame(dl1, dl2); |
||||||
|
|
||||||
|
dl2.set(INT_VAR, 2); |
||||||
|
|
||||||
|
assertEquals(dl1, dl2); |
||||||
|
|
||||||
|
dl1.set(BOOL_VAR, false); |
||||||
|
|
||||||
|
assertTrue(dl1.hashCode() == 2); |
||||||
|
assertNotSame(dl1, dl2); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testDeepClone() { |
||||||
|
DefineList dl1 = new DefineList(NUM_DEFINES); |
||||||
|
DefineList dl2 = dl1.deepClone(); |
||||||
|
|
||||||
|
assertFalse(dl1 == dl2); |
||||||
|
assertTrue(dl1.equals(dl2)); |
||||||
|
assertTrue(dl1.hashCode() == dl2.hashCode()); |
||||||
|
|
||||||
|
dl1.set(BOOL_VAR, true); |
||||||
|
dl2 = dl1.deepClone(); |
||||||
|
|
||||||
|
assertTrue(dl1.equals(dl2)); |
||||||
|
assertTrue(dl1.hashCode() == dl2.hashCode()); |
||||||
|
|
||||||
|
dl1.set(INT_VAR, 123); |
||||||
|
|
||||||
|
assertFalse(dl1.equals(dl2)); |
||||||
|
assertFalse(dl1.hashCode() == dl2.hashCode()); |
||||||
|
|
||||||
|
dl2 = dl1.deepClone(); |
||||||
|
|
||||||
|
assertTrue(dl1.equals(dl2)); |
||||||
|
assertTrue(dl1.hashCode() == dl2.hashCode()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testGenerateSource() { |
||||||
|
DefineList dl = new DefineList(NUM_DEFINES); |
||||||
|
|
||||||
|
assertEquals("", generateSource(dl)); |
||||||
|
|
||||||
|
dl.set(BOOL_VAR, true); |
||||||
|
|
||||||
|
assertEquals("#define BOOL_VAR 1\n", generateSource(dl)); |
||||||
|
|
||||||
|
dl.set(INT_VAR, 123); |
||||||
|
|
||||||
|
assertEquals("#define BOOL_VAR 1\n" + |
||||||
|
"#define INT_VAR 123\n", generateSource(dl)); |
||||||
|
|
||||||
|
dl.set(BOOL_VAR, false); |
||||||
|
|
||||||
|
assertEquals("#define INT_VAR 123\n", generateSource(dl)); |
||||||
|
|
||||||
|
dl.set(BOOL_VAR, true); |
||||||
|
|
||||||
|
// should have predictable ordering based on defineId
|
||||||
|
assertEquals("#define BOOL_VAR 1\n" + |
||||||
|
"#define INT_VAR 123\n", generateSource(dl)); |
||||||
|
} |
||||||
|
|
||||||
|
private static String doLookup(HashMap<DefineList, String> map, boolean boolVal, int intVal, float floatVal) { |
||||||
|
DefineList dl = new DefineList(NUM_DEFINES); |
||||||
|
dl.set(BOOL_VAR, boolVal); |
||||||
|
dl.set(INT_VAR, intVal); |
||||||
|
dl.set(FLOAT_VAR, floatVal); |
||||||
|
return map.get(dl); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testHashLookup() { |
||||||
|
String STR_EMPTY = "This is an empty define list"; |
||||||
|
String STR_INT = "This define list has an int value"; |
||||||
|
String STR_BOOL = "This define list just has boolean value set"; |
||||||
|
String STR_BOOL_INT = "This define list has both a boolean and int value"; |
||||||
|
String STR_BOOL_INT_FLOAT = "This define list has a boolean, int, and float value"; |
||||||
|
|
||||||
|
HashMap<DefineList, String> map = new HashMap<DefineList, String>(); |
||||||
|
|
||||||
|
DefineList lookup = new DefineList(NUM_DEFINES); |
||||||
|
|
||||||
|
map.put(lookup.deepClone(), STR_EMPTY); |
||||||
|
|
||||||
|
lookup.set(BOOL_VAR, true); |
||||||
|
map.put(lookup.deepClone(), STR_BOOL); |
||||||
|
|
||||||
|
lookup.set(BOOL_VAR, false); |
||||||
|
lookup.set(INT_VAR, 123); |
||||||
|
map.put(lookup.deepClone(), STR_INT); |
||||||
|
|
||||||
|
lookup.set(BOOL_VAR, true); |
||||||
|
map.put(lookup.deepClone(), STR_BOOL_INT); |
||||||
|
|
||||||
|
lookup.set(FLOAT_VAR, FastMath.PI); |
||||||
|
map.put(lookup.deepClone(), STR_BOOL_INT_FLOAT); |
||||||
|
|
||||||
|
assertEquals(doLookup(map, false, 0, 0f), STR_EMPTY); |
||||||
|
assertEquals(doLookup(map, false, 123, 0f), STR_INT); |
||||||
|
assertEquals(doLookup(map, true, 0, 0f), STR_BOOL); |
||||||
|
assertEquals(doLookup(map, true, 123, 0f), STR_BOOL_INT); |
||||||
|
assertEquals(doLookup(map, true, 123, FastMath.PI), STR_BOOL_INT_FLOAT); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
MaterialDef Test Material { |
||||||
|
Technique Test { |
||||||
|
VertexShader GLSL100 : test.vert |
||||||
|
FragmentShader GLSL100 : test.frag |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
MaterialDef Test Material { |
||||||
|
Technique A { |
||||||
|
} |
||||||
|
Technique B { |
||||||
|
VertexShader GLSL100 : test.vert |
||||||
|
FragmentShader GLSL100 : test.frag |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
MaterialDef Test Material { |
||||||
|
Technique Test { |
||||||
|
VertexShader GLSL150 : test150.vert |
||||||
|
FragmentShader GLSL150 : test150.frag |
||||||
|
} |
||||||
|
Technique Test { |
||||||
|
VertexShader GLSL100 : test.vert |
||||||
|
FragmentShader GLSL100 : test.frag |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
MaterialDef Simple { |
||||||
|
MaterialParameters { |
||||||
|
Color Color |
||||||
|
} |
||||||
|
Technique { |
||||||
|
WorldParameters { |
||||||
|
WorldViewProjectionMatrix |
||||||
|
} |
||||||
|
VertexShaderNodes { |
||||||
|
ShaderNode CommonVert { |
||||||
|
Definition : CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn |
||||||
|
InputMappings { |
||||||
|
worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix |
||||||
|
modelPosition = Global.position.xyz |
||||||
|
} |
||||||
|
OutputMappings { |
||||||
|
Global.position = projPosition |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
FragmentShaderNodes { |
||||||
|
ShaderNode ColorMult { |
||||||
|
Definition : ColorMult : Common/MatDefs/ShaderNodes/Basic/ColorMult.j3sn |
||||||
|
InputMappings { |
||||||
|
color1 = MatParam.Color |
||||||
|
color2 = Global.color |
||||||
|
} |
||||||
|
OutputMappings { |
||||||
|
Global.color = outColor |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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(); |
|
||||||
} |
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue