commit
0ec2263ae9
@ -0,0 +1,793 @@ |
||||
/* |
||||
* 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.app; |
||||
|
||||
import com.jme3.app.state.AppState; |
||||
import com.jme3.app.state.AppStateManager; |
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.audio.AudioContext; |
||||
import com.jme3.audio.AudioRenderer; |
||||
import com.jme3.audio.Listener; |
||||
import com.jme3.input.*; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.profile.AppProfiler; |
||||
import com.jme3.profile.AppStep; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.Renderer; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.system.*; |
||||
import com.jme3.system.JmeContext.Type; |
||||
import java.net.MalformedURLException; |
||||
import java.net.URL; |
||||
import java.util.concurrent.Callable; |
||||
import java.util.concurrent.ConcurrentLinkedQueue; |
||||
import java.util.concurrent.Future; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* The <code>LegacyApplication</code> class represents an instance of a |
||||
* real-time 3D rendering jME application. |
||||
* |
||||
* An <code>LegacyApplication</code> provides all the tools that are commonly used in jME3 |
||||
* applications. |
||||
* |
||||
* jME3 applications *SHOULD NOT EXTEND* this class but extend {@link com.jme3.app.SimpleApplication} instead. |
||||
* |
||||
*/ |
||||
public class LegacyApplication implements Application, SystemListener { |
||||
|
||||
private static final Logger logger = Logger.getLogger(LegacyApplication.class.getName()); |
||||
|
||||
protected AssetManager assetManager; |
||||
|
||||
protected AudioRenderer audioRenderer; |
||||
protected Renderer renderer; |
||||
protected RenderManager renderManager; |
||||
protected ViewPort viewPort; |
||||
protected ViewPort guiViewPort; |
||||
|
||||
protected JmeContext context; |
||||
protected AppSettings settings; |
||||
protected Timer timer = new NanoTimer(); |
||||
protected Camera cam; |
||||
protected Listener listener; |
||||
|
||||
protected boolean inputEnabled = true; |
||||
protected LostFocusBehavior lostFocusBehavior = LostFocusBehavior.ThrottleOnLostFocus; |
||||
protected float speed = 1f; |
||||
protected boolean paused = false; |
||||
protected MouseInput mouseInput; |
||||
protected KeyInput keyInput; |
||||
protected JoyInput joyInput; |
||||
protected TouchInput touchInput; |
||||
protected InputManager inputManager; |
||||
protected AppStateManager stateManager; |
||||
|
||||
protected AppProfiler prof; |
||||
|
||||
private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>(); |
||||
|
||||
/** |
||||
* Create a new instance of <code>LegacyApplication</code>. |
||||
*/ |
||||
public LegacyApplication() { |
||||
this((AppState[])null); |
||||
} |
||||
|
||||
/** |
||||
* Create a new instance of <code>LegacyApplication</code>, preinitialized |
||||
* with the specified set of app states. |
||||
*/ |
||||
public LegacyApplication( AppState... initialStates ) { |
||||
initStateManager(); |
||||
|
||||
if (initialStates != null) { |
||||
for (AppState a : initialStates) { |
||||
if (a != null) { |
||||
stateManager.attach(a); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Determine the application's behavior when unfocused. |
||||
* |
||||
* @return The lost focus behavior of the application. |
||||
*/ |
||||
public LostFocusBehavior getLostFocusBehavior() { |
||||
return lostFocusBehavior; |
||||
} |
||||
|
||||
/** |
||||
* Change the application's behavior when unfocused. |
||||
* |
||||
* By default, the application will |
||||
* {@link LostFocusBehavior#ThrottleOnLostFocus throttle the update loop} |
||||
* so as to not take 100% CPU usage when it is not in focus, e.g. |
||||
* alt-tabbed, minimized, or obstructed by another window. |
||||
* |
||||
* @param lostFocusBehavior The new lost focus behavior to use. |
||||
* |
||||
* @see LostFocusBehavior |
||||
*/ |
||||
public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior) { |
||||
this.lostFocusBehavior = lostFocusBehavior; |
||||
} |
||||
|
||||
/** |
||||
* Returns true if pause on lost focus is enabled, false otherwise. |
||||
* |
||||
* @return true if pause on lost focus is enabled |
||||
* |
||||
* @see #getLostFocusBehavior() |
||||
*/ |
||||
public boolean isPauseOnLostFocus() { |
||||
return getLostFocusBehavior() == LostFocusBehavior.PauseOnLostFocus; |
||||
} |
||||
|
||||
/** |
||||
* Enable or disable pause on lost focus. |
||||
* <p> |
||||
* By default, pause on lost focus is enabled. |
||||
* If enabled, the application will stop updating |
||||
* when it loses focus or becomes inactive (e.g. alt-tab). |
||||
* For online or real-time applications, this might not be preferable, |
||||
* so this feature should be set to disabled. For other applications, |
||||
* it is best to keep it on so that CPU usage is not used when |
||||
* not necessary. |
||||
* |
||||
* @param pauseOnLostFocus True to enable pause on lost focus, false |
||||
* otherwise. |
||||
* |
||||
* @see #setLostFocusBehavior(com.jme3.app.LostFocusBehavior) |
||||
*/ |
||||
public void setPauseOnLostFocus(boolean pauseOnLostFocus) { |
||||
if (pauseOnLostFocus) { |
||||
setLostFocusBehavior(LostFocusBehavior.PauseOnLostFocus); |
||||
} else { |
||||
setLostFocusBehavior(LostFocusBehavior.Disabled); |
||||
} |
||||
} |
||||
|
||||
@Deprecated |
||||
public void setAssetManager(AssetManager assetManager){ |
||||
if (this.assetManager != null) |
||||
throw new IllegalStateException("Can only set asset manager" |
||||
+ " before initialization."); |
||||
|
||||
this.assetManager = assetManager; |
||||
} |
||||
|
||||
private void initAssetManager(){ |
||||
URL assetCfgUrl = null; |
||||
|
||||
if (settings != null){ |
||||
String assetCfg = settings.getString("AssetConfigURL"); |
||||
if (assetCfg != null){ |
||||
try { |
||||
assetCfgUrl = new URL(assetCfg); |
||||
} catch (MalformedURLException ex) { |
||||
} |
||||
if (assetCfgUrl == null) { |
||||
assetCfgUrl = LegacyApplication.class.getClassLoader().getResource(assetCfg); |
||||
if (assetCfgUrl == null) { |
||||
logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
if (assetCfgUrl == null) { |
||||
assetCfgUrl = JmeSystem.getPlatformAssetConfigURL(); |
||||
} |
||||
if (assetManager == null){ |
||||
assetManager = JmeSystem.newAssetManager(assetCfgUrl); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Set the display settings to define the display created. |
||||
* <p> |
||||
* Examples of display parameters include display pixel width and height, |
||||
* color bit depth, z-buffer bits, anti-aliasing samples, and update frequency. |
||||
* If this method is called while the application is already running, then |
||||
* {@link #restart() } must be called to apply the settings to the display. |
||||
* |
||||
* @param settings The settings to set. |
||||
*/ |
||||
public void setSettings(AppSettings settings){ |
||||
this.settings = settings; |
||||
if (context != null && settings.useInput() != inputEnabled){ |
||||
// may need to create or destroy input based
|
||||
// on settings change
|
||||
inputEnabled = !inputEnabled; |
||||
if (inputEnabled){ |
||||
initInput(); |
||||
}else{ |
||||
destroyInput(); |
||||
} |
||||
}else{ |
||||
inputEnabled = settings.useInput(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Sets the Timer implementation that will be used for calculating |
||||
* frame times. By default, Application will use the Timer as returned |
||||
* by the current JmeContext implementation. |
||||
*/ |
||||
public void setTimer(Timer timer){ |
||||
this.timer = timer; |
||||
|
||||
if (timer != null) { |
||||
timer.reset(); |
||||
} |
||||
|
||||
if (renderManager != null) { |
||||
renderManager.setTimer(timer); |
||||
} |
||||
} |
||||
|
||||
public Timer getTimer(){ |
||||
return timer; |
||||
} |
||||
|
||||
private void initDisplay(){ |
||||
// aquire important objects
|
||||
// from the context
|
||||
settings = context.getSettings(); |
||||
|
||||
// Only reset the timer if a user has not already provided one
|
||||
if (timer == null) { |
||||
timer = context.getTimer(); |
||||
} |
||||
|
||||
renderer = context.getRenderer(); |
||||
} |
||||
|
||||
private void initAudio(){ |
||||
if (settings.getAudioRenderer() != null && context.getType() != Type.Headless){ |
||||
audioRenderer = JmeSystem.newAudioRenderer(settings); |
||||
audioRenderer.initialize(); |
||||
AudioContext.setAudioRenderer(audioRenderer); |
||||
|
||||
listener = new Listener(); |
||||
audioRenderer.setListener(listener); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates the camera to use for rendering. Default values are perspective |
||||
* projection with 45° field of view, with near and far values 1 and 1000 |
||||
* units respectively. |
||||
*/ |
||||
private void initCamera(){ |
||||
cam = new Camera(settings.getWidth(), settings.getHeight()); |
||||
|
||||
cam.setFrustumPerspective(45f, (float)cam.getWidth() / cam.getHeight(), 1f, 1000f); |
||||
cam.setLocation(new Vector3f(0f, 0f, 10f)); |
||||
cam.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y); |
||||
|
||||
renderManager = new RenderManager(renderer); |
||||
//Remy - 09/14/2010 setted the timer in the renderManager
|
||||
renderManager.setTimer(timer); |
||||
|
||||
if (prof != null) { |
||||
renderManager.setAppProfiler(prof); |
||||
} |
||||
|
||||
viewPort = renderManager.createMainView("Default", cam); |
||||
viewPort.setClearFlags(true, true, true); |
||||
|
||||
// Create a new cam for the gui
|
||||
Camera guiCam = new Camera(settings.getWidth(), settings.getHeight()); |
||||
guiViewPort = renderManager.createPostView("Gui Default", guiCam); |
||||
guiViewPort.setClearFlags(false, false, false); |
||||
} |
||||
|
||||
/** |
||||
* Initializes mouse and keyboard input. Also |
||||
* initializes joystick input if joysticks are enabled in the |
||||
* AppSettings. |
||||
*/ |
||||
private void initInput(){ |
||||
mouseInput = context.getMouseInput(); |
||||
if (mouseInput != null) |
||||
mouseInput.initialize(); |
||||
|
||||
keyInput = context.getKeyInput(); |
||||
if (keyInput != null) |
||||
keyInput.initialize(); |
||||
|
||||
touchInput = context.getTouchInput(); |
||||
if (touchInput != null) |
||||
touchInput.initialize(); |
||||
|
||||
if (!settings.getBoolean("DisableJoysticks")){ |
||||
joyInput = context.getJoyInput(); |
||||
if (joyInput != null) |
||||
joyInput.initialize(); |
||||
} |
||||
|
||||
inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput); |
||||
} |
||||
|
||||
private void initStateManager(){ |
||||
stateManager = new AppStateManager(this); |
||||
|
||||
// Always register a ResetStatsState to make sure
|
||||
// that the stats are cleared every frame
|
||||
stateManager.attach(new ResetStatsState()); |
||||
} |
||||
|
||||
/** |
||||
* @return The {@link AssetManager asset manager} for this application. |
||||
*/ |
||||
public AssetManager getAssetManager(){ |
||||
return assetManager; |
||||
} |
||||
|
||||
/** |
||||
* @return the {@link InputManager input manager}. |
||||
*/ |
||||
public InputManager getInputManager(){ |
||||
return inputManager; |
||||
} |
||||
|
||||
/** |
||||
* @return the {@link AppStateManager app state manager} |
||||
*/ |
||||
public AppStateManager getStateManager() { |
||||
return stateManager; |
||||
} |
||||
|
||||
/** |
||||
* @return the {@link RenderManager render manager} |
||||
*/ |
||||
public RenderManager getRenderManager() { |
||||
return renderManager; |
||||
} |
||||
|
||||
/** |
||||
* @return The {@link Renderer renderer} for the application |
||||
*/ |
||||
public Renderer getRenderer(){ |
||||
return renderer; |
||||
} |
||||
|
||||
/** |
||||
* @return The {@link AudioRenderer audio renderer} for the application |
||||
*/ |
||||
public AudioRenderer getAudioRenderer() { |
||||
return audioRenderer; |
||||
} |
||||
|
||||
/** |
||||
* @return The {@link Listener listener} object for audio |
||||
*/ |
||||
public Listener getListener() { |
||||
return listener; |
||||
} |
||||
|
||||
/** |
||||
* @return The {@link JmeContext display context} for the application |
||||
*/ |
||||
public JmeContext getContext(){ |
||||
return context; |
||||
} |
||||
|
||||
/** |
||||
* @return The {@link Camera camera} for the application |
||||
*/ |
||||
public Camera getCamera(){ |
||||
return cam; |
||||
} |
||||
|
||||
/** |
||||
* Starts the application in {@link Type#Display display} mode. |
||||
* |
||||
* @see #start(com.jme3.system.JmeContext.Type) |
||||
*/ |
||||
public void start(){ |
||||
start(JmeContext.Type.Display, false); |
||||
} |
||||
|
||||
/** |
||||
* Starts the application in {@link Type#Display display} mode. |
||||
* |
||||
* @see #start(com.jme3.system.JmeContext.Type) |
||||
*/ |
||||
public void start(boolean waitFor){ |
||||
start(JmeContext.Type.Display, waitFor); |
||||
} |
||||
|
||||
/** |
||||
* Starts the application. |
||||
* Creating a rendering context and executing |
||||
* the main loop in a separate thread. |
||||
*/ |
||||
public void start(JmeContext.Type contextType) { |
||||
start(contextType, false); |
||||
} |
||||
|
||||
/** |
||||
* Starts the application. |
||||
* Creating a rendering context and executing |
||||
* the main loop in a separate thread. |
||||
*/ |
||||
public void start(JmeContext.Type contextType, boolean waitFor){ |
||||
if (context != null && context.isCreated()){ |
||||
logger.warning("start() called when application already created!"); |
||||
return; |
||||
} |
||||
|
||||
if (settings == null){ |
||||
settings = new AppSettings(true); |
||||
} |
||||
|
||||
logger.log(Level.FINE, "Starting application: {0}", getClass().getName()); |
||||
context = JmeSystem.newContext(settings, contextType); |
||||
context.setSystemListener(this); |
||||
context.create(waitFor); |
||||
} |
||||
|
||||
/** |
||||
* Sets an AppProfiler hook that will be called back for |
||||
* specific steps within a single update frame. Value defaults |
||||
* to null. |
||||
*/ |
||||
public void setAppProfiler(AppProfiler prof) { |
||||
this.prof = prof; |
||||
if (renderManager != null) { |
||||
renderManager.setAppProfiler(prof); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the current AppProfiler hook, or null if none is set. |
||||
*/ |
||||
public AppProfiler getAppProfiler() { |
||||
return prof; |
||||
} |
||||
|
||||
/** |
||||
* Initializes the application's canvas for use. |
||||
* <p> |
||||
* After calling this method, cast the {@link #getContext() context} to |
||||
* {@link JmeCanvasContext}, |
||||
* then acquire the canvas with {@link JmeCanvasContext#getCanvas() } |
||||
* and attach it to an AWT/Swing Frame. |
||||
* The rendering thread will start when the canvas becomes visible on |
||||
* screen, however if you wish to start the context immediately you |
||||
* may call {@link #startCanvas() } to force the rendering thread |
||||
* to start. |
||||
* |
||||
* @see JmeCanvasContext |
||||
* @see Type#Canvas |
||||
*/ |
||||
public void createCanvas(){ |
||||
if (context != null && context.isCreated()){ |
||||
logger.warning("createCanvas() called when application already created!"); |
||||
return; |
||||
} |
||||
|
||||
if (settings == null){ |
||||
settings = new AppSettings(true); |
||||
} |
||||
|
||||
logger.log(Level.FINE, "Starting application: {0}", getClass().getName()); |
||||
context = JmeSystem.newContext(settings, JmeContext.Type.Canvas); |
||||
context.setSystemListener(this); |
||||
} |
||||
|
||||
/** |
||||
* Starts the rendering thread after createCanvas() has been called. |
||||
* <p> |
||||
* Same as calling startCanvas(false) |
||||
* |
||||
* @see #startCanvas(boolean) |
||||
*/ |
||||
public void startCanvas(){ |
||||
startCanvas(false); |
||||
} |
||||
|
||||
/** |
||||
* Starts the rendering thread after createCanvas() has been called. |
||||
* <p> |
||||
* Calling this method is optional, the canvas will start automatically |
||||
* when it becomes visible. |
||||
* |
||||
* @param waitFor If true, the current thread will block until the |
||||
* rendering thread is running |
||||
*/ |
||||
public void startCanvas(boolean waitFor){ |
||||
context.create(waitFor); |
||||
} |
||||
|
||||
/** |
||||
* Internal use only. |
||||
*/ |
||||
public void reshape(int w, int h){ |
||||
if (renderManager != null) { |
||||
renderManager.notifyReshape(w, h); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Restarts the context, applying any changed settings. |
||||
* <p> |
||||
* Changes to the {@link AppSettings} of this Application are not |
||||
* applied immediately; calling this method forces the context |
||||
* to restart, applying the new settings. |
||||
*/ |
||||
public void restart(){ |
||||
context.setSettings(settings); |
||||
context.restart(); |
||||
} |
||||
|
||||
/** |
||||
* Requests the context to close, shutting down the main loop |
||||
* and making necessary cleanup operations. |
||||
* |
||||
* Same as calling stop(false) |
||||
* |
||||
* @see #stop(boolean) |
||||
*/ |
||||
public void stop(){ |
||||
stop(false); |
||||
} |
||||
|
||||
/** |
||||
* Requests the context to close, shutting down the main loop |
||||
* and making necessary cleanup operations. |
||||
* After the application has stopped, it cannot be used anymore. |
||||
*/ |
||||
public void stop(boolean waitFor){ |
||||
logger.log(Level.FINE, "Closing application: {0}", getClass().getName()); |
||||
context.destroy(waitFor); |
||||
} |
||||
|
||||
/** |
||||
* Do not call manually. |
||||
* Callback from ContextListener. |
||||
* <p> |
||||
* Initializes the <code>Application</code>, by creating a display and |
||||
* default camera. If display settings are not specified, a default |
||||
* 640x480 display is created. Default values are used for the camera; |
||||
* perspective projection with 45° field of view, with near |
||||
* and far values 1 and 1000 units respectively. |
||||
*/ |
||||
public void initialize(){ |
||||
if (assetManager == null){ |
||||
initAssetManager(); |
||||
} |
||||
|
||||
initDisplay(); |
||||
initCamera(); |
||||
|
||||
if (inputEnabled){ |
||||
initInput(); |
||||
} |
||||
initAudio(); |
||||
|
||||
// update timer so that the next delta is not too large
|
||||
// timer.update();
|
||||
timer.reset(); |
||||
|
||||
// user code here..
|
||||
} |
||||
|
||||
/** |
||||
* Internal use only. |
||||
*/ |
||||
public void handleError(String errMsg, Throwable t){ |
||||
// Print error to log.
|
||||
logger.log(Level.SEVERE, errMsg, t); |
||||
// Display error message on screen if not in headless mode
|
||||
if (context.getType() != JmeContext.Type.Headless) { |
||||
if (t != null) { |
||||
JmeSystem.showErrorDialog(errMsg + "\n" + t.getClass().getSimpleName() + |
||||
(t.getMessage() != null ? ": " + t.getMessage() : "")); |
||||
} else { |
||||
JmeSystem.showErrorDialog(errMsg); |
||||
} |
||||
} |
||||
|
||||
stop(); // stop the application
|
||||
} |
||||
|
||||
/** |
||||
* Internal use only. |
||||
*/ |
||||
public void gainFocus(){ |
||||
if (lostFocusBehavior != LostFocusBehavior.Disabled) { |
||||
if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) { |
||||
paused = false; |
||||
} |
||||
context.setAutoFlushFrames(true); |
||||
if (inputManager != null) { |
||||
inputManager.reset(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Internal use only. |
||||
*/ |
||||
public void loseFocus(){ |
||||
if (lostFocusBehavior != LostFocusBehavior.Disabled){ |
||||
if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) { |
||||
paused = true; |
||||
} |
||||
context.setAutoFlushFrames(false); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Internal use only. |
||||
*/ |
||||
public void requestClose(boolean esc){ |
||||
context.destroy(false); |
||||
} |
||||
|
||||
/** |
||||
* Enqueues a task/callable object to execute in the jME3 |
||||
* rendering thread. |
||||
* <p> |
||||
* Callables are executed right at the beginning of the main loop. |
||||
* They are executed even if the application is currently paused |
||||
* or out of focus. |
||||
* |
||||
* @param callable The callable to run in the main jME3 thread |
||||
*/ |
||||
public <V> Future<V> enqueue(Callable<V> callable) { |
||||
AppTask<V> task = new AppTask<V>(callable); |
||||
taskQueue.add(task); |
||||
return task; |
||||
} |
||||
|
||||
/** |
||||
* Enqueues a runnable object to execute in the jME3 |
||||
* rendering thread. |
||||
* <p> |
||||
* Runnables are executed right at the beginning of the main loop. |
||||
* They are executed even if the application is currently paused |
||||
* or out of focus. |
||||
* |
||||
* @param runnable The runnable to run in the main jME3 thread |
||||
*/ |
||||
public void enqueue(Runnable runnable){ |
||||
enqueue(new RunnableWrapper(runnable)); |
||||
} |
||||
|
||||
/** |
||||
* Runs tasks enqueued via {@link #enqueue(Callable)} |
||||
*/ |
||||
protected void runQueuedTasks() { |
||||
AppTask<?> task; |
||||
while( (task = taskQueue.poll()) != null ) { |
||||
if (!task.isCancelled()) { |
||||
task.invoke(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Do not call manually. |
||||
* Callback from ContextListener. |
||||
*/ |
||||
public void update(){ |
||||
// Make sure the audio renderer is available to callables
|
||||
AudioContext.setAudioRenderer(audioRenderer); |
||||
|
||||
if (prof!=null) prof.appStep(AppStep.QueuedTasks); |
||||
runQueuedTasks(); |
||||
|
||||
if (speed == 0 || paused) |
||||
return; |
||||
|
||||
timer.update(); |
||||
|
||||
if (inputEnabled){ |
||||
if (prof!=null) prof.appStep(AppStep.ProcessInput); |
||||
inputManager.update(timer.getTimePerFrame()); |
||||
} |
||||
|
||||
if (audioRenderer != null){ |
||||
if (prof!=null) prof.appStep(AppStep.ProcessAudio); |
||||
audioRenderer.update(timer.getTimePerFrame()); |
||||
} |
||||
|
||||
// user code here..
|
||||
} |
||||
|
||||
protected void destroyInput(){ |
||||
if (mouseInput != null) |
||||
mouseInput.destroy(); |
||||
|
||||
if (keyInput != null) |
||||
keyInput.destroy(); |
||||
|
||||
if (joyInput != null) |
||||
joyInput.destroy(); |
||||
|
||||
if (touchInput != null) |
||||
touchInput.destroy(); |
||||
|
||||
inputManager = null; |
||||
} |
||||
|
||||
/** |
||||
* Do not call manually. |
||||
* Callback from ContextListener. |
||||
*/ |
||||
public void destroy(){ |
||||
stateManager.cleanup(); |
||||
|
||||
destroyInput(); |
||||
if (audioRenderer != null) |
||||
audioRenderer.cleanup(); |
||||
|
||||
timer.reset(); |
||||
} |
||||
|
||||
/** |
||||
* @return The GUI viewport. Which is used for the on screen |
||||
* statistics and FPS. |
||||
*/ |
||||
public ViewPort getGuiViewPort() { |
||||
return guiViewPort; |
||||
} |
||||
|
||||
public ViewPort getViewPort() { |
||||
return viewPort; |
||||
} |
||||
|
||||
private class RunnableWrapper implements Callable{ |
||||
private final Runnable runnable; |
||||
|
||||
public RunnableWrapper(Runnable runnable){ |
||||
this.runnable = runnable; |
||||
} |
||||
|
||||
@Override |
||||
public Object call(){ |
||||
runnable.run(); |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -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.*; |
||||
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, int lastTexUnit) { |
||||
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, int lastTexUnit) { |
||||
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,255 @@ |
||||
/* |
||||
* 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.bounding.BoundingSphere; |
||||
import com.jme3.light.*; |
||||
import com.jme3.material.*; |
||||
import com.jme3.material.RenderState.BlendMode; |
||||
import com.jme3.math.*; |
||||
import com.jme3.renderer.*; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.shader.*; |
||||
import com.jme3.util.TempVars; |
||||
|
||||
import java.util.EnumSet; |
||||
|
||||
public final class SinglePassAndImageBasedLightingLogic 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); |
||||
//Env textures units
|
||||
int irrUnit = -1; |
||||
int pemUnit = -1; |
||||
|
||||
static { |
||||
ADDITIVE_LIGHT.setBlendMode(BlendMode.AlphaAdditive); |
||||
ADDITIVE_LIGHT.setDepthWrite(false); |
||||
} |
||||
|
||||
private final int singlePassLightingDefineId; |
||||
private final int nbLightsDefineId; |
||||
|
||||
public SinglePassAndImageBasedLightingLogic(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, int lastTexUnit) { |
||||
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"); |
||||
Uniform lightProbeData = shader.getUniform("g_LightProbeData"); |
||||
lightProbeData.setVector4Length(1); |
||||
Uniform lightProbeIrrMap = shader.getUniform("g_IrradianceMap"); |
||||
Uniform lightProbePemMap = shader.getUniform("g_PrefEnvMap"); |
||||
|
||||
LightProbe lightProbe = null; |
||||
if (startIndex != 0) { |
||||
// apply additive blending for 2nd and future passes
|
||||
rm.getRenderer().applyRenderState(ADDITIVE_LIGHT); |
||||
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black); |
||||
}else{ |
||||
lightProbe = extractIndirectLights(lightList,true); |
||||
ambientColor.setValue(VarType.Vector4, ambientLightColor); |
||||
} |
||||
|
||||
//If there is a lightProbe in the list we force it's render on the first pass
|
||||
if(lightProbe != null){ |
||||
BoundingSphere s = (BoundingSphere)lightProbe.getBounds(); |
||||
lightProbeData.setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f/s.getRadius(), 0); |
||||
//assigning new texture indexes if they have never been assigned.
|
||||
if( irrUnit == -1 ){ |
||||
irrUnit = lastTexUnit++; |
||||
pemUnit = lastTexUnit++; |
||||
} |
||||
rm.getRenderer().setTexture(irrUnit, lightProbe.getIrradianceMap()); |
||||
lightProbeIrrMap.setValue(VarType.Int, irrUnit); |
||||
rm.getRenderer().setTexture(pemUnit, lightProbe.getPrefilteredEnvMap()); |
||||
lightProbePemMap.setValue(VarType.Int, pemUnit); |
||||
} else { |
||||
//Disable IBL for this pass
|
||||
lightProbeData.setVector4InArray(0,0,0,-1, 0); |
||||
} |
||||
|
||||
int lightDataIndex = 0; |
||||
TempVars vars = TempVars.get(); |
||||
Vector4f tmpVec = vars.vect4f1; |
||||
int curIndex; |
||||
int endIndex = numLights + startIndex; |
||||
boolean useIBL = false; |
||||
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
|
||||
|
||||
if(l.getType() != Light.Type.Probe){ |
||||
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); |
||||
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); |
||||
|
||||
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); |
||||
|
||||
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex); |
||||
lightDataIndex++; |
||||
|
||||
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f); |
||||
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 lastTexUnit) { |
||||
int nbRenderedLights = 0; |
||||
Renderer renderer = renderManager.getRenderer(); |
||||
int batchSize = renderManager.getSinglePassLightBatchSize(); |
||||
if (lights.size() == 0) { |
||||
updateLightListUniforms(shader, geometry, lights,batchSize, renderManager, 0, lastTexUnit); |
||||
renderer.setShader(shader); |
||||
renderMeshFromGeometry(renderer, geometry); |
||||
} else { |
||||
while (nbRenderedLights < lights.size()) { |
||||
nbRenderedLights = updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, nbRenderedLights, lastTexUnit); |
||||
renderer.setShader(shader); |
||||
renderMeshFromGeometry(renderer, geometry); |
||||
} |
||||
} |
||||
return; |
||||
} |
||||
|
||||
protected LightProbe extractIndirectLights(LightList lightList, boolean removeLights) { |
||||
ambientLightColor.set(0, 0, 0, 1); |
||||
LightProbe probe = null; |
||||
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); |
||||
j--; |
||||
} |
||||
} |
||||
if (l instanceof LightProbe) { |
||||
probe = (LightProbe)l; |
||||
if(removeLights){ |
||||
lightList.remove(l); |
||||
j--; |
||||
} |
||||
} |
||||
} |
||||
ambientLightColor.a = 1.0f; |
||||
return probe; |
||||
} |
||||
} |
@ -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 lastTexUnit) { |
||||
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,157 @@ |
||||
/* |
||||
* 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.Light.Type; |
||||
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.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); |
||||
|
||||
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 updateLightListUniforms(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 index = 0; |
||||
for (DirectionalLight light : tempDirLights) { |
||||
ColorRGBA color = light.getColor(); |
||||
Vector3f dir = light.getDirection(); |
||||
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++); |
||||
lightData.setVector4InArray(dir.x, dir.y, dir.z, 1f, index++); |
||||
} |
||||
|
||||
for (PointLight light : tempPointLights) { |
||||
ColorRGBA color = light.getColor(); |
||||
Vector3f pos = light.getPosition(); |
||||
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++); |
||||
lightData.setVector4InArray(pos.x, pos.y, pos.z, 1f, index++); |
||||
} |
||||
|
||||
for (SpotLight light : tempSpotLights) { |
||||
ColorRGBA color = light.getColor(); |
||||
Vector3f pos = light.getPosition(); |
||||
Vector3f dir = light.getDirection(); |
||||
float invRange = light.getInvSpotRange(); |
||||
float spotAngleCos = light.getPackedAngleCos(); |
||||
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++); |
||||
lightData.setVector4InArray(pos.x, pos.y, pos.z, invRange, index++); |
||||
lightData.setVector4InArray(dir.x, dir.y, dir.z, spotAngleCos, index++); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) { |
||||
Renderer renderer = renderManager.getRenderer(); |
||||
updateLightListUniforms(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, int lastTexUnit); |
||||
} |
@ -1,286 +1,179 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shader; |
||||
|
||||
import com.jme3.export.*; |
||||
import com.jme3.material.MatParam; |
||||
import com.jme3.material.TechniqueDef; |
||||
import com.jme3.util.ListMap; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.Map; |
||||
import java.util.TreeMap; |
||||
|
||||
public final class DefineList implements Savable, Cloneable { |
||||
|
||||
private static final String ONE = "1"; |
||||
|
||||
private TreeMap<String, String> defines = new TreeMap<String, String>(); |
||||
private String compiled = null; |
||||
private int cachedHashCode = 0; |
||||
|
||||
public void write(JmeExporter ex) throws IOException{ |
||||
OutputCapsule oc = ex.getCapsule(this); |
||||
|
||||
String[] keys = new String[defines.size()]; |
||||
String[] vals = new String[defines.size()]; |
||||
|
||||
int i = 0; |
||||
for (Map.Entry<String, String> define : defines.entrySet()){ |
||||
keys[i] = define.getKey(); |
||||
vals[i] = define.getValue(); |
||||
i++; |
||||
} |
||||
|
||||
oc.write(keys, "keys", null); |
||||
oc.write(vals, "vals", null); |
||||
} |
||||
|
||||
public void read(JmeImporter im) throws IOException{ |
||||
InputCapsule ic = im.getCapsule(this); |
||||
|
||||
String[] keys = ic.readStringArray("keys", null); |
||||
String[] vals = ic.readStringArray("vals", null); |
||||
for (int i = 0; i < keys.length; i++){ |
||||
defines.put(keys[i], vals[i]); |
||||
} |
||||
} |
||||
|
||||
public void clear() { |
||||
defines.clear(); |
||||
compiled = ""; |
||||
cachedHashCode = 0; |
||||
} |
||||
|
||||
public String get(String key){ |
||||
return defines.get(key); |
||||
} |
||||
|
||||
@Override |
||||
public DefineList clone() { |
||||
try { |
||||
DefineList clone = (DefineList) super.clone(); |
||||
clone.cachedHashCode = 0; |
||||
clone.compiled = null; |
||||
clone.defines = (TreeMap<String, String>) defines.clone(); |
||||
return clone; |
||||
} catch (CloneNotSupportedException ex) { |
||||
throw new AssertionError(); |
||||
} |
||||
} |
||||
|
||||
public boolean set(String key, VarType type, Object val){ |
||||
if (val == null){ |
||||
defines.remove(key); |
||||
compiled = null; |
||||
cachedHashCode = 0; |
||||
return true; |
||||
} |
||||
|
||||
switch (type){ |
||||
case Boolean: |
||||
if (((Boolean) val).booleanValue()) { |
||||
// same literal, != will work
|
||||
if (defines.put(key, ONE) != ONE) { |
||||
compiled = null; |
||||
cachedHashCode = 0; |
||||
return true; |
||||
} |
||||
} else if (defines.containsKey(key)) { |
||||
defines.remove(key); |
||||
compiled = null; |
||||
cachedHashCode = 0; |
||||
return true; |
||||
} |
||||
|
||||
break; |
||||
case Float: |
||||
case Int: |
||||
String newValue = val.toString(); |
||||
String original = defines.put(key, newValue); |
||||
if (!val.equals(original)) { |
||||
compiled = null; |
||||
cachedHashCode = 0; |
||||
return true; |
||||
} |
||||
break; |
||||
default: |
||||
// same literal, != will work
|
||||
if (defines.put(key, ONE) != ONE) { |
||||
compiled = null; |
||||
cachedHashCode = 0; |
||||
return true; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public boolean remove(String key){ |
||||
if (defines.remove(key) != null) { |
||||
compiled = null; |
||||
cachedHashCode = 0; |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public void addFrom(DefineList other){ |
||||
if (other == null) { |
||||
return; |
||||
} |
||||
compiled = null; |
||||
cachedHashCode = 0; |
||||
defines.putAll(other.defines); |
||||
} |
||||
|
||||
public String getCompiled(){ |
||||
if (compiled == null){ |
||||
StringBuilder sb = new StringBuilder(); |
||||
for (Map.Entry<String, String> entry : defines.entrySet()){ |
||||
sb.append("#define ").append(entry.getKey()).append(" "); |
||||
sb.append(entry.getValue()).append('\n'); |
||||
} |
||||
compiled = sb.toString(); |
||||
} |
||||
return compiled; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
final DefineList other = (DefineList) obj; |
||||
return defines.equals(other.defines); |
||||
} |
||||
|
||||
/** |
||||
* Update defines if the define list changed based on material parameters. |
||||
* @param params |
||||
* @param def |
||||
* @return true if defines was updated |
||||
*/ |
||||
public boolean update(ListMap params, TechniqueDef def){ |
||||
if(equalsParams(params, def)){ |
||||
return false; |
||||
} |
||||
// Defines were changed, update define list
|
||||
clear(); |
||||
for(int i=0;i<params.size();i++) { |
||||
MatParam param = (MatParam)params.getValue(i); |
||||
String defineName = def.getShaderParamDefine(param.getName()); |
||||
if (defineName != null) { |
||||
set(defineName, param.getVarType(), param.getValue()); |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private boolean equalsParams(ListMap params, TechniqueDef def) { |
||||
|
||||
int size = 0; |
||||
|
||||
for(int i = 0; i < params.size() ; i++ ) { |
||||
MatParam param = (MatParam)params.getValue(i); |
||||
String key = def.getShaderParamDefine(param.getName()); |
||||
if (key != null) { |
||||
Object val = param.getValue(); |
||||
if (val != null) { |
||||
|
||||
switch (param.getVarType()) { |
||||
case Boolean: { |
||||
String current = defines.get(key); |
||||
if (((Boolean) val).booleanValue()) { |
||||
if (current == null || current != ONE) { |
||||
return false; |
||||
} |
||||
size++; |
||||
} else { |
||||
if (current != null) { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
break; |
||||
case Float: |
||||
case Int: { |
||||
String newValue = val.toString(); |
||||
String current = defines.get(key); |
||||
if (!newValue.equals(current)) { |
||||
return false; |
||||
} |
||||
size++; |
||||
} |
||||
break; |
||||
default: { |
||||
if (!defines.containsKey(key)) { |
||||
return false; |
||||
} |
||||
size++; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
if (size != defines.size()) { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
if (cachedHashCode == 0) { |
||||
cachedHashCode = defines.hashCode(); |
||||
} |
||||
return cachedHashCode; |
||||
} |
||||
|
||||
@Override |
||||
public String toString(){ |
||||
StringBuilder sb = new StringBuilder(); |
||||
int i = 0; |
||||
for (Map.Entry<String, String> entry : defines.entrySet()) { |
||||
sb.append(entry.getKey()).append("=").append(entry.getValue()); |
||||
if (i != defines.size() - 1) { |
||||
sb.append(", "); |
||||
} |
||||
i++; |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
} |
||||
/* |
||||
* Copyright (c) 2009-2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shader; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* The new define list. |
||||
* |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public final class DefineList { |
||||
|
||||
public static final int MAX_DEFINES = 64; |
||||
|
||||
private long hash; |
||||
private final int[] vals; |
||||
|
||||
public DefineList(int numValues) { |
||||
if (numValues < 0 || numValues > MAX_DEFINES) { |
||||
throw new IllegalArgumentException("numValues must be between 0 and 64"); |
||||
} |
||||
vals = new int[numValues]; |
||||
} |
||||
|
||||
private DefineList(DefineList original) { |
||||
this.hash = original.hash; |
||||
this.vals = new int[original.vals.length]; |
||||
System.arraycopy(original.vals, 0, vals, 0, vals.length); |
||||
} |
||||
|
||||
public void set(int id, int val) { |
||||
assert 0 <= id && id < 64; |
||||
if (val != 0) { |
||||
hash |= (1L << id); |
||||
} else { |
||||
hash &= ~(1L << id); |
||||
} |
||||
vals[id] = val; |
||||
} |
||||
|
||||
public void set(int id, float val) { |
||||
set(id, Float.floatToIntBits(val)); |
||||
} |
||||
|
||||
public void set(int id, boolean val) { |
||||
set(id, val ? 1 : 0); |
||||
} |
||||
|
||||
public void set(int id, VarType type, Object value) { |
||||
if (value == null) { |
||||
set(id, 0); |
||||
return; |
||||
} |
||||
|
||||
switch (type) { |
||||
case Int: |
||||
set(id, (Integer) value); |
||||
break; |
||||
case Float: |
||||
set(id, (Float) value); |
||||
break; |
||||
case Boolean: |
||||
set(id, ((Boolean) value)); |
||||
break; |
||||
default: |
||||
set(id, 1); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
public void setAll(DefineList other) { |
||||
for (int i = 0; i < other.vals.length; i++) { |
||||
if (other.vals[i] != 0) { |
||||
vals[i] = other.vals[i]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void clear() { |
||||
hash = 0; |
||||
Arrays.fill(vals, 0); |
||||
} |
||||
|
||||
public boolean getBoolean(int id) { |
||||
return vals[id] != 0; |
||||
} |
||||
|
||||
public float getFloat(int id) { |
||||
return Float.intBitsToFloat(vals[id]); |
||||
} |
||||
|
||||
public int getInt(int id) { |
||||
return vals[id]; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return (int)((hash >> 32) ^ hash); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object other) { |
||||
DefineList o = (DefineList) other; |
||||
if (hash == o.hash) { |
||||
for (int i = 0; i < vals.length; i++) { |
||||
if (vals[i] != o.vals[i]) return false; |
||||
} |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public DefineList deepClone() { |
||||
return new DefineList(this); |
||||
} |
||||
|
||||
public void generateSource(StringBuilder sb, List<String> defineNames, List<VarType> defineTypes) { |
||||
for (int i = 0; i < vals.length; i++) { |
||||
if (vals[i] != 0) { |
||||
String defineName = defineNames.get(i); |
||||
|
||||
sb.append("#define "); |
||||
sb.append(defineName); |
||||
sb.append(" "); |
||||
|
||||
if (defineTypes != null && defineTypes.get(i) == VarType.Float) { |
||||
float val = Float.intBitsToFloat(vals[i]); |
||||
if (Float.isInfinite(val) || Float.isNaN(val)) { |
||||
throw new IllegalArgumentException( |
||||
"GLSL does not support NaN " |
||||
+ "or Infinite float literals"); |
||||
} |
||||
sb.append(val); |
||||
} else { |
||||
sb.append(vals[i]); |
||||
} |
||||
|
||||
sb.append("\n"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public String generateSource(List<String> defineNames, List<VarType> defineTypes) { |
||||
StringBuilder sb = new StringBuilder(); |
||||
generateSource(sb, defineNames, defineTypes); |
||||
return sb.toString(); |
||||
} |
||||
} |
@ -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 @@ |
||||
version.properties |
@ -1,12 +0,0 @@ |
||||
# THIS IS AN AUTO-GENERATED FILE.. |
||||
# DO NOT MODIFY! |
||||
build.date=2016-03-25 |
||||
git.revision=0 |
||||
git.branch=unknown |
||||
git.hash= |
||||
git.hash.short= |
||||
git.tag= |
||||
name.full=jMonkeyEngine 3.1.0-UNKNOWN |
||||
version.full=3.1.0-UNKNOWN |
||||
version.number=3.1.0 |
||||
version.tag=SNAPSHOT |
@ -0,0 +1,52 @@ |
||||
/* |
||||
* 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.asset; |
||||
|
||||
import com.jme3.asset.plugins.ClasspathLocator; |
||||
import com.jme3.shader.plugins.GLSLLoader; |
||||
import com.jme3.system.JmeSystem; |
||||
import com.jme3.system.MockJmeSystemDelegate; |
||||
import org.junit.Test; |
||||
|
||||
public class LoadShaderSourceTest { |
||||
|
||||
@Test |
||||
public void testLoadShaderSource() { |
||||
JmeSystem.setSystemDelegate(new MockJmeSystemDelegate()); |
||||
AssetManager assetManager = new DesktopAssetManager(); |
||||
assetManager.registerLocator(null, ClasspathLocator.class); |
||||
assetManager.registerLoader(GLSLLoader.class, "frag"); |
||||
String showNormals = (String) assetManager.loadAsset("Common/MatDefs/Misc/ShowNormals.frag"); |
||||
System.out.println(showNormals); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,538 @@ |
||||
/* |
||||
* 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.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.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 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 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(); |
||||
geometry.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(); |
||||
geometry.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(); |
||||
geometry.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(); |
||||
geometry.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 LightList lightList = new LightList(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); |
||||
} |
||||
geometry.updateGeometricState(); |
||||
} |
||||
|
||||
private void reset() { |
||||
evaluated = false; |
||||
usedShader = null; |
||||
Arrays.fill(usedTextures, null); |
||||
} |
||||
|
||||
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) { |
||||
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,342 @@ |
||||
/* |
||||
* 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.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("Default", 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("Default", 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("Default", 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,78 @@ |
||||
/* |
||||
* Copyright (c) 2009-2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.system; |
||||
|
||||
import com.jme3.audio.AudioRenderer; |
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.net.URL; |
||||
import java.nio.ByteBuffer; |
||||
|
||||
public class MockJmeSystemDelegate extends JmeSystemDelegate { |
||||
|
||||
@Override |
||||
public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException { |
||||
} |
||||
|
||||
@Override |
||||
public void showErrorDialog(String message) { |
||||
} |
||||
|
||||
@Override |
||||
public boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry) { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public URL getPlatformAssetConfigURL() { |
||||
return Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/General.cfg"); |
||||
} |
||||
|
||||
@Override |
||||
public JmeContext newContext(AppSettings settings, JmeContext.Type contextType) { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public AudioRenderer newAudioRenderer(AppSettings settings) { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public void initialize(AppSettings settings) { |
||||
} |
||||
|
||||
@Override |
||||
public void showSoftKeyboard(boolean show) { |
||||
} |
||||
|
||||
} |
@ -0,0 +1,55 @@ |
||||
/* |
||||
* Copyright (c) 2009-2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.system; |
||||
|
||||
import com.jme3.asset.AssetConfig; |
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.asset.DesktopAssetManager; |
||||
import com.jme3.renderer.RenderManager; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
public class TestUtil { |
||||
|
||||
static { |
||||
JmeSystem.setSystemDelegate(new MockJmeSystemDelegate()); |
||||
} |
||||
|
||||
public static AssetManager createAssetManager() { |
||||
Logger.getLogger(AssetConfig.class.getName()).setLevel(Level.OFF); |
||||
return new DesktopAssetManager(true); |
||||
} |
||||
|
||||
public static RenderManager createRenderManager() { |
||||
return new RenderManager(new NullRenderer()); |
||||
} |
||||
} |
@ -0,0 +1,213 @@ |
||||
/* |
||||
* Copyright (c) 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 jme3test.app; |
||||
|
||||
import java.lang.reflect.*; |
||||
import java.util.*; |
||||
|
||||
import com.jme3.light.*; |
||||
import com.jme3.material.*; |
||||
import com.jme3.math.*; |
||||
import com.jme3.scene.*; |
||||
import com.jme3.scene.control.*; |
||||
import com.jme3.scene.shape.*; |
||||
import com.jme3.util.clone.*; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public class TestCloneSpatial { |
||||
|
||||
public static void main( String... args ) throws Exception { |
||||
|
||||
// Setup a test node with some children, controls, etc.
|
||||
Node root = new Node("rootNode"); |
||||
|
||||
// A root light
|
||||
DirectionalLight rootLight = new DirectionalLight(); |
||||
root.addLight(rootLight); |
||||
|
||||
Box sharedBox = new Box(1, 1, 1); |
||||
Geometry geom1 = new Geometry("box1", sharedBox); |
||||
Material sharedMaterial = new Material(); // not a valid material, just for testing
|
||||
geom1.setMaterial(sharedMaterial); |
||||
|
||||
Geometry geom2 = new Geometry("box2", sharedBox); |
||||
geom2.setMaterial(sharedMaterial); |
||||
|
||||
root.attachChild(geom1); |
||||
root.attachChild(geom2); |
||||
|
||||
// Add some controls
|
||||
geom1.addControl(new BillboardControl()); |
||||
geom2.addControl(new BillboardControl()); |
||||
|
||||
// A light that will only affect the children and be controlled
|
||||
// by one child
|
||||
PointLight childLight = new PointLight(); |
||||
geom1.addLight(childLight); |
||||
geom2.addLight(childLight); |
||||
|
||||
geom1.addControl(new LightControl(childLight)); |
||||
|
||||
// Set some shared user data also
|
||||
Vector3f sharedUserData = new Vector3f(1, 2, 3); |
||||
geom1.setUserData("shared", sharedUserData); |
||||
geom2.setUserData("shared", sharedUserData); |
||||
|
||||
dump("", root); |
||||
|
||||
System.out.println("-------- cloning spatial --------------"); |
||||
Node clone = root.clone(true); |
||||
dump("", clone); |
||||
|
||||
System.out.println("-------- cloning spatial without cloning material --------------"); |
||||
clone = root.clone(false); |
||||
dump("", clone); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Debug dump to check structure and identity |
||||
*/ |
||||
public static void dump( String indent, Spatial s ) { |
||||
if( s instanceof Node ) { |
||||
dump(indent, (Node)s); |
||||
} else if( s instanceof Geometry ) { |
||||
dump(indent, (Geometry)s); |
||||
} |
||||
} |
||||
|
||||
public static void dump( String indent, Node n ) { |
||||
System.out.println(indent + objectToString(n)); |
||||
dumpSpatialProperties(indent + " ", n); |
||||
if( !n.getChildren().isEmpty() ) { |
||||
System.out.println(indent + " children:"); |
||||
for( Spatial s : n.getChildren() ) { |
||||
dump(indent + " ", s); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static void dump( String indent, Geometry g ) { |
||||
System.out.println(indent + objectToString(g)); |
||||
//System.out.println(indent + " mesh:" + objectToString(g.getMesh()));
|
||||
//System.out.println(indent + " material:" + objectToString(g.getMaterial()));
|
||||
dumpSpatialProperties(indent + " ", g); |
||||
} |
||||
|
||||
public static void dump( String indent, Control ctl ) { |
||||
System.out.println(indent + objectToString(ctl)); |
||||
if( ctl instanceof AbstractControl ) { |
||||
System.out.println(indent + " spatial:" + objectToString(((AbstractControl)ctl).getSpatial())); |
||||
} |
||||
} |
||||
|
||||
private static void dumpSpatialProperties( String indent, Spatial s ) { |
||||
dumpProperties(indent, s, "children"); |
||||
|
||||
if( !s.getUserDataKeys().isEmpty() ) { |
||||
System.out.println(indent + "userData:"); |
||||
for( String key : s.getUserDataKeys() ) { |
||||
System.out.println(indent + " " + key + ":" + objectToString(s.getUserData(key))); |
||||
} |
||||
} |
||||
|
||||
if( s.getNumControls() > 0 ) { |
||||
System.out.println(indent + "controls:"); |
||||
for( int i = 0; i < s.getNumControls(); i++ ) { |
||||
Control ctl = s.getControl(i); |
||||
//dump(indent + " ", ctl);
|
||||
dumpObject(indent + " ", ctl); |
||||
} |
||||
} |
||||
|
||||
LightList lights = s.getLocalLightList(); |
||||
if( lights.size() > 0 ) { |
||||
System.out.println(indent + "lights:"); |
||||
for( Light l : lights ) { |
||||
dumpObject(indent + " ", l); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private static void dumpObject( String indent, Object o ) { |
||||
System.out.println(indent + objectToString(o)); |
||||
dumpProperties(indent + " ", o); |
||||
} |
||||
|
||||
private static void dumpProperties( String indent, Object o, String... skip ) { |
||||
if( o == null ) { |
||||
return; |
||||
} |
||||
Set<String> skipSet = new HashSet<>(Arrays.asList(skip)); |
||||
for( Method m : o.getClass().getMethods() ) { |
||||
if( m.getParameterTypes().length > 0 ) { |
||||
continue; |
||||
} |
||||
String name = m.getName(); |
||||
if( "getClass".equals(name) ) { |
||||
continue; |
||||
} |
||||
if( !name.startsWith("get") ) { |
||||
continue; |
||||
} |
||||
Class type = m.getReturnType(); |
||||
if( type.isPrimitive() || type.isEnum() ) { |
||||
continue; |
||||
} |
||||
name = name.substring(3); |
||||
if( skipSet.contains(name.toLowerCase()) ) { |
||||
continue; |
||||
} |
||||
try { |
||||
Object value = m.invoke(o); |
||||
System.out.println(indent + name + ":" + objectToString(value)); |
||||
} catch( Exception e ) { |
||||
throw new RuntimeException("Error with method:" + m, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private static String objectToString( Object o ) { |
||||
if( o == null ) { |
||||
return null; |
||||
} |
||||
String s = o + "@" + System.identityHashCode(o); |
||||
s = s.replaceAll("\\r?\\n", ""); |
||||
return s; |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue