commit
a8fca2bcf6
@ -0,0 +1,774 @@ |
||||
/* |
||||
* 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.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(){ |
||||
initStateManager(); |
||||
} |
||||
|
||||
/** |
||||
* 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){ |
||||
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,81 @@ |
||||
/* |
||||
* 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 com.jme3.util.clone; |
||||
|
||||
|
||||
/** |
||||
* Provides custom cloning for a particular object type. Once |
||||
* registered with the Cloner, this function object will be called twice |
||||
* for any cloned object that matches the class for which it was registered. |
||||
* It will first call cloneObject() to shallow clone the object and then call |
||||
* cloneFields() to deep clone the object's values. |
||||
* |
||||
* <p>This two step process is important because this is what allows |
||||
* circular references in the cloned object graph.</p> |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public interface CloneFunction<T> { |
||||
|
||||
/** |
||||
* Performs a shallow clone of the specified object. This is similar |
||||
* to the JmeCloneable.clone() method in semantics and is the first part |
||||
* of a two part cloning process. Once the shallow clone is created, it |
||||
* is cached and CloneFunction.cloneFields() is called. In this way, |
||||
* the CloneFunction interface can completely take over the JmeCloneable |
||||
* style cloning for an object that doesn't otherwise implement that interface. |
||||
* |
||||
* @param cloner The cloner performing the cloning operation. |
||||
* @param original The original object that needs to be cloned. |
||||
*/ |
||||
public T cloneObject( Cloner cloner, T original ); |
||||
|
||||
|
||||
/** |
||||
* Performs a deep clone of the specified clone's fields. This is similar |
||||
* to the JmeCloneable.cloneFields() method in semantics and is the second part |
||||
* of a two part cloning process. Once the shallow clone is created, it |
||||
* is cached and CloneFunction.cloneFields() is called. In this way, |
||||
* the CloneFunction interface can completely take over the JmeCloneable |
||||
* style cloning for an object that doesn't otherwise implement that interface. |
||||
* |
||||
* @param cloner The cloner performing the cloning operation. |
||||
* @param clone The clone previously returned from cloneObject(). |
||||
* @param original The original object that was cloned. This is provided for |
||||
* the very special case where field cloning needs to refer to |
||||
* the original object. Mostly the necessary fields should already |
||||
* be on the clone. |
||||
*/ |
||||
public void cloneFields( Cloner cloner, T clone, T original ); |
||||
|
||||
} |
@ -0,0 +1,412 @@ |
||||
/* |
||||
* 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 com.jme3.util.clone; |
||||
|
||||
import java.lang.reflect.Array; |
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.lang.reflect.Method; |
||||
import java.util.HashMap; |
||||
import java.util.IdentityHashMap; |
||||
import java.util.logging.Logger; |
||||
import java.util.logging.Level; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
/** |
||||
* A deep clone utility that provides similar object-graph-preserving |
||||
* qualities to typical serialization schemes. An internal registry |
||||
* of cloned objects is kept to be used by other objects in the deep |
||||
* clone process that implement JmeCloneable. |
||||
* |
||||
* <p>By default, objects that do not implement JmeCloneable will |
||||
* be treated like normal Java Cloneable objects. If the object does |
||||
* not implement the JmeCloneable or the regular JDK Cloneable interfaces |
||||
* AND has no special handling defined then an IllegalArgumentException |
||||
* will be thrown.</p> |
||||
* |
||||
* <p>Enhanced object cloning is done in a two step process. First, |
||||
* the object is cloned using the normal Java clone() method and stored |
||||
* in the clone registry. After that, if it implements JmeCloneable then |
||||
* its cloneFields() method is called to deep clone any of the fields. |
||||
* This two step process has a few benefits. First, it means that objects |
||||
* can easily have a regular shallow clone implementation just like any |
||||
* normal Java objects. Second, the deep cloning of fields happens after |
||||
* creation wich means that the clone is available to future field cloning |
||||
* to resolve circular references.</p> |
||||
* |
||||
* <p>Similar to Java serialization, the handling of specific object |
||||
* types can be customized. This allows certain objects to be cloned gracefully |
||||
* even if they aren't normally Cloneable. This can also be used as a |
||||
* sort of filter to keep certain types of objects from being cloned. |
||||
* (For example, adding the IdentityCloneFunction for Mesh.class would cause |
||||
* all mesh instances to be shared with the original object graph.)</p> |
||||
* |
||||
* <p>By default, the Cloner registers serveral default clone functions |
||||
* as follows:</p> |
||||
* <ul> |
||||
* <li>java.util.ArrayList: ListCloneFunction |
||||
* <li>java.util.LinkedList: ListCloneFunction |
||||
* <li>java.util.concurrent.CopyOnWriteArrayList: ListCloneFunction |
||||
* <li>java.util.Vector: ListCloneFunction |
||||
* <li>java.util.Stack: ListCloneFunction |
||||
* <li>com.jme3.util.SafeArrayList: ListCloneFunction |
||||
* </ul> |
||||
* |
||||
* <p>Usage:</p> |
||||
* <pre> |
||||
* // Example 1: using an instantiated, reusable cloner.
|
||||
* Cloner cloner = new Cloner(); |
||||
* Foo fooClone = cloner.clone(foo); |
||||
* cloner.clearIndex(); // prepare it for reuse
|
||||
* Foo fooClone2 = cloner.clone(foo); |
||||
* |
||||
* // Example 2: using the utility method that self-instantiates a temporary cloner.
|
||||
* Foo fooClone = Cloner.deepClone(foo); |
||||
* |
||||
* </pre> |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public class Cloner { |
||||
|
||||
static Logger log = Logger.getLogger(Cloner.class.getName()); |
||||
|
||||
/** |
||||
* Keeps track of the objects that have been cloned so far. |
||||
*/ |
||||
private IdentityHashMap<Object, Object> index = new IdentityHashMap<Object, Object>(); |
||||
|
||||
/** |
||||
* Custom functions for cloning objects. |
||||
*/ |
||||
private Map<Class, CloneFunction> functions = new HashMap<Class, CloneFunction>(); |
||||
|
||||
/** |
||||
* Cache the clone methods once for all cloners. |
||||
*/ |
||||
private static final Map<Class, Method> methodCache = new ConcurrentHashMap<>(); |
||||
|
||||
/** |
||||
* Creates a new cloner with only default clone functions and an empty |
||||
* object index. |
||||
*/ |
||||
public Cloner() { |
||||
// Register some standard types
|
||||
ListCloneFunction listFunction = new ListCloneFunction(); |
||||
functions.put(java.util.ArrayList.class, listFunction); |
||||
functions.put(java.util.LinkedList.class, listFunction); |
||||
functions.put(java.util.concurrent.CopyOnWriteArrayList.class, listFunction); |
||||
functions.put(java.util.Vector.class, listFunction); |
||||
functions.put(java.util.Stack.class, listFunction); |
||||
functions.put(com.jme3.util.SafeArrayList.class, listFunction); |
||||
} |
||||
|
||||
/** |
||||
* Convenience utility function that creates a new Cloner, uses it to |
||||
* deep clone the object, and then returns the result. |
||||
*/ |
||||
public static <T> T deepClone( T object ) { |
||||
return new Cloner().clone(object); |
||||
} |
||||
|
||||
/** |
||||
* Deeps clones the specified object, reusing previous clones when possible. |
||||
* |
||||
* <p>Object cloning priority works as follows:</p> |
||||
* <ul> |
||||
* <li>If the object has already been cloned then its clone is returned. |
||||
* <li>If there is a custom CloneFunction then it is called to clone the object. |
||||
* <li>If the object implements Cloneable then its clone() method is called, arrays are |
||||
* deep cloned with entries passing through clone(). |
||||
* <li>If the object implements JmeCloneable then its cloneFields() method is called on the |
||||
* clone. |
||||
* <li>Else an IllegalArgumentException is thrown. |
||||
* </ul> |
||||
* |
||||
* Note: objects returned by this method may not have yet had their cloneField() |
||||
* method called. |
||||
*/ |
||||
public <T> T clone( T object ) { |
||||
return clone(object, true); |
||||
} |
||||
|
||||
/** |
||||
* Internal method to work around a Java generics typing issue by |
||||
* isolating the 'bad' case into a method with suppressed warnings. |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
private <T> Class<T> objectClass( T object ) { |
||||
// This should be 100% allowed without a cast but Java generics
|
||||
// is not that smart sometimes.
|
||||
// Wrapping it in a method at least isolates the warning suppression
|
||||
return (Class<T>)object.getClass(); |
||||
} |
||||
|
||||
/** |
||||
* Deeps clones the specified object, reusing previous clones when possible. |
||||
* |
||||
* <p>Object cloning priority works as follows:</p> |
||||
* <ul> |
||||
* <li>If the object has already been cloned then its clone is returned. |
||||
* <li>If useFunctions is true and there is a custom CloneFunction then it is |
||||
* called to clone the object. |
||||
* <li>If the object implements Cloneable then its clone() method is called, arrays are |
||||
* deep cloned with entries passing through clone(). |
||||
* <li>If the object implements JmeCloneable then its cloneFields() method is called on the |
||||
* clone. |
||||
* <li>Else an IllegalArgumentException is thrown. |
||||
* </ul> |
||||
* |
||||
* <p>The abililty to selectively use clone functions is useful when |
||||
* being called from a clone function.</p> |
||||
* |
||||
* Note: objects returned by this method may not have yet had their cloneField() |
||||
* method called. |
||||
*/ |
||||
public <T> T clone( T object, boolean useFunctions ) { |
||||
|
||||
if( object == null ) { |
||||
return null; |
||||
} |
||||
|
||||
if( log.isLoggable(Level.FINER) ) { |
||||
log.finer("cloning:" + object.getClass() + "@" + System.identityHashCode(object)); |
||||
} |
||||
|
||||
Class<T> type = objectClass(object); |
||||
|
||||
// Check the index to see if we already have it
|
||||
Object clone = index.get(object); |
||||
if( clone != null ) { |
||||
if( log.isLoggable(Level.FINER) ) { |
||||
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object) |
||||
+ " as cached:" + clone.getClass() + "@" + System.identityHashCode(clone)); |
||||
} |
||||
return type.cast(clone); |
||||
} |
||||
|
||||
// See if there is a custom function... that trumps everything.
|
||||
CloneFunction<T> f = getCloneFunction(type); |
||||
if( f != null ) { |
||||
T result = f.cloneObject(this, object); |
||||
|
||||
// Store the object in the identity map so that any circular references
|
||||
// are resolvable.
|
||||
index.put(object, result); |
||||
|
||||
// Now call the function again to deep clone the fields
|
||||
f.cloneFields(this, result, object); |
||||
|
||||
if( log.isLoggable(Level.FINER) ) { |
||||
if( result == null ) { |
||||
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object) |
||||
+ " as transformed:null"); |
||||
} else { |
||||
log.finer("clone:" + object.getClass() + "@" + System.identityHashCode(object) |
||||
+ " as transformed:" + result.getClass() + "@" + System.identityHashCode(result)); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
if( object.getClass().isArray() ) { |
||||
// Perform an array clone
|
||||
clone = arrayClone(object); |
||||
|
||||
// Array clone already indexes the clone
|
||||
} else if( object instanceof JmeCloneable ) { |
||||
// Use the two-step cloning semantics
|
||||
clone = ((JmeCloneable)object).jmeClone(); |
||||
|
||||
// Store the object in the identity map so that any circular references
|
||||
// are resolvable
|
||||
index.put(object, clone); |
||||
|
||||
((JmeCloneable)clone).cloneFields(this, object); |
||||
} else if( object instanceof Cloneable ) { |
||||
|
||||
// Perform a regular Java shallow clone
|
||||
try { |
||||
clone = javaClone(object); |
||||
} catch( CloneNotSupportedException e ) { |
||||
throw new IllegalArgumentException("Object is not cloneable, type:" + type, e); |
||||
} |
||||
|
||||
// Store the object in the identity map so that any circular references
|
||||
// are resolvable
|
||||
index.put(object, clone); |
||||
} else { |
||||
throw new IllegalArgumentException("Object is not cloneable, type:" + type); |
||||
} |
||||
|
||||
if( log.isLoggable(Level.FINER) ) { |
||||
log.finer("cloned:" + object.getClass() + "@" + System.identityHashCode(object) |
||||
+ " as " + clone.getClass() + "@" + System.identityHashCode(clone)); |
||||
} |
||||
return type.cast(clone); |
||||
} |
||||
|
||||
/** |
||||
* Sets a custom CloneFunction for implementations of the specified Java type. Some |
||||
* inheritance checks are made but no disambiguation is performed. |
||||
* <p>Note: in the general case, it is better to register against specific classes and |
||||
* not super-classes or super-interfaces unless you know specifically that they are cloneable.</p> |
||||
* <p>By default ListCloneFunction is registered for ArrayList, LinkedList, CopyOnWriteArrayList, |
||||
* Vector, Stack, and JME's SafeArrayList.</p> |
||||
*/ |
||||
public <T> void setCloneFunction( Class<T> type, CloneFunction<T> function ) { |
||||
if( function == null ) { |
||||
functions.remove(type); |
||||
} else { |
||||
functions.put(type, function); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns a previously registered clone function for the specified type or null |
||||
* if there is no custom clone function for the type. |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public <T> CloneFunction<T> getCloneFunction( Class<T> type ) { |
||||
CloneFunction<T> result = (CloneFunction<T>)functions.get(type); |
||||
if( result == null ) { |
||||
// Do a more exhaustive search
|
||||
for( Map.Entry<Class, CloneFunction> e : functions.entrySet() ) { |
||||
if( e.getKey().isAssignableFrom(type) ) { |
||||
result = e.getValue(); |
||||
break; |
||||
} |
||||
} |
||||
if( result != null ) { |
||||
// Cache it for later
|
||||
functions.put(type, result); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Forces an object to be added to the indexing cache such that attempts |
||||
* to clone the 'original' will always result in the 'clone' being returned. |
||||
* This can be used to stub out specific values from being cloned or to |
||||
* force global shared instances to be used even if the object is cloneable |
||||
* normally. |
||||
*/ |
||||
public <T> void setClonedValue( T original, T clone ) { |
||||
index.put(original, clone); |
||||
} |
||||
|
||||
/** |
||||
* Returns true if the specified object has already been cloned |
||||
* by this cloner during this session. Cloned objects are cached |
||||
* for later use and it's sometimes convenient to know if some |
||||
* objects have already been cloned. |
||||
*/ |
||||
public boolean isCloned( Object o ) { |
||||
return index.containsKey(o); |
||||
} |
||||
|
||||
/** |
||||
* Clears the object index allowing the cloner to be reused for a brand new |
||||
* cloning operation. |
||||
*/ |
||||
public void clearIndex() { |
||||
index.clear(); |
||||
} |
||||
|
||||
/** |
||||
* Performs a raw shallow Java clone using reflection. This call does NOT |
||||
* check against the clone index and so will return new objects every time |
||||
* it is called. That's because these are shallow clones and have not (and may |
||||
* not ever, depending on the caller) get resolved. |
||||
* |
||||
* <p>This method is provided as a convenient way for CloneFunctions to call |
||||
* clone() and objects without necessarily knowing their real type.</p> |
||||
*/ |
||||
public <T> T javaClone( T object ) throws CloneNotSupportedException { |
||||
Method m = methodCache.get(object.getClass()); |
||||
if( m == null ) { |
||||
try { |
||||
// Lookup the method and cache it
|
||||
m = object.getClass().getMethod("clone"); |
||||
} catch( NoSuchMethodException e ) { |
||||
throw new CloneNotSupportedException("No public clone method found for:" + object.getClass()); |
||||
} |
||||
methodCache.put(object.getClass(), m); |
||||
|
||||
// Note: yes we might cache the method twice... but so what?
|
||||
} |
||||
|
||||
try { |
||||
Class<? extends T> type = objectClass(object); |
||||
return type.cast(m.invoke(object)); |
||||
} catch( IllegalAccessException | InvocationTargetException e ) { |
||||
throw new RuntimeException("Error cloning object of type:" + object.getClass(), e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Clones a primitive array by coping it and clones an object |
||||
* array by coping it and then running each of its values through |
||||
* Cloner.clone(). |
||||
*/ |
||||
protected <T> T arrayClone( T object ) { |
||||
|
||||
// Java doesn't support the cloning of arrays through reflection unless
|
||||
// you open access to Object's protected clone array... which requires
|
||||
// elevated privileges. So we will do a work-around that is slightly less
|
||||
// elegant.
|
||||
// This should be 100% allowed without a case but Java generics
|
||||
// is not that smart
|
||||
Class<T> type = objectClass(object); |
||||
Class elementType = type.getComponentType(); |
||||
int size = Array.getLength(object); |
||||
Object clone = Array.newInstance(elementType, size); |
||||
|
||||
// Store the clone for later lookups
|
||||
index.put(object, clone); |
||||
|
||||
if( elementType.isPrimitive() ) { |
||||
// Then our job is a bit easier
|
||||
System.arraycopy(object, 0, clone, 0, size); |
||||
} else { |
||||
// Else it's an object array so we'll clone it and its children
|
||||
for( int i = 0; i < size; i++ ) { |
||||
Object element = clone(Array.get(object, i)); |
||||
Array.set(clone, i, element); |
||||
} |
||||
} |
||||
|
||||
return type.cast(clone); |
||||
} |
||||
} |
@ -0,0 +1,58 @@ |
||||
/* |
||||
* 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 com.jme3.util.clone; |
||||
|
||||
|
||||
/** |
||||
* A CloneFunction implementation that simply returns the |
||||
* the passed object without cloning it. This is useful for |
||||
* forcing some object types (like Meshes) to be shared between |
||||
* the original and cloned object graph. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public class IdentityCloneFunction<T> implements CloneFunction<T> { |
||||
|
||||
/** |
||||
* Returns the object directly. |
||||
*/ |
||||
public T cloneObject( Cloner cloner, T object ) { |
||||
return object; |
||||
} |
||||
|
||||
/** |
||||
* Does nothing. |
||||
*/ |
||||
public void cloneFields( Cloner cloner, T clone, T object ) { |
||||
} |
||||
} |
@ -0,0 +1,99 @@ |
||||
/* |
||||
* 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 com.jme3.util.clone; |
||||
|
||||
|
||||
/** |
||||
* Indicates an object that wishes to more actively participate in the |
||||
* two-part deep copying process provided by the Cloner. Objects implementing |
||||
* this interface can access the already cloned object graph to resolve |
||||
* their local dependencies in a way that will be equivalent to the |
||||
* original object graph. In other words, if two objects in the graph |
||||
* share the same target reference then the cloned version will share |
||||
* the cloned reference. |
||||
* |
||||
* <p>For example, if an object wishes to deep clone one of its fields |
||||
* then it will call cloner.clone(object) instead of object.clone(). |
||||
* The cloner will keep track of any clones already created for 'object' |
||||
* and return that instead of a new clone.</p> |
||||
* |
||||
* <p>Cloning of a JmeCloneable object is done in two parts. First, |
||||
* the standard Java clone() method is called to create a shallow clone |
||||
* of the object. Second, the cloner wil lcall the cloneFields() method |
||||
* to let the object deep clone any of its fields that should be cloned.</p> |
||||
* |
||||
* <p>This two part process is necessary to facilitate circular references. |
||||
* When an object calls cloner.clone() during its cloneFields() method, it |
||||
* may get only a shallow copy that will be filled in later.</p> |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public interface JmeCloneable extends Cloneable { |
||||
|
||||
/** |
||||
* Performs a regular shallow clone of the object. Some fields |
||||
* may also be cloned but generally only if they will never be |
||||
* shared with other objects. (For example, local Vector3fs and so on.) |
||||
* |
||||
* <p>This method is separate from the regular clone() method |
||||
* so that objects might still maintain their own regular java clone() |
||||
* semantics (perhaps even using Cloner for those methods). However, |
||||
* because Java's clone() has specific features in the sense of Object's |
||||
* clone() implementation, it's usually best to have some path for |
||||
* subclasses to bypass the public clone() method that might be cloning |
||||
* fields and instead get at the superclass protected clone() methods. |
||||
* For example, through super.jmeClone() or another protected clone |
||||
* method that some base class eventually calls super.clone() in.</p> |
||||
*/ |
||||
public Object jmeClone(); |
||||
|
||||
/** |
||||
* Implemented to perform deep cloning for this object, resolving |
||||
* local cloned references using the specified cloner. The object |
||||
* can call cloner.clone(fieldValue) to deep clone any of its fields. |
||||
* |
||||
* <p>Note: during normal clone operations the original object |
||||
* will not be needed as the clone has already had all of the fields |
||||
* shallow copied.</p> |
||||
* |
||||
* @param cloner The cloner that is performing the cloning operation. The |
||||
* cloneFields method can call back into the cloner to make |
||||
* clones if its subordinate fields. |
||||
* @param original The original object from which this object was cloned. |
||||
* This is provided for the very rare case that this object needs |
||||
* to refer to its original for some reason. In general, all of |
||||
* the relevant values should have been transferred during the |
||||
* shallow clone and this object need merely clone what it wants. |
||||
*/ |
||||
public void cloneFields( Cloner cloner, Object original ); |
||||
} |
@ -0,0 +1,70 @@ |
||||
/* |
||||
* 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 com.jme3.util.clone; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* A CloneFunction implementation that deep clones a list by |
||||
* creating a new list and cloning its values using the cloner. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public class ListCloneFunction<T extends List> implements CloneFunction<T> { |
||||
|
||||
public T cloneObject( Cloner cloner, T object ) { |
||||
try { |
||||
T clone = cloner.javaClone(object); |
||||
return clone; |
||||
} catch( CloneNotSupportedException e ) { |
||||
throw new IllegalArgumentException("Clone not supported for type:" + object.getClass(), e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Clones the elements of the list. |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public void cloneFields( Cloner cloner, T clone, T object ) { |
||||
for( int i = 0; i < clone.size(); i++ ) { |
||||
// Need to clone the clones... because T might
|
||||
// have done something special in its clone method that
|
||||
// we will have to adhere to. For example, clone may have nulled
|
||||
// out some things or whatever that might be implementation specific.
|
||||
// At any rate, if it's a proper clone then the clone will already
|
||||
// have shallow versions of the elements that we can clone.
|
||||
clone.set(i, cloner.clone(clone.get(i))); |
||||
} |
||||
} |
||||
} |
||||
|
@ -1,80 +0,0 @@ |
||||
#import "Common/ShaderLib/Shadows15.glsllib" |
||||
|
||||
out vec4 outFragColor; |
||||
|
||||
#if defined(PSSM) || defined(FADE) |
||||
in float shadowPosition; |
||||
#endif |
||||
|
||||
in vec4 projCoord0; |
||||
in vec4 projCoord1; |
||||
in vec4 projCoord2; |
||||
in vec4 projCoord3; |
||||
|
||||
#ifdef POINTLIGHT |
||||
in vec4 projCoord4; |
||||
in vec4 projCoord5; |
||||
in vec4 worldPos; |
||||
uniform vec3 m_LightPos; |
||||
#else |
||||
#ifndef PSSM |
||||
in float lightDot; |
||||
#endif |
||||
#endif |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
uniform sampler2D m_ColorMap; |
||||
#else |
||||
uniform sampler2D m_DiffuseMap; |
||||
#endif |
||||
uniform float m_AlphaDiscardThreshold; |
||||
varying vec2 texCoord; |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
uniform vec2 m_FadeInfo; |
||||
#endif |
||||
|
||||
void main(){ |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
float alpha = texture2D(m_ColorMap,texCoord).a; |
||||
#else |
||||
float alpha = texture2D(m_DiffuseMap,texCoord).a; |
||||
#endif |
||||
|
||||
if(alpha < m_AlphaDiscardThreshold){ |
||||
discard; |
||||
} |
||||
#endif |
||||
|
||||
float shadow = 1.0; |
||||
#ifdef POINTLIGHT |
||||
shadow = getPointLightShadows(worldPos, m_LightPos, |
||||
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5, |
||||
projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5); |
||||
#else |
||||
#ifdef PSSM |
||||
shadow = getDirectionalLightShadows(m_Splits, shadowPosition, |
||||
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3, |
||||
projCoord0, projCoord1, projCoord2, projCoord3); |
||||
#else |
||||
//spotlight |
||||
if(lightDot < 0){ |
||||
outFragColor = vec4(1.0); |
||||
return; |
||||
} |
||||
shadow = getSpotLightShadows(m_ShadowMap0,projCoord0); |
||||
#endif |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); |
||||
#endif |
||||
|
||||
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); |
||||
outFragColor = vec4(shadow, shadow, shadow, 1.0); |
||||
} |
||||
|
@ -1,82 +0,0 @@ |
||||
#import "Common/ShaderLib/Instancing.glsllib" |
||||
#import "Common/ShaderLib/Skinning.glsllib" |
||||
uniform mat4 m_LightViewProjectionMatrix0; |
||||
uniform mat4 m_LightViewProjectionMatrix1; |
||||
uniform mat4 m_LightViewProjectionMatrix2; |
||||
uniform mat4 m_LightViewProjectionMatrix3; |
||||
|
||||
|
||||
out vec4 projCoord0; |
||||
out vec4 projCoord1; |
||||
out vec4 projCoord2; |
||||
out vec4 projCoord3; |
||||
|
||||
#ifdef POINTLIGHT |
||||
uniform mat4 m_LightViewProjectionMatrix4; |
||||
uniform mat4 m_LightViewProjectionMatrix5; |
||||
out vec4 projCoord4; |
||||
out vec4 projCoord5; |
||||
out vec4 worldPos; |
||||
#else |
||||
#ifndef PSSM |
||||
uniform vec3 m_LightPos; |
||||
uniform vec3 m_LightDir; |
||||
out float lightDot; |
||||
#endif |
||||
#endif |
||||
|
||||
#if defined(PSSM) || defined(FADE) |
||||
out float shadowPosition; |
||||
#endif |
||||
out vec3 lightVec; |
||||
|
||||
out vec2 texCoord; |
||||
|
||||
in vec3 inPosition; |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
in vec2 inTexCoord; |
||||
#endif |
||||
|
||||
const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, |
||||
0.0, 0.5, 0.0, 0.0, |
||||
0.0, 0.0, 0.5, 0.0, |
||||
0.5, 0.5, 0.5, 1.0); |
||||
|
||||
|
||||
void main(){ |
||||
vec4 modelSpacePos = vec4(inPosition, 1.0); |
||||
|
||||
#ifdef NUM_BONES |
||||
Skinning_Compute(modelSpacePos); |
||||
#endif |
||||
gl_Position = TransformWorldViewProjection(modelSpacePos); |
||||
|
||||
#if defined(PSSM) || defined(FADE) |
||||
shadowPosition = gl_Position.z; |
||||
#endif |
||||
|
||||
#ifndef POINTLIGHT |
||||
vec4 worldPos=vec4(0.0); |
||||
#endif |
||||
// get the vertex in world space |
||||
worldPos = TransformWorld(modelSpacePos); |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
texCoord = inTexCoord; |
||||
#endif |
||||
// populate the light view matrices array and convert vertex to light viewProj space |
||||
projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos; |
||||
projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; |
||||
projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos; |
||||
projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos; |
||||
#ifdef POINTLIGHT |
||||
projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos; |
||||
projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos; |
||||
#else |
||||
#ifndef PSSM |
||||
vec3 lightDir = worldPos.xyz - m_LightPos; |
||||
lightDot = dot(m_LightDir,lightDir); |
||||
#endif |
||||
#endif |
||||
} |
@ -1,242 +0,0 @@ |
||||
// Because gpu_shader5 is actually where those |
||||
// gather functions are declared to work on shadowmaps |
||||
#extension GL_ARB_gpu_shader5 : enable |
||||
|
||||
#ifdef HARDWARE_SHADOWS |
||||
#define SHADOWMAP sampler2DShadow |
||||
#define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset) |
||||
#define SHADOWCOMPARE(tex,coord) textureProj(tex, coord) |
||||
#define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z) |
||||
#else |
||||
#define SHADOWMAP sampler2D |
||||
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r) |
||||
#define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r) |
||||
#define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy)) |
||||
#endif |
||||
|
||||
|
||||
#if FILTER_MODE == 0 |
||||
#define GETSHADOW Shadow_Nearest |
||||
#define KERNEL 1.0 |
||||
#elif FILTER_MODE == 1 |
||||
#ifdef HARDWARE_SHADOWS |
||||
#define GETSHADOW Shadow_Nearest |
||||
#else |
||||
#define GETSHADOW Shadow_DoBilinear_2x2 |
||||
#endif |
||||
#define KERNEL 1.0 |
||||
#elif FILTER_MODE == 2 |
||||
#define GETSHADOW Shadow_DoDither_2x2 |
||||
#define KERNEL 1.0 |
||||
#elif FILTER_MODE == 3 |
||||
#define GETSHADOW Shadow_DoPCF |
||||
#define KERNEL 4.0 |
||||
#elif FILTER_MODE == 4 |
||||
#define GETSHADOW Shadow_DoPCFPoisson |
||||
#define KERNEL 4.0 |
||||
#elif FILTER_MODE == 5 |
||||
#define GETSHADOW Shadow_DoPCF |
||||
#define KERNEL 8.0 |
||||
#endif |
||||
|
||||
|
||||
|
||||
uniform SHADOWMAP m_ShadowMap0; |
||||
uniform SHADOWMAP m_ShadowMap1; |
||||
uniform SHADOWMAP m_ShadowMap2; |
||||
uniform SHADOWMAP m_ShadowMap3; |
||||
#ifdef POINTLIGHT |
||||
uniform SHADOWMAP m_ShadowMap4; |
||||
uniform SHADOWMAP m_ShadowMap5; |
||||
#endif |
||||
|
||||
#ifdef PSSM |
||||
uniform vec4 m_Splits; |
||||
#endif |
||||
uniform float m_ShadowIntensity; |
||||
|
||||
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE); |
||||
float shadowBorderScale = 1.0; |
||||
|
||||
float Shadow_BorderCheck(in vec2 coord){ |
||||
// Fastest, "hack" method (uses 4-5 instructions) |
||||
vec4 t = vec4(coord.xy, 0.0, 1.0); |
||||
t = step(t.wwxy, t.xyzz); |
||||
return dot(t,t); |
||||
} |
||||
|
||||
float Shadow_Nearest(in SHADOWMAP tex, in vec4 projCoord){ |
||||
float border = Shadow_BorderCheck(projCoord.xy); |
||||
if (border > 0.0){ |
||||
return 1.0; |
||||
} |
||||
return SHADOWCOMPARE(tex,projCoord); |
||||
} |
||||
|
||||
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ |
||||
float border = Shadow_BorderCheck(projCoord.xy); |
||||
if (border > 0.0) |
||||
return 1.0; |
||||
|
||||
vec2 pixSize = pixSize2 * shadowBorderScale; |
||||
|
||||
float shadow = 0.0; |
||||
ivec2 o = ivec2(mod(floor(gl_FragCoord.xy), 2.0)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, 1.5)+o), projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, 1.5)+o), projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, -0.5)+o), projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, -0.5)+o), projCoord.zw)); |
||||
shadow *= 0.25; |
||||
return shadow; |
||||
} |
||||
|
||||
float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){ |
||||
float border = Shadow_BorderCheck(projCoord.xy); |
||||
if (border > 0.0) |
||||
return 1.0; |
||||
|
||||
#ifdef GL_ARB_gpu_shader5 |
||||
vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0); |
||||
vec4 gather = SHADOWGATHER(tex, coord); |
||||
#else |
||||
vec4 gather = vec4(0.0); |
||||
gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1)); |
||||
gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1)); |
||||
gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0)); |
||||
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0)); |
||||
#endif |
||||
|
||||
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE ); |
||||
vec2 mx = mix( gather.wx, gather.zy, f.x ); |
||||
return mix( mx.x, mx.y, f.y ); |
||||
} |
||||
|
||||
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){ |
||||
|
||||
vec2 pixSize = pixSize2 * shadowBorderScale; |
||||
float shadow = 0.0; |
||||
float border = Shadow_BorderCheck(projCoord.xy); |
||||
if (border > 0.0) |
||||
return 1.0; |
||||
|
||||
float bound = KERNEL * 0.5 - 0.5; |
||||
bound *= PCFEDGE; |
||||
for (float y = -bound; y <= bound; y += PCFEDGE){ |
||||
for (float x = -bound; x <= bound; x += PCFEDGE){ |
||||
vec4 coord = vec4(projCoord.xy + vec2(x,y) * pixSize, projCoord.zw); |
||||
shadow += SHADOWCOMPARE(tex, coord); |
||||
} |
||||
} |
||||
|
||||
shadow = shadow / (KERNEL * KERNEL); |
||||
return shadow; |
||||
} |
||||
|
||||
|
||||
//12 tap poisson disk |
||||
const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016); |
||||
const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201); |
||||
const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521); |
||||
const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819); |
||||
const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957); |
||||
const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841); |
||||
const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516); |
||||
const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126); |
||||
const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693); |
||||
const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707); |
||||
const vec2 poissonDisk10 = vec2(0.5653144, 0.60262); |
||||
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419); |
||||
|
||||
|
||||
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){ |
||||
|
||||
float shadow = 0.0; |
||||
float border = Shadow_BorderCheck(projCoord.xy); |
||||
if (border > 0.0){ |
||||
return 1.0; |
||||
} |
||||
|
||||
vec2 texelSize = pixSize2 * 4.0 * PCFEDGE * shadowBorderScale; |
||||
|
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk0 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw)); |
||||
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw)); |
||||
|
||||
//this is divided by 12 |
||||
return shadow * 0.08333333333; |
||||
} |
||||
|
||||
#ifdef POINTLIGHT |
||||
float getPointLightShadows(in vec4 worldPos,in vec3 lightPos, |
||||
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,in SHADOWMAP shadowMap4,in SHADOWMAP shadowMap5, |
||||
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3,in vec4 projCoord4,in vec4 projCoord5){ |
||||
float shadow = 1.0; |
||||
vec3 vect = worldPos.xyz - lightPos; |
||||
vec3 absv= abs(vect); |
||||
float maxComp = max(absv.x,max(absv.y,absv.z)); |
||||
if(maxComp == absv.y){ |
||||
if(vect.y < 0.0){ |
||||
shadow = GETSHADOW(shadowMap0, projCoord0 / projCoord0.w); |
||||
}else{ |
||||
shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w); |
||||
} |
||||
}else if(maxComp == absv.z){ |
||||
if(vect.z < 0.0){ |
||||
shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w); |
||||
}else{ |
||||
shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w); |
||||
} |
||||
}else if(maxComp == absv.x){ |
||||
if(vect.x < 0.0){ |
||||
shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w); |
||||
}else{ |
||||
shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w); |
||||
} |
||||
} |
||||
return shadow; |
||||
} |
||||
#else |
||||
#ifdef PSSM |
||||
float getDirectionalLightShadows(in vec4 splits,in float shadowPosition, |
||||
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3, |
||||
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3){ |
||||
float shadow = 1.0; |
||||
if(shadowPosition < splits.x){ |
||||
shadow = GETSHADOW(shadowMap0, projCoord0 ); |
||||
}else if( shadowPosition < splits.y){ |
||||
shadowBorderScale = 0.5; |
||||
shadow = GETSHADOW(shadowMap1, projCoord1); |
||||
}else if( shadowPosition < splits.z){ |
||||
shadowBorderScale = 0.25; |
||||
shadow = GETSHADOW(shadowMap2, projCoord2); |
||||
}else if( shadowPosition < splits.w){ |
||||
shadowBorderScale = 0.125; |
||||
shadow = GETSHADOW(shadowMap3, projCoord3); |
||||
} |
||||
return shadow; |
||||
} |
||||
#else |
||||
float getSpotLightShadows(in SHADOWMAP shadowMap,in vec4 projCoord){ |
||||
float shadow = 1.0; |
||||
projCoord /= projCoord.w; |
||||
shadow = GETSHADOW(shadowMap,projCoord); |
||||
|
||||
//a small falloff to make the shadow blend nicely into the not lighten |
||||
//we translate the texture coordinate value to a -1,1 range so the length |
||||
//of the texture coordinate vector is actually the radius of the lighten area on the ground |
||||
projCoord = projCoord * 2.0 - 1.0; |
||||
float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1; |
||||
return mix(shadow,1.0,clamp(fallOff,0.0,1.0)); |
||||
|
||||
} |
||||
#endif |
||||
#endif |
@ -0,0 +1 @@ |
||||
version.properties |
@ -1,11 +0,0 @@ |
||||
# THIS IS AN AUTO-GENERATED FILE.. |
||||
# DO NOT MODIFY! |
||||
build.date=1900-01-01 |
||||
git.revision=0 |
||||
git.branch=unknown |
||||
git.hash= |
||||
git.hash.short= |
||||
git.tag= |
||||
name.full=jMonkeyEngine 3.1.0-UNKNOWN |
||||
version.number=3.1.0 |
||||
version.tag=SNAPSHOT |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue