diff options
Diffstat (limited to 'engine/src/core')
21 files changed, 3181 insertions, 2937 deletions
diff --git a/engine/src/core/com/jme3/app/Application.java b/engine/src/core/com/jme3/app/Application.java index 517ec61..5100725 100644 --- a/engine/src/core/com/jme3/app/Application.java +++ b/engine/src/core/com/jme3/app/Application.java @@ -290,6 +290,10 @@ public class Application implements SystemListener { 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());
}
/**
@@ -580,6 +584,17 @@ public class Application implements SystemListener { }
task.invoke();
} while (((task = taskQueue.poll()) != null));
+
+ /* I think the above is really just doing this:
+ AppTask<?> task;
+ while( (task = taskQueue.poll()) != null ) {
+ if (!task.isCancelled()) {
+ task.invoke();
+ }
+ }
+ //...but it's hard to say for sure. It's so twisted
+ //up that I don't trust my eyes. -pspeed
+ */
if (speed == 0 || paused)
return;
diff --git a/engine/src/core/com/jme3/app/ResetStatsState.java b/engine/src/core/com/jme3/app/ResetStatsState.java new file mode 100644 index 0000000..03f3013 --- /dev/null +++ b/engine/src/core/com/jme3/app/ResetStatsState.java @@ -0,0 +1,60 @@ +/* + * 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.AbstractAppState; +import com.jme3.renderer.RenderManager; + + +/** + * Resets (clearFrame()) the render's stats object every frame + * during AppState.render(). This state is registered once + * with Application to ensure that the stats are cleared once + * a frame. Using this makes sure that any Appliction based + * application that properly runs its state manager will have + * stats reset no matter how many views it has or if it even + * has views. + * + * @author Paul Speed + */ +public class ResetStatsState extends AbstractAppState { + + public ResetStatsState() { + } + + @Override + public void render(RenderManager rm) { + super.render(rm); + rm.getRenderer().getStatistics().clearFrame(); + } + +} diff --git a/engine/src/core/com/jme3/app/SimpleApplication.java b/engine/src/core/com/jme3/app/SimpleApplication.java index c79ce83..aa8e749 100644 --- a/engine/src/core/com/jme3/app/SimpleApplication.java +++ b/engine/src/core/com/jme3/app/SimpleApplication.java @@ -241,13 +241,6 @@ public abstract class SimpleApplication extends Application { rootNode.updateGeometricState(); guiNode.updateGeometricState(); - // Moving this here to make sure it is always done. - // Now the sets are cleared every frame (guaranteed) - // and more than one viewer can access the data. This - // used to be cleared by StatsView but then only StatsView - // could get accurate counts. - renderer.getStatistics().clearFrame(); - // render states stateManager.render(renderManager); renderManager.render(tpf, context.isRenderable()); diff --git a/engine/src/core/com/jme3/app/StatsView.java b/engine/src/core/com/jme3/app/StatsView.java index 49eeb13..f2b2ba5 100644 --- a/engine/src/core/com/jme3/app/StatsView.java +++ b/engine/src/core/com/jme3/app/StatsView.java @@ -105,7 +105,7 @@ public class StatsView extends Node implements Control { labels[i].setText(stringBuilder); } - // Moved to SimpleApplication to make sure it is + // Moved to ResetStatsState to make sure it is // done even if there is no StatsView or the StatsView // is disable. //statistics.clearFrame(); diff --git a/engine/src/core/com/jme3/app/state/AppStateManager.java b/engine/src/core/com/jme3/app/state/AppStateManager.java index 81228af..92a00f5 100644 --- a/engine/src/core/com/jme3/app/state/AppStateManager.java +++ b/engine/src/core/com/jme3/app/state/AppStateManager.java @@ -202,6 +202,9 @@ public class AppStateManager { protected void initializePending(){
AppState[] array = getInitializing();
+ if (array.length == 0)
+ return;
+
synchronized( states ) {
// Move the states that will be initialized
// into the active array. In all but one case the
@@ -219,6 +222,9 @@ public class AppStateManager { protected void terminatePending(){
AppState[] array = getTerminating();
+ if (array.length == 0)
+ return;
+
for (AppState state : array) {
state.cleanup();
}
diff --git a/engine/src/core/com/jme3/input/InputManager.java b/engine/src/core/com/jme3/input/InputManager.java index 23f2988..a0c8788 100644 --- a/engine/src/core/com/jme3/input/InputManager.java +++ b/engine/src/core/com/jme3/input/InputManager.java @@ -1,881 +1,892 @@ -/* - * 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.input; - -import com.jme3.app.Application; -import com.jme3.input.controls.*; -import com.jme3.input.event.*; -import com.jme3.math.FastMath; -import com.jme3.math.Vector2f; -import com.jme3.util.IntMap; -import com.jme3.util.IntMap.Entry; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * The <code>InputManager</code> is responsible for converting input events - * received from the Key, Mouse and Joy Input implementations into an - * abstract, input device independent representation that user code can use. - * <p> - * By default an <code>InputManager</code> is included with every Application instance for use - * in user code to query input, unless the Application is created as headless - * or with input explicitly disabled. - * <p> - * The input manager has two concepts, a {@link Trigger} and a mapping. - * A trigger represents a specific input trigger, such as a key button, - * or a mouse axis. A mapping represents a link onto one or several triggers, - * when the appropriate trigger is activated (e.g. a key is pressed), the - * mapping will be invoked. Any listeners registered to receive an event - * from the mapping will have an event raised. - * <p> - * There are two types of events that {@link InputListener input listeners} - * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action} - * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog} - * events. - * <p> - * <code>onAction</code> events are raised when the specific input - * activates or deactivates. For a digital input such as key press, the <code>onAction()</code> - * event will be raised with the <code>isPressed</code> argument equal to true, - * when the key is released, <code>onAction</code> is called again but this time - * with the <code>isPressed</code> argument set to false. - * For analog inputs, the <code>onAction</code> method will be called any time - * the input is non-zero, however an exception to this is for joystick axis inputs, - * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}. - * <p> - * <code>onAnalog</code> events are raised every frame while the input is activated. - * For digital inputs, every frame that the input is active will cause the - * <code>onAnalog</code> method to be called, the argument <code>value</code> - * argument will equal to the frame's time per frame (TPF) value but only - * for digital inputs. For analog inputs however, the <code>value</code> argument - * will equal the actual analog value. - */ -public class InputManager implements RawInputListener { - - private static final Logger logger = Logger.getLogger(InputManager.class.getName()); - private final KeyInput keys; - private final MouseInput mouse; - private final JoyInput joystick; - private final TouchInput touch; - private float frameTPF; - private long lastLastUpdateTime = 0; - private long lastUpdateTime = 0; - private long frameDelta = 0; - private long firstTime = 0; - private boolean eventsPermitted = false; - private boolean mouseVisible = true; - private boolean safeMode = false; - private float axisDeadZone = 0.05f; - private Vector2f cursorPos = new Vector2f(); - private Joystick[] joysticks; - private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>(); - private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>(); - private final IntMap<Long> pressedButtons = new IntMap<Long>(); - private final IntMap<Float> axisValues = new IntMap<Float>(); - private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>(); - private RawInputListener[] rawListenerArray = null; - private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>(); - - private static class Mapping { - - private final String name; - private final ArrayList<Integer> triggers = new ArrayList<Integer>(); - private final ArrayList<InputListener> listeners = new ArrayList<InputListener>(); - - public Mapping(String name) { - this.name = name; - } - } - - /** - * Initializes the InputManager. - * - * <p>This should only be called internally in {@link Application}. - * - * @param mouse - * @param keys - * @param joystick - * @param touch - * @throws IllegalArgumentException If either mouseInput or keyInput are null. - */ - public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) { - if (keys == null || mouse == null) { - throw new NullPointerException("Mouse or keyboard cannot be null"); - } - - this.keys = keys; - this.mouse = mouse; - this.joystick = joystick; - this.touch = touch; - - keys.setInputListener(this); - mouse.setInputListener(this); - if (joystick != null) { - joystick.setInputListener(this); - joysticks = joystick.loadJoysticks(this); - } - if (touch != null) { - touch.setInputListener(this); - } - - firstTime = keys.getInputTimeNanos(); - } - - private void invokeActions(int hash, boolean pressed) { - ArrayList<Mapping> maps = bindings.get(hash); - if (maps == null) { - return; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - if (listener instanceof ActionListener) { - ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF); - } - } - } - } - - private float computeAnalogValue(long timeDelta) { - if (safeMode || frameDelta == 0) { - return 1f; - } else { - return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1); - } - } - - private void invokeTimedActions(int hash, long time, boolean pressed) { - if (!bindings.containsKey(hash)) { - return; - } - - if (pressed) { - pressedButtons.put(hash, time); - } else { - Long pressTimeObj = pressedButtons.remove(hash); - if (pressTimeObj == null) { - return; // under certain circumstances it can be null, ignore - } // the event then. - - long pressTime = pressTimeObj; - long lastUpdate = lastLastUpdateTime; - long releaseTime = time; - long timeDelta = releaseTime - Math.max(pressTime, lastUpdate); - - if (timeDelta > 0) { - invokeAnalogs(hash, computeAnalogValue(timeDelta), false); - } - } - } - - private void invokeUpdateActions() { - for (Entry<Long> pressedButton : pressedButtons) { - int hash = pressedButton.getKey(); - - long pressTime = pressedButton.getValue(); - long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime); - - if (timeDelta > 0) { - invokeAnalogs(hash, computeAnalogValue(timeDelta), false); - } - } - - for (Entry<Float> axisValue : axisValues) { - int hash = axisValue.getKey(); - float value = axisValue.getValue(); - invokeAnalogs(hash, value * frameTPF, true); - } - } - - private void invokeAnalogs(int hash, float value, boolean isAxis) { - ArrayList<Mapping> maps = bindings.get(hash); - if (maps == null) { - return; - } - - if (!isAxis) { - value *= frameTPF; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - if (listener instanceof AnalogListener) { - // NOTE: multiply by TPF for any button bindings - ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF); - } - } - } - } - - private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) { - if (value < axisDeadZone) { - invokeAnalogs(hash, value, !applyTpf); - return; - } - - ArrayList<Mapping> maps = bindings.get(hash); - if (maps == null) { - return; - } - - boolean valueChanged = !axisValues.containsKey(hash); - if (applyTpf) { - value *= frameTPF; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - - if (listener instanceof ActionListener && valueChanged) { - ((ActionListener) listener).onAction(mapping.name, true, frameTPF); - } - - if (listener instanceof AnalogListener) { - ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF); - } - - } - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void beginInput() { - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void endInput() { - } - - private void onJoyAxisEventQueued(JoyAxisEvent evt) { -// for (int i = 0; i < rawListeners.size(); i++){ -// rawListeners.get(i).onJoyAxisEvent(evt); -// } - - int joyId = evt.getJoyIndex(); - int axis = evt.getAxisIndex(); - float value = evt.getValue(); - if (value < axisDeadZone && value > -axisDeadZone) { - int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true); - int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false); - - Float val1 = axisValues.get(hash1); - Float val2 = axisValues.get(hash2); - - if (val1 != null && val1.floatValue() > axisDeadZone) { - invokeActions(hash1, false); - } - if (val2 != null && val2.floatValue() > axisDeadZone) { - invokeActions(hash2, false); - } - - axisValues.remove(hash1); - axisValues.remove(hash2); - - } else if (value < 0) { - int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true); - int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false); - invokeAnalogsAndActions(hash, -value, true); - axisValues.put(hash, -value); - axisValues.remove(otherHash); - } else { - int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false); - int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true); - invokeAnalogsAndActions(hash, value, true); - axisValues.put(hash, value); - axisValues.remove(otherHash); - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onJoyAxisEvent(JoyAxisEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); - } - - inputQueue.add(evt); - } - - private void onJoyButtonEventQueued(JoyButtonEvent evt) { -// for (int i = 0; i < rawListeners.size(); i++){ -// rawListeners.get(i).onJoyButtonEvent(evt); -// } - - int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex()); - invokeActions(hash, evt.isPressed()); - invokeTimedActions(hash, evt.getTime(), evt.isPressed()); - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onJoyButtonEvent(JoyButtonEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); - } - - inputQueue.add(evt); - } - - private void onMouseMotionEventQueued(MouseMotionEvent evt) { -// for (int i = 0; i < rawListeners.size(); i++){ -// rawListeners.get(i).onMouseMotionEvent(evt); -// } - - if (evt.getDX() != 0) { - float val = Math.abs(evt.getDX()) / 1024f; - invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false); - } - if (evt.getDY() != 0) { - float val = Math.abs(evt.getDY()) / 1024f; - invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false); - } - if (evt.getDeltaWheel() != 0) { - float val = Math.abs(evt.getDeltaWheel()) / 100f; - invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false); - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onMouseMotionEvent(MouseMotionEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); - } - - cursorPos.set(evt.getX(), evt.getY()); - inputQueue.add(evt); - } - - private void onMouseButtonEventQueued(MouseButtonEvent evt) { - int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex()); - invokeActions(hash, evt.isPressed()); - invokeTimedActions(hash, evt.getTime(), evt.isPressed()); - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onMouseButtonEvent(MouseButtonEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); - } - //updating cursor pos on click, so that non android touch events can properly update cursor position. - cursorPos.set(evt.getX(), evt.getY()); - inputQueue.add(evt); - } - - private void onKeyEventQueued(KeyInputEvent evt) { - if (evt.isRepeating()) { - return; // repeat events not used for bindings - } - - int hash = KeyTrigger.keyHash(evt.getKeyCode()); - invokeActions(hash, evt.isPressed()); - invokeTimedActions(hash, evt.getTime(), evt.isPressed()); - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onKeyEvent(KeyInputEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time."); - } - - inputQueue.add(evt); - } - - /** - * Set the deadzone for joystick axes. - * - * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) } - * events will only be raised if the joystick axis value is greater than - * the <code>deadZone</code>. - * - * @param deadZone the deadzone for joystick axes. - */ - public void setAxisDeadZone(float deadZone) { - this.axisDeadZone = deadZone; - } - - /** - * Returns the deadzone for joystick axes. - * - * @return the deadzone for joystick axes. - */ - public float getAxisDeadZone() { - return axisDeadZone; - } - - /** - * Adds a new listener to receive events on the given mappings. - * - * <p>The given InputListener will be registered to receive events - * on the specified mapping names. When a mapping raises an event, the - * listener will have its appropriate method invoked, either - * {@link ActionListener#onAction(java.lang.String, boolean, float) } - * or {@link AnalogListener#onAnalog(java.lang.String, float, float) } - * depending on which interface the <code>listener</code> implements. - * If the listener implements both interfaces, then it will receive the - * appropriate event for each method. - * - * @param listener The listener to register to receive input events. - * @param mappingNames The mapping names which the listener will receive - * events from. - * - * @see InputManager#removeListener(com.jme3.input.controls.InputListener) - */ - public void addListener(InputListener listener, String... mappingNames) { - for (String mappingName : mappingNames) { - Mapping mapping = mappings.get(mappingName); - if (mapping == null) { - mapping = new Mapping(mappingName); - mappings.put(mappingName, mapping); - } - if (!mapping.listeners.contains(listener)) { - mapping.listeners.add(listener); - } - } - } - - /** - * Removes a listener from receiving events. - * - * <p>This will unregister the listener from any mappings that it - * was previously registered with via - * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }. - * - * @param listener The listener to unregister. - * - * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) - */ - public void removeListener(InputListener listener) { - for (Mapping mapping : mappings.values()) { - mapping.listeners.remove(listener); - } - } - - /** - * Create a new mapping to the given triggers. - * - * <p> - * The given mapping will be assigned to the given triggers, when - * any of the triggers given raise an event, the listeners - * registered to the mappings will receive appropriate events. - * - * @param mappingName The mapping name to assign. - * @param triggers The triggers to which the mapping is to be registered. - * - * @see InputManager#deleteMapping(java.lang.String) - */ - public void addMapping(String mappingName, Trigger... triggers) { - Mapping mapping = mappings.get(mappingName); - if (mapping == null) { - mapping = new Mapping(mappingName); - mappings.put(mappingName, mapping); - } - - for (Trigger trigger : triggers) { - int hash = trigger.triggerHashCode(); - ArrayList<Mapping> names = bindings.get(hash); - if (names == null) { - names = new ArrayList<Mapping>(); - bindings.put(hash, names); - } - if (!names.contains(mapping)) { - names.add(mapping); - mapping.triggers.add(hash); - } else { - logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName); - } - } - } - - /** - * Returns true if this InputManager has a mapping registered - * for the given mappingName. - * - * @param mappingName The mapping name to check. - * - * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) - * @see InputManager#deleteMapping(java.lang.String) - */ - public boolean hasMapping(String mappingName) { - return mappings.containsKey(mappingName); - } - - /** - * Deletes a mapping from receiving trigger events. - * - * <p> - * The given mapping will no longer be assigned to receive trigger - * events. - * - * @param mappingName The mapping name to unregister. - * - * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) - */ - public void deleteMapping(String mappingName) { - Mapping mapping = mappings.remove(mappingName); - if (mapping == null) { - throw new IllegalArgumentException("Cannot find mapping: " + mappingName); - } - - ArrayList<Integer> triggers = mapping.triggers; - for (int i = triggers.size() - 1; i >= 0; i--) { - int hash = triggers.get(i); - ArrayList<Mapping> maps = bindings.get(hash); - maps.remove(mapping); - } - } - - /** - * Deletes a specific trigger registered to a mapping. - * - * <p> - * The given mapping will no longer receive events raised by the - * trigger. - * - * @param mappingName The mapping name to cease receiving events from the - * trigger. - * @param trigger The trigger to no longer invoke events on the mapping. - */ - public void deleteTrigger(String mappingName, Trigger trigger) { - Mapping mapping = mappings.get(mappingName); - if (mapping == null) { - throw new IllegalArgumentException("Cannot find mapping: " + mappingName); - } - - ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode()); - maps.remove(mapping); - - } - - /** - * Clears all the input mappings from this InputManager. - * Consequently, also clears all of the - * InputListeners as well. - */ - public void clearMappings() { - mappings.clear(); - bindings.clear(); - reset(); - } - - /** - * Do not use. - * Called to reset pressed keys or buttons when focus is restored. - */ - public void reset() { - pressedButtons.clear(); - axisValues.clear(); - } - - /** - * Returns whether the mouse cursor is visible or not. - * - * <p>By default the cursor is visible. - * - * @return whether the mouse cursor is visible or not. - * - * @see InputManager#setCursorVisible(boolean) - */ - public boolean isCursorVisible() { - return mouseVisible; - } - - /** - * Set whether the mouse cursor should be visible or not. - * - * @param visible whether the mouse cursor should be visible or not. - */ - public void setCursorVisible(boolean visible) { - if (mouseVisible != visible) { - mouseVisible = visible; - mouse.setCursorVisible(mouseVisible); - } - } - - /** - * Returns the current cursor position. The position is relative to the - * bottom-left of the screen and is in pixels. - * - * @return the current cursor position - */ - public Vector2f getCursorPosition() { - return cursorPos; - } - - /** - * Returns an array of all joysticks installed on the system. - * - * @return an array of all joysticks installed on the system. - */ - public Joystick[] getJoysticks() { - return joysticks; - } - - /** - * Adds a {@link RawInputListener} to receive raw input events. - * - * <p> - * Any raw input listeners registered to this <code>InputManager</code> - * will receive raw input events first, before they get handled - * by the <code>InputManager</code> itself. The listeners are - * each processed in the order they were added, e.g. FIFO. - * <p> - * If a raw input listener has handled the event and does not wish - * other listeners down the list to process the event, it may set the - * {@link InputEvent#setConsumed() consumed flag} to indicate the - * event was consumed and shouldn't be processed any further. - * The listener may do this either at each of the event callbacks - * or at the {@link RawInputListener#endInput() } method. - * - * @param listener A listener to receive raw input events. - * - * @see RawInputListener - */ - public void addRawInputListener(RawInputListener listener) { - rawListeners.add(listener); - rawListenerArray = null; - } - - /** - * Removes a {@link RawInputListener} so that it no longer - * receives raw input events. - * - * @param listener The listener to cease receiving raw input events. - * - * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) - */ - public void removeRawInputListener(RawInputListener listener) { - rawListeners.remove(listener); - rawListenerArray = null; - } - - /** - * Clears all {@link RawInputListener}s. - * - * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) - */ - public void clearRawInputListeners() { - rawListeners.clear(); - rawListenerArray = null; - } - - private RawInputListener[] getRawListenerArray() { - if (rawListenerArray == null) - rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]); - return rawListenerArray; - } - - /** - * Enable simulation of mouse events. Used for touchscreen input only. - * - * @param value True to enable simulation of mouse events - */ - public void setSimulateMouse(boolean value) { - if (touch != null) { - touch.setSimulateMouse(value); - } - } - - /** - * Enable simulation of keyboard events. Used for touchscreen input only. - * - * @param value True to enable simulation of keyboard events - */ - public void setSimulateKeyboard(boolean value) { - if (touch != null) { - touch.setSimulateKeyboard(value); - } - } - - private void processQueue() { - int queueSize = inputQueue.size(); - RawInputListener[] array = getRawListenerArray(); - - for (RawInputListener listener : array) { - listener.beginInput(); - - for (int j = 0; j < queueSize; j++) { - InputEvent event = inputQueue.get(j); - if (event.isConsumed()) { - continue; - } - - if (event instanceof MouseMotionEvent) { - listener.onMouseMotionEvent((MouseMotionEvent) event); - } else if (event instanceof KeyInputEvent) { - listener.onKeyEvent((KeyInputEvent) event); - } else if (event instanceof MouseButtonEvent) { - listener.onMouseButtonEvent((MouseButtonEvent) event); - } else if (event instanceof JoyAxisEvent) { - listener.onJoyAxisEvent((JoyAxisEvent) event); - } else if (event instanceof JoyButtonEvent) { - listener.onJoyButtonEvent((JoyButtonEvent) event); - } else if (event instanceof TouchEvent) { - listener.onTouchEvent((TouchEvent) event); - } else { - assert false; - } - } - - listener.endInput(); - } - - for (int i = 0; i < queueSize; i++) { - InputEvent event = inputQueue.get(i); - if (event.isConsumed()) { - continue; - } - - if (event instanceof MouseMotionEvent) { - onMouseMotionEventQueued((MouseMotionEvent) event); - } else if (event instanceof KeyInputEvent) { - onKeyEventQueued((KeyInputEvent) event); - } else if (event instanceof MouseButtonEvent) { - onMouseButtonEventQueued((MouseButtonEvent) event); - } else if (event instanceof JoyAxisEvent) { - onJoyAxisEventQueued((JoyAxisEvent) event); - } else if (event instanceof JoyButtonEvent) { - onJoyButtonEventQueued((JoyButtonEvent) event); - } else if (event instanceof TouchEvent) { - onTouchEventQueued((TouchEvent) event); - } else { - assert false; - } - // larynx, 2011.06.10 - flag event as reusable because - // the android input uses a non-allocating ringbuffer which - // needs to know when the event is not anymore in inputQueue - // and therefor can be reused. - event.setConsumed(); - } - - inputQueue.clear(); - } - - /** - * Updates the <code>InputManager</code>. - * This will query current input devices and send - * appropriate events to registered listeners. - * - * @param tpf Time per frame value. - */ - public void update(float tpf) { - frameTPF = tpf; - - // Activate safemode if the TPF value is so small - // that rounding errors are inevitable - safeMode = tpf < 0.015f; - - long currentTime = keys.getInputTimeNanos(); - frameDelta = currentTime - lastUpdateTime; - - eventsPermitted = true; - - keys.update(); - mouse.update(); - if (joystick != null) { - joystick.update(); - } - if (touch != null) { - touch.update(); - } - - eventsPermitted = false; - - processQueue(); - invokeUpdateActions(); - - lastLastUpdateTime = lastUpdateTime; - lastUpdateTime = currentTime; - } - - /** - * Dispatches touch events to touch listeners - * @param evt The touch event to be dispatched to all onTouch listeners - */ - public void onTouchEventQueued(TouchEvent evt) { - ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode())); - if (maps == null) { - return; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - if (listener instanceof TouchListener) { - ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF); - } - } - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - @Override - public void onTouchEvent(TouchEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time."); - } - inputQueue.add(evt); - } -} +/*
+ * 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.input;
+
+import com.jme3.app.Application;
+import com.jme3.input.controls.*;
+import com.jme3.input.event.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector2f;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The <code>InputManager</code> is responsible for converting input events
+ * received from the Key, Mouse and Joy Input implementations into an
+ * abstract, input device independent representation that user code can use.
+ * <p>
+ * By default an <code>InputManager</code> is included with every Application instance for use
+ * in user code to query input, unless the Application is created as headless
+ * or with input explicitly disabled.
+ * <p>
+ * The input manager has two concepts, a {@link Trigger} and a mapping.
+ * A trigger represents a specific input trigger, such as a key button,
+ * or a mouse axis. A mapping represents a link onto one or several triggers,
+ * when the appropriate trigger is activated (e.g. a key is pressed), the
+ * mapping will be invoked. Any listeners registered to receive an event
+ * from the mapping will have an event raised.
+ * <p>
+ * There are two types of events that {@link InputListener input listeners}
+ * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action}
+ * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog}
+ * events.
+ * <p>
+ * <code>onAction</code> events are raised when the specific input
+ * activates or deactivates. For a digital input such as key press, the <code>onAction()</code>
+ * event will be raised with the <code>isPressed</code> argument equal to true,
+ * when the key is released, <code>onAction</code> is called again but this time
+ * with the <code>isPressed</code> argument set to false.
+ * For analog inputs, the <code>onAction</code> method will be called any time
+ * the input is non-zero, however an exception to this is for joystick axis inputs,
+ * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}.
+ * <p>
+ * <code>onAnalog</code> events are raised every frame while the input is activated.
+ * For digital inputs, every frame that the input is active will cause the
+ * <code>onAnalog</code> method to be called, the argument <code>value</code>
+ * argument will equal to the frame's time per frame (TPF) value but only
+ * for digital inputs. For analog inputs however, the <code>value</code> argument
+ * will equal the actual analog value.
+ */
+public class InputManager implements RawInputListener {
+
+ private static final Logger logger = Logger.getLogger(InputManager.class.getName());
+ private final KeyInput keys;
+ private final MouseInput mouse;
+ private final JoyInput joystick;
+ private final TouchInput touch;
+ private float frameTPF;
+ private long lastLastUpdateTime = 0;
+ private long lastUpdateTime = 0;
+ private long frameDelta = 0;
+ private long firstTime = 0;
+ private boolean eventsPermitted = false;
+ private boolean mouseVisible = true;
+ private boolean safeMode = false;
+ private float axisDeadZone = 0.05f;
+ private Vector2f cursorPos = new Vector2f();
+ private Joystick[] joysticks;
+ private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>();
+ private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>();
+ private final IntMap<Long> pressedButtons = new IntMap<Long>();
+ private final IntMap<Float> axisValues = new IntMap<Float>();
+ private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>();
+ private RawInputListener[] rawListenerArray = null;
+ private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>();
+
+ private static class Mapping {
+
+ private final String name;
+ private final ArrayList<Integer> triggers = new ArrayList<Integer>();
+ private final ArrayList<InputListener> listeners = new ArrayList<InputListener>();
+
+ public Mapping(String name) {
+ this.name = name;
+ }
+ }
+
+ /**
+ * Initializes the InputManager.
+ *
+ * <p>This should only be called internally in {@link Application}.
+ *
+ * @param mouse
+ * @param keys
+ * @param joystick
+ * @param touch
+ * @throws IllegalArgumentException If either mouseInput or keyInput are null.
+ */
+ public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) {
+ if (keys == null || mouse == null) {
+ throw new NullPointerException("Mouse or keyboard cannot be null");
+ }
+
+ this.keys = keys;
+ this.mouse = mouse;
+ this.joystick = joystick;
+ this.touch = touch;
+
+ keys.setInputListener(this);
+ mouse.setInputListener(this);
+ if (joystick != null) {
+ joystick.setInputListener(this);
+ joysticks = joystick.loadJoysticks(this);
+ }
+ if (touch != null) {
+ touch.setInputListener(this);
+ }
+
+ firstTime = keys.getInputTimeNanos();
+ }
+
+ private void invokeActions(int hash, boolean pressed) {
+ ArrayList<Mapping> maps = bindings.get(hash);
+ if (maps == null) {
+ return;
+ }
+
+ int size = maps.size();
+ for (int i = size - 1; i >= 0; i--) {
+ Mapping mapping = maps.get(i);
+ ArrayList<InputListener> listeners = mapping.listeners;
+ int listenerSize = listeners.size();
+ for (int j = listenerSize - 1; j >= 0; j--) {
+ InputListener listener = listeners.get(j);
+ if (listener instanceof ActionListener) {
+ ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF);
+ }
+ }
+ }
+ }
+
+ private float computeAnalogValue(long timeDelta) {
+ if (safeMode || frameDelta == 0) {
+ return 1f;
+ } else {
+ return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1);
+ }
+ }
+
+ private void invokeTimedActions(int hash, long time, boolean pressed) {
+ if (!bindings.containsKey(hash)) {
+ return;
+ }
+
+ if (pressed) {
+ pressedButtons.put(hash, time);
+ } else {
+ Long pressTimeObj = pressedButtons.remove(hash);
+ if (pressTimeObj == null) {
+ return; // under certain circumstances it can be null, ignore
+ } // the event then.
+
+ long pressTime = pressTimeObj;
+ long lastUpdate = lastLastUpdateTime;
+ long releaseTime = time;
+ long timeDelta = releaseTime - Math.max(pressTime, lastUpdate);
+
+ if (timeDelta > 0) {
+ invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
+ }
+ }
+ }
+
+ private void invokeUpdateActions() {
+ if (pressedButtons.size() > 0) for (Entry<Long> pressedButton : pressedButtons) {
+ int hash = pressedButton.getKey();
+
+ long pressTime = pressedButton.getValue();
+ long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime);
+
+ if (timeDelta > 0) {
+ invokeAnalogs(hash, computeAnalogValue(timeDelta), false);
+ }
+ }
+
+ if (axisValues.size() > 0) for (Entry<Float> axisValue : axisValues) {
+ int hash = axisValue.getKey();
+ float value = axisValue.getValue();
+ invokeAnalogs(hash, value * frameTPF, true);
+ }
+ }
+
+ private void invokeAnalogs(int hash, float value, boolean isAxis) {
+ ArrayList<Mapping> maps = bindings.get(hash);
+ if (maps == null) {
+ return;
+ }
+
+ if (!isAxis) {
+ value *= frameTPF;
+ }
+
+ int size = maps.size();
+ for (int i = size - 1; i >= 0; i--) {
+ Mapping mapping = maps.get(i);
+ ArrayList<InputListener> listeners = mapping.listeners;
+ int listenerSize = listeners.size();
+ for (int j = listenerSize - 1; j >= 0; j--) {
+ InputListener listener = listeners.get(j);
+ if (listener instanceof AnalogListener) {
+ // NOTE: multiply by TPF for any button bindings
+ ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
+ }
+ }
+ }
+ }
+
+ private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) {
+ if (value < axisDeadZone) {
+ invokeAnalogs(hash, value, !applyTpf);
+ return;
+ }
+
+ ArrayList<Mapping> maps = bindings.get(hash);
+ if (maps == null) {
+ return;
+ }
+
+ boolean valueChanged = !axisValues.containsKey(hash);
+ if (applyTpf) {
+ value *= frameTPF;
+ }
+
+ int size = maps.size();
+ for (int i = size - 1; i >= 0; i--) {
+ Mapping mapping = maps.get(i);
+ ArrayList<InputListener> listeners = mapping.listeners;
+ int listenerSize = listeners.size();
+ for (int j = listenerSize - 1; j >= 0; j--) {
+ InputListener listener = listeners.get(j);
+
+ if (listener instanceof ActionListener && valueChanged) {
+ ((ActionListener) listener).onAction(mapping.name, true, frameTPF);
+ }
+
+ if (listener instanceof AnalogListener) {
+ ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF);
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void beginInput() {
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void endInput() {
+ }
+
+ private void onJoyAxisEventQueued(JoyAxisEvent evt) {
+// for (int i = 0; i < rawListeners.size(); i++){
+// rawListeners.get(i).onJoyAxisEvent(evt);
+// }
+
+ int joyId = evt.getJoyIndex();
+ int axis = evt.getAxisIndex();
+ float value = evt.getValue();
+ if (value < axisDeadZone && value > -axisDeadZone) {
+ int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
+ int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
+
+ Float val1 = axisValues.get(hash1);
+ Float val2 = axisValues.get(hash2);
+
+ if (val1 != null && val1.floatValue() > axisDeadZone) {
+ invokeActions(hash1, false);
+ }
+ if (val2 != null && val2.floatValue() > axisDeadZone) {
+ invokeActions(hash2, false);
+ }
+
+ axisValues.remove(hash1);
+ axisValues.remove(hash2);
+
+ } else if (value < 0) {
+ int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
+ int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
+ invokeAnalogsAndActions(hash, -value, true);
+ axisValues.put(hash, -value);
+ axisValues.remove(otherHash);
+ } else {
+ int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false);
+ int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true);
+ invokeAnalogsAndActions(hash, value, true);
+ axisValues.put(hash, value);
+ axisValues.remove(otherHash);
+ }
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onJoyAxisEvent(JoyAxisEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
+ }
+
+ inputQueue.add(evt);
+ }
+
+ private void onJoyButtonEventQueued(JoyButtonEvent evt) {
+// for (int i = 0; i < rawListeners.size(); i++){
+// rawListeners.get(i).onJoyButtonEvent(evt);
+// }
+
+ int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex());
+ invokeActions(hash, evt.isPressed());
+ invokeTimedActions(hash, evt.getTime(), evt.isPressed());
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onJoyButtonEvent(JoyButtonEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time.");
+ }
+
+ inputQueue.add(evt);
+ }
+
+ private void onMouseMotionEventQueued(MouseMotionEvent evt) {
+// for (int i = 0; i < rawListeners.size(); i++){
+// rawListeners.get(i).onMouseMotionEvent(evt);
+// }
+
+ if (evt.getDX() != 0) {
+ float val = Math.abs(evt.getDX()) / 1024f;
+ invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false);
+ }
+ if (evt.getDY() != 0) {
+ float val = Math.abs(evt.getDY()) / 1024f;
+ invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false);
+ }
+ if (evt.getDeltaWheel() != 0) {
+ float val = Math.abs(evt.getDeltaWheel()) / 100f;
+ invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false);
+ }
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onMouseMotionEvent(MouseMotionEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
+ }
+
+ cursorPos.set(evt.getX(), evt.getY());
+ inputQueue.add(evt);
+ }
+
+ private void onMouseButtonEventQueued(MouseButtonEvent evt) {
+ int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex());
+ invokeActions(hash, evt.isPressed());
+ invokeTimedActions(hash, evt.getTime(), evt.isPressed());
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onMouseButtonEvent(MouseButtonEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time.");
+ }
+ //updating cursor pos on click, so that non android touch events can properly update cursor position.
+ cursorPos.set(evt.getX(), evt.getY());
+ inputQueue.add(evt);
+ }
+
+ private void onKeyEventQueued(KeyInputEvent evt) {
+ if (evt.isRepeating()) {
+ return; // repeat events not used for bindings
+ }
+
+ int hash = KeyTrigger.keyHash(evt.getKeyCode());
+ invokeActions(hash, evt.isPressed());
+ invokeTimedActions(hash, evt.getTime(), evt.isPressed());
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ public void onKeyEvent(KeyInputEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time.");
+ }
+
+ inputQueue.add(evt);
+ }
+
+ /**
+ * Set the deadzone for joystick axes.
+ *
+ * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) }
+ * events will only be raised if the joystick axis value is greater than
+ * the <code>deadZone</code>.
+ *
+ * @param deadZone the deadzone for joystick axes.
+ */
+ public void setAxisDeadZone(float deadZone) {
+ this.axisDeadZone = deadZone;
+ }
+
+ /**
+ * Returns the deadzone for joystick axes.
+ *
+ * @return the deadzone for joystick axes.
+ */
+ public float getAxisDeadZone() {
+ return axisDeadZone;
+ }
+
+ /**
+ * Adds a new listener to receive events on the given mappings.
+ *
+ * <p>The given InputListener will be registered to receive events
+ * on the specified mapping names. When a mapping raises an event, the
+ * listener will have its appropriate method invoked, either
+ * {@link ActionListener#onAction(java.lang.String, boolean, float) }
+ * or {@link AnalogListener#onAnalog(java.lang.String, float, float) }
+ * depending on which interface the <code>listener</code> implements.
+ * If the listener implements both interfaces, then it will receive the
+ * appropriate event for each method.
+ *
+ * @param listener The listener to register to receive input events.
+ * @param mappingNames The mapping names which the listener will receive
+ * events from.
+ *
+ * @see InputManager#removeListener(com.jme3.input.controls.InputListener)
+ */
+ public void addListener(InputListener listener, String... mappingNames) {
+ for (String mappingName : mappingNames) {
+ Mapping mapping = mappings.get(mappingName);
+ if (mapping == null) {
+ mapping = new Mapping(mappingName);
+ mappings.put(mappingName, mapping);
+ }
+ if (!mapping.listeners.contains(listener)) {
+ mapping.listeners.add(listener);
+ }
+ }
+ }
+
+ /**
+ * Removes a listener from receiving events.
+ *
+ * <p>This will unregister the listener from any mappings that it
+ * was previously registered with via
+ * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }.
+ *
+ * @param listener The listener to unregister.
+ *
+ * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[])
+ */
+ public void removeListener(InputListener listener) {
+ for (Mapping mapping : mappings.values()) {
+ mapping.listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Create a new mapping to the given triggers.
+ *
+ * <p>
+ * The given mapping will be assigned to the given triggers, when
+ * any of the triggers given raise an event, the listeners
+ * registered to the mappings will receive appropriate events.
+ *
+ * @param mappingName The mapping name to assign.
+ * @param triggers The triggers to which the mapping is to be registered.
+ *
+ * @see InputManager#deleteMapping(java.lang.String)
+ */
+ public void addMapping(String mappingName, Trigger... triggers) {
+ Mapping mapping = mappings.get(mappingName);
+ if (mapping == null) {
+ mapping = new Mapping(mappingName);
+ mappings.put(mappingName, mapping);
+ }
+
+ for (Trigger trigger : triggers) {
+ int hash = trigger.triggerHashCode();
+ ArrayList<Mapping> names = bindings.get(hash);
+ if (names == null) {
+ names = new ArrayList<Mapping>();
+ bindings.put(hash, names);
+ }
+ if (!names.contains(mapping)) {
+ names.add(mapping);
+ mapping.triggers.add(hash);
+ } else {
+ logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName);
+ }
+ }
+ }
+
+ /**
+ * Returns true if this InputManager has a mapping registered
+ * for the given mappingName.
+ *
+ * @param mappingName The mapping name to check.
+ *
+ * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[])
+ * @see InputManager#deleteMapping(java.lang.String)
+ */
+ public boolean hasMapping(String mappingName) {
+ return mappings.containsKey(mappingName);
+ }
+
+ /**
+ * Deletes a mapping from receiving trigger events.
+ *
+ * <p>
+ * The given mapping will no longer be assigned to receive trigger
+ * events.
+ *
+ * @param mappingName The mapping name to unregister.
+ *
+ * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[])
+ */
+ public void deleteMapping(String mappingName) {
+ Mapping mapping = mappings.remove(mappingName);
+ if (mapping == null) {
+ throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
+ }
+
+ ArrayList<Integer> triggers = mapping.triggers;
+ for (int i = triggers.size() - 1; i >= 0; i--) {
+ int hash = triggers.get(i);
+ ArrayList<Mapping> maps = bindings.get(hash);
+ maps.remove(mapping);
+ }
+ }
+
+ /**
+ * Deletes a specific trigger registered to a mapping.
+ *
+ * <p>
+ * The given mapping will no longer receive events raised by the
+ * trigger.
+ *
+ * @param mappingName The mapping name to cease receiving events from the
+ * trigger.
+ * @param trigger The trigger to no longer invoke events on the mapping.
+ */
+ public void deleteTrigger(String mappingName, Trigger trigger) {
+ Mapping mapping = mappings.get(mappingName);
+ if (mapping == null) {
+ throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
+ }
+
+ ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode());
+ maps.remove(mapping);
+
+ }
+
+ /**
+ * Clears all the input mappings from this InputManager.
+ * Consequently, also clears all of the
+ * InputListeners as well.
+ */
+ public void clearMappings() {
+ mappings.clear();
+ bindings.clear();
+ reset();
+ }
+
+ /**
+ * Do not use.
+ * Called to reset pressed keys or buttons when focus is restored.
+ */
+ public void reset() {
+ pressedButtons.clear();
+ axisValues.clear();
+ }
+
+ /**
+ * Returns whether the mouse cursor is visible or not.
+ *
+ * <p>By default the cursor is visible.
+ *
+ * @return whether the mouse cursor is visible or not.
+ *
+ * @see InputManager#setCursorVisible(boolean)
+ */
+ public boolean isCursorVisible() {
+ return mouseVisible;
+ }
+
+ /**
+ * Set whether the mouse cursor should be visible or not.
+ *
+ * @param visible whether the mouse cursor should be visible or not.
+ */
+ public void setCursorVisible(boolean visible) {
+ if (mouseVisible != visible) {
+ mouseVisible = visible;
+ mouse.setCursorVisible(mouseVisible);
+ }
+ }
+
+ /**
+ * Returns the current cursor position. The position is relative to the
+ * bottom-left of the screen and is in pixels.
+ *
+ * @return the current cursor position
+ */
+ public Vector2f getCursorPosition() {
+ return cursorPos;
+ }
+
+ /**
+ * Returns an array of all joysticks installed on the system.
+ *
+ * @return an array of all joysticks installed on the system.
+ */
+ public Joystick[] getJoysticks() {
+ return joysticks;
+ }
+
+ /**
+ * Adds a {@link RawInputListener} to receive raw input events.
+ *
+ * <p>
+ * Any raw input listeners registered to this <code>InputManager</code>
+ * will receive raw input events first, before they get handled
+ * by the <code>InputManager</code> itself. The listeners are
+ * each processed in the order they were added, e.g. FIFO.
+ * <p>
+ * If a raw input listener has handled the event and does not wish
+ * other listeners down the list to process the event, it may set the
+ * {@link InputEvent#setConsumed() consumed flag} to indicate the
+ * event was consumed and shouldn't be processed any further.
+ * The listener may do this either at each of the event callbacks
+ * or at the {@link RawInputListener#endInput() } method.
+ *
+ * @param listener A listener to receive raw input events.
+ *
+ * @see RawInputListener
+ */
+ public void addRawInputListener(RawInputListener listener) {
+ rawListeners.add(listener);
+ rawListenerArray = null;
+ }
+
+ /**
+ * Removes a {@link RawInputListener} so that it no longer
+ * receives raw input events.
+ *
+ * @param listener The listener to cease receiving raw input events.
+ *
+ * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener)
+ */
+ public void removeRawInputListener(RawInputListener listener) {
+ rawListeners.remove(listener);
+ rawListenerArray = null;
+ }
+
+ /**
+ * Clears all {@link RawInputListener}s.
+ *
+ * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener)
+ */
+ public void clearRawInputListeners() {
+ rawListeners.clear();
+ rawListenerArray = null;
+ }
+
+ private RawInputListener[] getRawListenerArray() {
+ if (rawListenerArray == null)
+ rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]);
+ return rawListenerArray;
+ }
+
+ /**
+ * Enable simulation of mouse events. Used for touchscreen input only.
+ *
+ * @param value True to enable simulation of mouse events
+ */
+ public void setSimulateMouse(boolean value) {
+ if (touch != null) {
+ touch.setSimulateMouse(value);
+ }
+ }
+ /**
+ * Returns state of simulation of mouse events. Used for touchscreen input only.
+ *
+ */
+ public boolean getSimulateMouse() {
+ if (touch != null) {
+ return touch.getSimulateMouse();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Enable simulation of keyboard events. Used for touchscreen input only.
+ *
+ * @param value True to enable simulation of keyboard events
+ */
+ public void setSimulateKeyboard(boolean value) {
+ if (touch != null) {
+ touch.setSimulateKeyboard(value);
+ }
+ }
+
+ private void processQueue() {
+ int queueSize = inputQueue.size();
+ RawInputListener[] array = getRawListenerArray();
+
+ for (RawInputListener listener : array) {
+ listener.beginInput();
+
+ for (int j = 0; j < queueSize; j++) {
+ InputEvent event = inputQueue.get(j);
+ if (event.isConsumed()) {
+ continue;
+ }
+
+ if (event instanceof MouseMotionEvent) {
+ listener.onMouseMotionEvent((MouseMotionEvent) event);
+ } else if (event instanceof KeyInputEvent) {
+ listener.onKeyEvent((KeyInputEvent) event);
+ } else if (event instanceof MouseButtonEvent) {
+ listener.onMouseButtonEvent((MouseButtonEvent) event);
+ } else if (event instanceof JoyAxisEvent) {
+ listener.onJoyAxisEvent((JoyAxisEvent) event);
+ } else if (event instanceof JoyButtonEvent) {
+ listener.onJoyButtonEvent((JoyButtonEvent) event);
+ } else if (event instanceof TouchEvent) {
+ listener.onTouchEvent((TouchEvent) event);
+ } else {
+ assert false;
+ }
+ }
+
+ listener.endInput();
+ }
+
+ for (int i = 0; i < queueSize; i++) {
+ InputEvent event = inputQueue.get(i);
+ if (event.isConsumed()) {
+ continue;
+ }
+
+ if (event instanceof MouseMotionEvent) {
+ onMouseMotionEventQueued((MouseMotionEvent) event);
+ } else if (event instanceof KeyInputEvent) {
+ onKeyEventQueued((KeyInputEvent) event);
+ } else if (event instanceof MouseButtonEvent) {
+ onMouseButtonEventQueued((MouseButtonEvent) event);
+ } else if (event instanceof JoyAxisEvent) {
+ onJoyAxisEventQueued((JoyAxisEvent) event);
+ } else if (event instanceof JoyButtonEvent) {
+ onJoyButtonEventQueued((JoyButtonEvent) event);
+ } else if (event instanceof TouchEvent) {
+ onTouchEventQueued((TouchEvent) event);
+ } else {
+ assert false;
+ }
+ // larynx, 2011.06.10 - flag event as reusable because
+ // the android input uses a non-allocating ringbuffer which
+ // needs to know when the event is not anymore in inputQueue
+ // and therefor can be reused.
+ event.setConsumed();
+ }
+
+ inputQueue.clear();
+ }
+
+ /**
+ * Updates the <code>InputManager</code>.
+ * This will query current input devices and send
+ * appropriate events to registered listeners.
+ *
+ * @param tpf Time per frame value.
+ */
+ public void update(float tpf) {
+ frameTPF = tpf;
+
+ // Activate safemode if the TPF value is so small
+ // that rounding errors are inevitable
+ safeMode = tpf < 0.015f;
+
+ long currentTime = keys.getInputTimeNanos();
+ frameDelta = currentTime - lastUpdateTime;
+
+ eventsPermitted = true;
+
+ keys.update();
+ mouse.update();
+ if (joystick != null) {
+ joystick.update();
+ }
+ if (touch != null) {
+ touch.update();
+ }
+
+ eventsPermitted = false;
+
+ processQueue();
+ invokeUpdateActions();
+
+ lastLastUpdateTime = lastUpdateTime;
+ lastUpdateTime = currentTime;
+ }
+
+ /**
+ * Dispatches touch events to touch listeners
+ * @param evt The touch event to be dispatched to all onTouch listeners
+ */
+ public void onTouchEventQueued(TouchEvent evt) {
+ ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode()));
+ if (maps == null) {
+ return;
+ }
+
+ int size = maps.size();
+ for (int i = size - 1; i >= 0; i--) {
+ Mapping mapping = maps.get(i);
+ ArrayList<InputListener> listeners = mapping.listeners;
+ int listenerSize = listeners.size();
+ for (int j = listenerSize - 1; j >= 0; j--) {
+ InputListener listener = listeners.get(j);
+ if (listener instanceof TouchListener) {
+ ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF);
+ }
+ }
+ }
+ }
+
+ /**
+ * Callback from RawInputListener. Do not use.
+ */
+ @Override
+ public void onTouchEvent(TouchEvent evt) {
+ if (!eventsPermitted) {
+ throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time.");
+ }
+ inputQueue.add(evt);
+ }
+}
diff --git a/engine/src/core/com/jme3/input/SoftTextDialogInput.java b/engine/src/core/com/jme3/input/SoftTextDialogInput.java new file mode 100644 index 0000000..d166b97 --- /dev/null +++ b/engine/src/core/com/jme3/input/SoftTextDialogInput.java @@ -0,0 +1,13 @@ +package com.jme3.input;
+
+import com.jme3.input.controls.SoftTextDialogInputListener;
+
+public interface SoftTextDialogInput {
+
+ public static int TEXT_ENTRY_DIALOG = 0;
+ public static int NUMERIC_ENTRY_DIALOG = 1;
+ public static int NUMERIC_KEYPAD_DIALOG = 2;
+
+ public void requestDialog(int id, String title, String initialValue, SoftTextDialogInputListener listener);
+
+}
diff --git a/engine/src/core/com/jme3/input/TouchInput.java b/engine/src/core/com/jme3/input/TouchInput.java index 2f45b44..e69c4b3 100644 --- a/engine/src/core/com/jme3/input/TouchInput.java +++ b/engine/src/core/com/jme3/input/TouchInput.java @@ -73,6 +73,12 @@ public interface TouchInput extends Input { * @param simulate if mouse events should be generated */ public void setSimulateMouse(boolean simulate); + + /** + * Get if mouse events are generated + * + */ + public boolean getSimulateMouse(); /** * Set if keyboard events should be generated diff --git a/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java b/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java new file mode 100644 index 0000000..d33844d --- /dev/null +++ b/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009-2010 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.input.controls; + +/** + * + * @author potterec (aka iwgeric) + */ +public interface SoftTextDialogInputListener { + + public static int COMPLETE = 0; + public static int CANCEL = 1; + + public void onSoftText(int action, String text); +} diff --git a/engine/src/core/com/jme3/math/Transform.java b/engine/src/core/com/jme3/math/Transform.java index 7ccd847..3573d8c 100644 --- a/engine/src/core/com/jme3/math/Transform.java +++ b/engine/src/core/com/jme3/math/Transform.java @@ -1,318 +1,318 @@ -/*
- * Copyright (c) 2009-2010 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.math;
-
-import com.jme3.export.*;
-import java.io.IOException;
-
-/**
- * Started Date: Jul 16, 2004<br><br>
- * Represents a translation, rotation and scale in one object.
- *
- * @author Jack Lindamood
- * @author Joshua Slack
- */
-public final class Transform implements Savable, Cloneable, java.io.Serializable {
-
- static final long serialVersionUID = 1;
-
- public static final Transform IDENTITY = new Transform();
-
- private Quaternion rot = new Quaternion();
- private Vector3f translation = new Vector3f();
- private Vector3f scale = new Vector3f(1,1,1);
-
- public Transform(Vector3f translation, Quaternion rot){
- this.translation.set(translation);
- this.rot.set(rot);
- }
-
- public Transform(Vector3f translation, Quaternion rot, Vector3f scale){
- this(translation, rot);
- this.scale.set(scale);
- }
-
- public Transform(Vector3f translation){
- this(translation, Quaternion.IDENTITY);
- }
-
- public Transform(Quaternion rot){
- this(Vector3f.ZERO, rot);
- }
-
- public Transform(){
- this(Vector3f.ZERO, Quaternion.IDENTITY);
- }
-
- /**
- * Sets this rotation to the given Quaternion value.
- * @param rot The new rotation for this matrix.
- * @return this
- */
- public Transform setRotation(Quaternion rot) {
- this.rot.set(rot);
- return this;
- }
-
- /**
- * Sets this translation to the given value.
- * @param trans The new translation for this matrix.
- * @return this
- */
- public Transform setTranslation(Vector3f trans) {
- this.translation.set(trans);
- return this;
- }
-
- /**
- * Return the translation vector in this matrix.
- * @return translation vector.
- */
- public Vector3f getTranslation() {
- return translation;
- }
-
- /**
- * Sets this scale to the given value.
- * @param scale The new scale for this matrix.
- * @return this
- */
- public Transform setScale(Vector3f scale) {
- this.scale.set(scale);
- return this;
- }
-
- /**
- * Sets this scale to the given value.
- * @param scale The new scale for this matrix.
- * @return this
- */
- public Transform setScale(float scale) {
- this.scale.set(scale, scale, scale);
- return this;
- }
-
- /**
- * Return the scale vector in this matrix.
- * @return scale vector.
- */
- public Vector3f getScale() {
- return scale;
- }
-
- /**
- * Stores this translation value into the given vector3f. If trans is null, a new vector3f is created to
- * hold the value. The value, once stored, is returned.
- * @param trans The store location for this matrix's translation.
- * @return The value of this matrix's translation.
- */
- public Vector3f getTranslation(Vector3f trans) {
- if (trans==null) trans=new Vector3f();
- trans.set(this.translation);
- return trans;
- }
-
- /**
- * Stores this rotation value into the given Quaternion. If quat is null, a new Quaternion is created to
- * hold the value. The value, once stored, is returned.
- * @param quat The store location for this matrix's rotation.
- * @return The value of this matrix's rotation.
- */
- public Quaternion getRotation(Quaternion quat) {
- if (quat==null) quat=new Quaternion();
- quat.set(rot);
- return quat;
- }
-
- /**
- * Return the rotation quaternion in this matrix.
- * @return rotation quaternion.
- */
- public Quaternion getRotation() {
- return rot;
- }
-
- /**
- * Stores this scale value into the given vector3f. If scale is null, a new vector3f is created to
- * hold the value. The value, once stored, is returned.
- * @param scale The store location for this matrix's scale.
- * @return The value of this matrix's scale.
- */
- public Vector3f getScale(Vector3f scale) {
- if (scale==null) scale=new Vector3f();
- scale.set(this.scale);
- return scale;
- }
-
- /**
- * Sets this matrix to the interpolation between the first matrix and the second by delta amount.
- * @param t1 The begining transform.
- * @param t2 The ending transform.
- * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2.
- */
- public void interpolateTransforms(Transform t1, Transform t2, float delta) {
- this.rot.slerp(t1.rot,t2.rot,delta);
- this.translation.interpolate(t1.translation,t2.translation,delta);
- this.scale.interpolate(t1.scale,t2.scale,delta);
- }
-
- /**
- * Changes the values of this matrix acording to it's parent. Very similar to the concept of Node/Spatial transforms.
- * @param parent The parent matrix.
- * @return This matrix, after combining.
- */
- public Transform combineWithParent(Transform parent) {
- scale.multLocal(parent.scale);
-// rot.multLocal(parent.rot);
- parent.rot.mult(rot, rot);
-
- // This here, is evil code
-// parent
-// .rot
-// .multLocal(translation)
-// .multLocal(parent.scale)
-// .addLocal(parent.translation);
-
- translation.multLocal(parent.scale);
- parent
- .rot
- .multLocal(translation)
- .addLocal(parent.translation);
- return this;
- }
-
- /**
- * Sets this matrix's translation to the given x,y,z values.
- * @param x This matrix's new x translation.
- * @param y This matrix's new y translation.
- * @param z This matrix's new z translation.
- * @return this
- */
- public Transform setTranslation(float x,float y, float z) {
- translation.set(x,y,z);
- return this;
- }
-
- /**
- * Sets this matrix's scale to the given x,y,z values.
- * @param x This matrix's new x scale.
- * @param y This matrix's new y scale.
- * @param z This matrix's new z scale.
- * @return this
- */
- public Transform setScale(float x, float y, float z) {
- scale.set(x,y,z);
- return this;
- }
-
- public Vector3f transformVector(final Vector3f in, Vector3f store){
- if (store == null)
- store = new Vector3f();
-
- // multiply with scale first, then rotate, finally translate (cf.
- // Eberly)
- return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation);
- }
-
- public Vector3f transformInverseVector(final Vector3f in, Vector3f store){
- if (store == null)
- store = new Vector3f();
-
- // The author of this code should look above and take the inverse of that
- // But for some reason, they didnt ..
-// in.subtract(translation, store).divideLocal(scale);
-// rot.inverse().mult(store, store);
-
- in.subtract(translation, store);
- rot.inverse().mult(store, store);
- store.divideLocal(scale);
-
- return store;
- }
-
- /**
- * Loads the identity. Equal to translation=1,1,1 scale=0,0,0 rot=0,0,0,1.
- */
- public void loadIdentity() {
- translation.set(0,0,0);
- scale.set(1,1,1);
- rot.set(0,0,0,1);
- }
-
- @Override
- public String toString(){
- return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n"
- + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n"
- + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]";
- }
-
- /**
- * Sets this matrix to be equal to the given matrix.
- * @param matrixQuat The matrix to be equal to.
- * @return this
- */
- public Transform set(Transform matrixQuat) {
- this.translation.set(matrixQuat.translation);
- this.rot.set(matrixQuat.rot);
- this.scale.set(matrixQuat.scale);
- return this;
- }
-
- public void write(JmeExporter e) throws IOException {
- OutputCapsule capsule = e.getCapsule(this);
- capsule.write(rot, "rot", new Quaternion());
- capsule.write(translation, "translation", Vector3f.ZERO);
- capsule.write(scale, "scale", Vector3f.UNIT_XYZ);
- }
-
- public void read(JmeImporter e) throws IOException {
- InputCapsule capsule = e.getCapsule(this);
-
- rot = (Quaternion)capsule.readSavable("rot", new Quaternion());
- translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO);
- scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ);
- }
-
- @Override
- public Transform clone() {
- try {
- Transform tq = (Transform) super.clone();
- tq.rot = rot.clone();
- tq.scale = scale.clone();
- tq.translation = translation.clone();
- return tq;
- } catch (CloneNotSupportedException e) {
- throw new AssertionError();
- }
- }
-}
+/* + * Copyright (c) 2009-2010 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.math; + +import com.jme3.export.*; +import java.io.IOException; + +/** + * Started Date: Jul 16, 2004<br><br> + * Represents a translation, rotation and scale in one object. + * + * @author Jack Lindamood + * @author Joshua Slack + */ +public final class Transform implements Savable, Cloneable, java.io.Serializable { + + static final long serialVersionUID = 1; + + public static final Transform IDENTITY = new Transform(); + + private Quaternion rot = new Quaternion(); + private Vector3f translation = new Vector3f(); + private Vector3f scale = new Vector3f(1,1,1); + + public Transform(Vector3f translation, Quaternion rot){ + this.translation.set(translation); + this.rot.set(rot); + } + + public Transform(Vector3f translation, Quaternion rot, Vector3f scale){ + this(translation, rot); + this.scale.set(scale); + } + + public Transform(Vector3f translation){ + this(translation, Quaternion.IDENTITY); + } + + public Transform(Quaternion rot){ + this(Vector3f.ZERO, rot); + } + + public Transform(){ + this(Vector3f.ZERO, Quaternion.IDENTITY); + } + + /** + * Sets this rotation to the given Quaternion value. + * @param rot The new rotation for this matrix. + * @return this + */ + public Transform setRotation(Quaternion rot) { + this.rot.set(rot); + return this; + } + + /** + * Sets this translation to the given value. + * @param trans The new translation for this matrix. + * @return this + */ + public Transform setTranslation(Vector3f trans) { + this.translation.set(trans); + return this; + } + + /** + * Return the translation vector in this matrix. + * @return translation vector. + */ + public Vector3f getTranslation() { + return translation; + } + + /** + * Sets this scale to the given value. + * @param scale The new scale for this matrix. + * @return this + */ + public Transform setScale(Vector3f scale) { + this.scale.set(scale); + return this; + } + + /** + * Sets this scale to the given value. + * @param scale The new scale for this matrix. + * @return this + */ + public Transform setScale(float scale) { + this.scale.set(scale, scale, scale); + return this; + } + + /** + * Return the scale vector in this matrix. + * @return scale vector. + */ + public Vector3f getScale() { + return scale; + } + + /** + * Stores this translation value into the given vector3f. If trans is null, a new vector3f is created to + * hold the value. The value, once stored, is returned. + * @param trans The store location for this matrix's translation. + * @return The value of this matrix's translation. + */ + public Vector3f getTranslation(Vector3f trans) { + if (trans==null) trans=new Vector3f(); + trans.set(this.translation); + return trans; + } + + /** + * Stores this rotation value into the given Quaternion. If quat is null, a new Quaternion is created to + * hold the value. The value, once stored, is returned. + * @param quat The store location for this matrix's rotation. + * @return The value of this matrix's rotation. + */ + public Quaternion getRotation(Quaternion quat) { + if (quat==null) quat=new Quaternion(); + quat.set(rot); + return quat; + } + + /** + * Return the rotation quaternion in this matrix. + * @return rotation quaternion. + */ + public Quaternion getRotation() { + return rot; + } + + /** + * Stores this scale value into the given vector3f. If scale is null, a new vector3f is created to + * hold the value. The value, once stored, is returned. + * @param scale The store location for this matrix's scale. + * @return The value of this matrix's scale. + */ + public Vector3f getScale(Vector3f scale) { + if (scale==null) scale=new Vector3f(); + scale.set(this.scale); + return scale; + } + + /** + * Sets this matrix to the interpolation between the first matrix and the second by delta amount. + * @param t1 The begining transform. + * @param t2 The ending transform. + * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2. + */ + public void interpolateTransforms(Transform t1, Transform t2, float delta) { + this.rot.slerp(t1.rot,t2.rot,delta); + this.translation.interpolate(t1.translation,t2.translation,delta); + this.scale.interpolate(t1.scale,t2.scale,delta); + } + + /** + * Changes the values of this matrix acording to it's parent. Very similar to the concept of Node/Spatial transforms. + * @param parent The parent matrix. + * @return This matrix, after combining. + */ + public Transform combineWithParent(Transform parent) { + scale.multLocal(parent.scale); +// rot.multLocal(parent.rot); + parent.rot.mult(rot, rot); + + // This here, is evil code +// parent +// .rot +// .multLocal(translation) +// .multLocal(parent.scale) +// .addLocal(parent.translation); + + translation.multLocal(parent.scale); + parent + .rot + .multLocal(translation) + .addLocal(parent.translation); + return this; + } + + /** + * Sets this matrix's translation to the given x,y,z values. + * @param x This matrix's new x translation. + * @param y This matrix's new y translation. + * @param z This matrix's new z translation. + * @return this + */ + public Transform setTranslation(float x,float y, float z) { + translation.set(x,y,z); + return this; + } + + /** + * Sets this matrix's scale to the given x,y,z values. + * @param x This matrix's new x scale. + * @param y This matrix's new y scale. + * @param z This matrix's new z scale. + * @return this + */ + public Transform setScale(float x, float y, float z) { + scale.set(x,y,z); + return this; + } + + public Vector3f transformVector(final Vector3f in, Vector3f store){ + if (store == null) + store = new Vector3f(); + + // multiply with scale first, then rotate, finally translate (cf. + // Eberly) + return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation); + } + + public Vector3f transformInverseVector(final Vector3f in, Vector3f store){ + if (store == null) + store = new Vector3f(); + + // The author of this code should look above and take the inverse of that + // But for some reason, they didnt .. +// in.subtract(translation, store).divideLocal(scale); +// rot.inverse().mult(store, store); + + in.subtract(translation, store); + rot.inverse().mult(store, store); + store.divideLocal(scale); + + return store; + } + + /** + * Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1. + */ + public void loadIdentity() { + translation.set(0,0,0); + scale.set(1,1,1); + rot.set(0,0,0,1); + } + + @Override + public String toString(){ + return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n" + + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n" + + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]"; + } + + /** + * Sets this matrix to be equal to the given matrix. + * @param matrixQuat The matrix to be equal to. + * @return this + */ + public Transform set(Transform matrixQuat) { + this.translation.set(matrixQuat.translation); + this.rot.set(matrixQuat.rot); + this.scale.set(matrixQuat.scale); + return this; + } + + public void write(JmeExporter e) throws IOException { + OutputCapsule capsule = e.getCapsule(this); + capsule.write(rot, "rot", new Quaternion()); + capsule.write(translation, "translation", Vector3f.ZERO); + capsule.write(scale, "scale", Vector3f.UNIT_XYZ); + } + + public void read(JmeImporter e) throws IOException { + InputCapsule capsule = e.getCapsule(this); + + rot = (Quaternion)capsule.readSavable("rot", new Quaternion()); + translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO); + scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ); + } + + @Override + public Transform clone() { + try { + Transform tq = (Transform) super.clone(); + tq.rot = rot.clone(); + tq.scale = scale.clone(); + tq.translation = translation.clone(); + return tq; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } +} diff --git a/engine/src/core/com/jme3/post/Filter.java b/engine/src/core/com/jme3/post/Filter.java index ef9a6ff..12babf5 100644 --- a/engine/src/core/com/jme3/post/Filter.java +++ b/engine/src/core/com/jme3/post/Filter.java @@ -40,6 +40,7 @@ import com.jme3.renderer.Renderer; import com.jme3.renderer.ViewPort;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import java.io.IOException;
import java.util.Collection;
@@ -236,7 +237,7 @@ public abstract class Filter implements Savable { * cleanup this filter
* @param r
*/
- protected final void cleanup(Renderer r) {
+ protected final void cleanup(Renderer r) {
processor = null;
if (defaultPass != null) {
defaultPass.cleanup(r);
@@ -269,8 +270,6 @@ public abstract class Filter implements Savable { protected void cleanUpFilter(Renderer r) {
}
- ;
-
/**
* Must return the material used for this filter.
* this method is called every frame.
@@ -278,6 +277,14 @@ public abstract class Filter implements Savable { * @return the material used for this filter.
*/
protected abstract Material getMaterial();
+
+ /**
+ * Override if you want to do something special with the depth texture;
+ * @param depthTexture
+ */
+ protected void setDepthTexture(Texture depthTexture){
+ getMaterial().setTexture("DepthTexture", depthTexture);
+ }
/**
* Override this method if you want to make a pre pass, before the actual rendering of the frame
diff --git a/engine/src/core/com/jme3/post/FilterPostProcessor.java b/engine/src/core/com/jme3/post/FilterPostProcessor.java index 2e48f0f..d97e8e9 100644 --- a/engine/src/core/com/jme3/post/FilterPostProcessor.java +++ b/engine/src/core/com/jme3/post/FilterPostProcessor.java @@ -77,7 +77,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable { private int originalHeight;
private int lastFilterIndex = -1;
private boolean cameraInit = false;
-
+
/**
* Create a FilterProcessor
* @param assetManager the assetManager
@@ -98,8 +98,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable { * @param filter the filter to add
*/
public void addFilter(Filter filter) {
- filters.add(filter);
- filter.setProcessor(this);
+ filters.add(filter);
if (isInitialized()) {
initFilter(filter, viewPort);
@@ -148,14 +147,17 @@ public class FilterPostProcessor implements SceneProcessor, Savable { * @param vp
*/
private void initFilter(Filter filter, ViewPort vp) {
- filter.init(assetManager, renderManager, vp, width, height);
+ filter.setProcessor(this);
if (filter.isRequiresDepthTexture()) {
- if (!computeDepth && renderFrameBuffer != null) {
+ if (!computeDepth && renderFrameBuffer != null) {
depthTexture = new Texture2D(width, height, Format.Depth24);
renderFrameBuffer.setDepthTexture(depthTexture);
}
computeDepth = true;
- filter.getMaterial().setTexture("DepthTexture", depthTexture);
+ filter.init(assetManager, renderManager, vp, width, height);
+ filter.setDepthTexture(depthTexture);
+ } else {
+ filter.init(assetManager, renderManager, vp, width, height);
}
}
@@ -281,9 +283,9 @@ public class FilterPostProcessor implements SceneProcessor, Savable { } else if (renderFrameBufferMS != null) {
sceneBuffer = renderFrameBufferMS;
}
- renderFilterChain(renderer, sceneBuffer);
+ renderFilterChain(renderer, sceneBuffer);
renderer.setFrameBuffer(outputBuffer);
-
+
//viewport can be null if no filters are enabled
if (viewPort != null) {
renderManager.setCamera(viewPort.getCamera(), false);
@@ -356,8 +358,11 @@ public class FilterPostProcessor implements SceneProcessor, Savable { //reseting the viewport camera viewport to its initial value
viewPort.getCamera().resize(originalWidth, originalHeight, true);
viewPort.getCamera().setViewPort(left, right, bottom, top);
- viewPort.setOutputFrameBuffer(outputBuffer);
+ viewPort.setOutputFrameBuffer(outputBuffer);
viewPort = null;
+ for (Filter filter : filters) {
+ filter.cleanup(renderer);
+ }
}
}
@@ -484,7 +489,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable { * For internal use only<br>
* returns the depth texture of the scene
* @return
- */
+ */
public Texture2D getDepthTexture() {
return depthTexture;
}
diff --git a/engine/src/core/com/jme3/renderer/RenderManager.java b/engine/src/core/com/jme3/renderer/RenderManager.java index 1d58d22..a0f5fdf 100644 --- a/engine/src/core/com/jme3/renderer/RenderManager.java +++ b/engine/src/core/com/jme3/renderer/RenderManager.java @@ -1,1170 +1,1178 @@ -/* - * 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.renderer; - -import com.jme3.material.Material; -import com.jme3.material.MaterialDef; -import com.jme3.material.RenderState; -import com.jme3.material.Technique; -import com.jme3.math.*; -import com.jme3.post.SceneProcessor; -import com.jme3.renderer.queue.GeometryList; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.renderer.queue.RenderQueue.ShadowMode; -import com.jme3.scene.*; -import com.jme3.shader.Uniform; -import com.jme3.shader.UniformBinding; -import com.jme3.shader.VarType; -import com.jme3.system.NullRenderer; -import com.jme3.system.Timer; -import com.jme3.util.IntMap.Entry; -import com.jme3.util.TempVars; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Logger; - -/** - * <code>RenderManager</code> is a high-level rendering interface that is - * above the Renderer implementation. RenderManager takes care - * of rendering the scene graphs attached to each viewport and - * handling SceneProcessors. - * - * @see SceneProcessor - * @see ViewPort - * @see Spatial - */ -public class RenderManager { - - private static final Logger logger = Logger.getLogger(RenderManager.class.getName()); - - private Renderer renderer; - private Timer timer; - private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>(); - private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>(); - private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>(); - private Camera prevCam = null; - private Material forcedMaterial = null; - private String forcedTechnique = null; - private RenderState forcedRenderState = null; - private boolean shader; - private int viewX, viewY, viewWidth, viewHeight; - private float near, far; - private Matrix4f orthoMatrix = new Matrix4f(); - private Matrix4f viewMatrix = new Matrix4f(); - private Matrix4f projMatrix = new Matrix4f(); - private Matrix4f viewProjMatrix = new Matrix4f(); - private Matrix4f worldMatrix = new Matrix4f(); - private Vector3f camUp = new Vector3f(), - camLeft = new Vector3f(), - camDir = new Vector3f(), - camLoc = new Vector3f(); - //temp technique - private String tmpTech; - private boolean handleTranlucentBucket = true; - - /** - * Create a high-level rendering interface over the - * low-level rendering interface. - * @param renderer - */ - public RenderManager(Renderer renderer) { - this.renderer = renderer; - //this.shader = renderer.getCaps().contains(Caps.GLSL100); - } - - /** - * Returns the pre ViewPort with the given name. - * - * @param viewName The name of the pre ViewPort to look up - * @return The ViewPort, or null if not found. - * - * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) - */ - public ViewPort getPreView(String viewName) { - for (int i = 0; i < preViewPorts.size(); i++) { - if (preViewPorts.get(i).getName().equals(viewName)) { - return preViewPorts.get(i); - } - } - return null; - } - - /** - * Removes the specified pre ViewPort. - * - * @param view The pre ViewPort to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removePreView(ViewPort view) { - return preViewPorts.remove(view); - } - - /** - * Returns the main ViewPort with the given name. - * - * @param viewName The name of the main ViewPort to look up - * @return The ViewPort, or null if not found. - * - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) - */ - public ViewPort getMainView(String viewName) { - for (int i = 0; i < viewPorts.size(); i++) { - if (viewPorts.get(i).getName().equals(viewName)) { - return viewPorts.get(i); - } - } - return null; - } - - /** - * Removes the main ViewPort with the specified name. - * - * @param viewName The main ViewPort name to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removeMainView(String viewName) { - for (int i = 0; i < viewPorts.size(); i++) { - if (viewPorts.get(i).getName().equals(viewName)) { - viewPorts.remove(i); - return true; - } - } - return false; - } - - /** - * Removes the specified main ViewPort. - * - * @param view The main ViewPort to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removeMainView(ViewPort view) { - return viewPorts.remove(view); - } - - /** - * Returns the post ViewPort with the given name. - * - * @param viewName The name of the post ViewPort to look up - * @return The ViewPort, or null if not found. - * - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) - */ - public ViewPort getPostView(String viewName) { - for (int i = 0; i < postViewPorts.size(); i++) { - if (postViewPorts.get(i).getName().equals(viewName)) { - return postViewPorts.get(i); - } - } - return null; - } - - /** - * Removes the post ViewPort with the specified name. - * - * @param viewName The post ViewPort name to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removePostView(String viewName) { - for (int i = 0; i < postViewPorts.size(); i++) { - if (postViewPorts.get(i).getName().equals(viewName)) { - postViewPorts.remove(i); - - return true; - } - } - return false; - } - - /** - * Removes the specified post ViewPort. - * - * @param view The post ViewPort to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removePostView(ViewPort view) { - return postViewPorts.remove(view); - } - - /** - * Returns a read-only list of all pre ViewPorts - * @return a read-only list of all pre ViewPorts - * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) - */ - public List<ViewPort> getPreViews() { - return Collections.unmodifiableList(preViewPorts); - } - - /** - * Returns a read-only list of all main ViewPorts - * @return a read-only list of all main ViewPorts - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) - */ - public List<ViewPort> getMainViews() { - return Collections.unmodifiableList(viewPorts); - } - - /** - * Returns a read-only list of all post ViewPorts - * @return a read-only list of all post ViewPorts - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) - */ - public List<ViewPort> getPostViews() { - return Collections.unmodifiableList(postViewPorts); - } - - /** - * Creates a new pre ViewPort, to display the given camera's content. - * <p> - * The view will be processed before the main and post viewports. - */ - public ViewPort createPreView(String viewName, Camera cam) { - ViewPort vp = new ViewPort(viewName, cam); - preViewPorts.add(vp); - return vp; - } - - /** - * Creates a new main ViewPort, to display the given camera's content. - * <p> - * The view will be processed before the post viewports but after - * the pre viewports. - */ - public ViewPort createMainView(String viewName, Camera cam) { - ViewPort vp = new ViewPort(viewName, cam); - viewPorts.add(vp); - return vp; - } - - /** - * Creates a new post ViewPort, to display the given camera's content. - * <p> - * The view will be processed after the pre and main viewports. - */ - public ViewPort createPostView(String viewName, Camera cam) { - ViewPort vp = new ViewPort(viewName, cam); - postViewPorts.add(vp); - return vp; - } - - private void notifyReshape(ViewPort vp, int w, int h) { - List<SceneProcessor> processors = vp.getProcessors(); - for (SceneProcessor proc : processors) { - if (!proc.isInitialized()) { - proc.initialize(this, vp); - } else { - proc.reshape(vp, w, h); - } - } - } - - /** - * Internal use only. - * Updates the resolution of all on-screen cameras to match - * the given width and height. - */ - public void notifyReshape(int w, int h) { - for (ViewPort vp : preViewPorts) { - if (vp.getOutputFrameBuffer() == null) { - Camera cam = vp.getCamera(); - cam.resize(w, h, true); - } - notifyReshape(vp, w, h); - } - for (ViewPort vp : viewPorts) { - if (vp.getOutputFrameBuffer() == null) { - Camera cam = vp.getCamera(); - cam.resize(w, h, true); - } - notifyReshape(vp, w, h); - } - for (ViewPort vp : postViewPorts) { - if (vp.getOutputFrameBuffer() == null) { - Camera cam = vp.getCamera(); - cam.resize(w, h, true); - } - notifyReshape(vp, w, h); - } - } - - /** - * Internal use only. - * Updates the given list of uniforms with {@link UniformBinding uniform bindings} - * based on the current world state. - */ - public void updateUniformBindings(List<Uniform> params) { - // assums worldMatrix is properly set. - TempVars vars = TempVars.get(); - - Matrix4f tempMat4 = vars.tempMat4; - Matrix3f tempMat3 = vars.tempMat3; - Vector2f tempVec2 = vars.vect2d; - Quaternion tempVec4 = vars.quat1; - - for (int i = 0; i < params.size(); i++) { - Uniform u = params.get(i); - switch (u.getBinding()) { - case WorldMatrix: - u.setValue(VarType.Matrix4, worldMatrix); - break; - case ViewMatrix: - u.setValue(VarType.Matrix4, viewMatrix); - break; - case ProjectionMatrix: - u.setValue(VarType.Matrix4, projMatrix); - break; - case ViewProjectionMatrix: - u.setValue(VarType.Matrix4, viewProjMatrix); - break; - case WorldViewMatrix: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - u.setValue(VarType.Matrix4, tempMat4); - break; - case NormalMatrix: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.toRotationMatrix(tempMat3); - tempMat3.invertLocal(); - tempMat3.transposeLocal(); - u.setValue(VarType.Matrix3, tempMat3); - break; - case WorldViewProjectionMatrix: - tempMat4.set(viewProjMatrix); - tempMat4.multLocal(worldMatrix); - u.setValue(VarType.Matrix4, tempMat4); - break; - case WorldMatrixInverse: - tempMat4.multLocal(worldMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ViewMatrixInverse: - tempMat4.set(viewMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ProjectionMatrixInverse: - tempMat4.set(projMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ViewProjectionMatrixInverse: - tempMat4.set(viewProjMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case WorldViewMatrixInverse: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case NormalMatrixInverse: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.toRotationMatrix(tempMat3); - tempMat3.invertLocal(); - tempMat3.transposeLocal(); - tempMat3.invertLocal(); - u.setValue(VarType.Matrix3, tempMat3); - break; - case WorldViewProjectionMatrixInverse: - tempMat4.set(viewProjMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ViewPort: - tempVec4.set(viewX, viewY, viewWidth, viewHeight); - u.setValue(VarType.Vector4, tempVec4); - break; - case Resolution: - tempVec2.set(viewWidth, viewHeight); - u.setValue(VarType.Vector2, tempVec2); - break; - case Aspect: - float aspect = ((float) viewWidth) / viewHeight; - u.setValue(VarType.Float, aspect); - break; - case FrustumNearFar: - tempVec2.set(near, far); - u.setValue(VarType.Vector2, tempVec2); - break; - case CameraPosition: - u.setValue(VarType.Vector3, camLoc); - break; - case CameraDirection: - u.setValue(VarType.Vector3, camDir); - break; - case CameraLeft: - u.setValue(VarType.Vector3, camLeft); - break; - case CameraUp: - u.setValue(VarType.Vector3, camUp); - break; - case Time: - u.setValue(VarType.Float, timer.getTimeInSeconds()); - break; - case Tpf: - u.setValue(VarType.Float, timer.getTimePerFrame()); - break; - case FrameRate: - u.setValue(VarType.Float, timer.getFrameRate()); - break; - } - } - - vars.release(); - } - - /** - * Set the material to use to render all future objects. - * This overrides the material set on the geometry and renders - * with the provided material instead. - * Use null to clear the material and return renderer to normal - * functionality. - * @param mat The forced material to set, or null to return to normal - */ - public void setForcedMaterial(Material mat) { - forcedMaterial = mat; - } - - /** - * Returns the forced render state previously set with - * {@link #setForcedRenderState(com.jme3.material.RenderState) }. - * @return the forced render state - */ - public RenderState getForcedRenderState() { - return forcedRenderState; - } - - /** - * Set the render state to use for all future objects. - * This overrides the render state set on the material and instead - * forces this render state to be applied for all future materials - * rendered. Set to null to return to normal functionality. - * - * @param forcedRenderState The forced render state to set, or null - * to return to normal - */ - public void setForcedRenderState(RenderState forcedRenderState) { - this.forcedRenderState = forcedRenderState; - } - - /** - * Set the timer that should be used to query the time based - * {@link UniformBinding}s for material world parameters. - * - * @param timer The timer to query time world parameters - */ - public void setTimer(Timer timer) { - this.timer = timer; - } - - /** - * Returns the forced technique name set. - * - * @return the forced technique name set. - * - * @see #setForcedTechnique(java.lang.String) - */ - public String getForcedTechnique() { - return forcedTechnique; - } - - /** - * Sets the forced technique to use when rendering geometries. - * <p> - * If the specified technique name is available on the geometry's - * material, then it is used, otherwise, the - * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used. - * If a forced material is not set and the forced technique name cannot - * be found on the material, the geometry will <em>not</em> be rendered. - * - * @param forcedTechnique The forced technique name to use, set to null - * to return to normal functionality. - * - * @see #renderGeometry(com.jme3.scene.Geometry) - */ - public void setForcedTechnique(String forcedTechnique) { - this.forcedTechnique = forcedTechnique; - } - - /** - * Enable or disable alpha-to-coverage. - * <p> - * When alpha to coverage is enabled and the renderer implementation - * supports it, then alpha blending will be replaced with alpha dissolve - * if multi-sampling is also set on the renderer. - * This feature allows avoiding of alpha blending artifacts due to - * lack of triangle-level back-to-front sorting. - * - * @param value True to enable alpha-to-coverage, false otherwise. - */ - public void setAlphaToCoverage(boolean value) { - renderer.setAlphaToCoverage(value); - } - - /** - * True if the translucent bucket should automatically be rendered - * by the RenderManager. - * - * @return Whether or not the translucent bucket is rendered. - * - * @see #setHandleTranslucentBucket(boolean) - */ - public boolean isHandleTranslucentBucket() { - return handleTranlucentBucket; - } - - /** - * Enable or disable rendering of the - * {@link Bucket#Translucent translucent bucket} - * by the RenderManager. The default is enabled. - * - * @param handleTranslucentBucket Whether or not the translucent bucket should - * be rendered. - */ - public void setHandleTranslucentBucket(boolean handleTranslucentBucket) { - this.handleTranlucentBucket = handleTranslucentBucket; - } - - /** - * Internal use only. Sets the world matrix to use for future - * rendering. This has no effect unless objects are rendered manually - * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }. - * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will - * override this value. - * - * @param mat The world matrix to set - */ - public void setWorldMatrix(Matrix4f mat) { - if (shader) { - worldMatrix.set(mat); - } else { - renderer.setWorldMatrix(mat); - } - } - - /** - * Renders the given geometry. - * <p> - * First the proper world matrix is set, if - * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform} - * feature is enabled, the identity world matrix is used, otherwise, the - * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used. - * <p> - * Once the world matrix is applied, the proper material is chosen for rendering. - * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is - * set on this RenderManager, then it is used for rendering the geometry, - * otherwise, the {@link Geometry#getMaterial() geometry's material} is used. - * <p> - * If a {@link #setForcedTechnique(java.lang.String) forced technique} is - * set on this RenderManager, then it is selected automatically - * on the geometry's material and is used for rendering. Otherwise, one - * of the {@link MaterialDef#getDefaultTechniques() default techniques} is - * used. - * <p> - * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced - * render state} is set on this RenderManager, then it is used - * for rendering the material, and the material's own render state is ignored. - * Otherwise, the material's render state is used as intended. - * - * @param g The geometry to render - * - * @see Technique - * @see RenderState - * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) - * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) - */ - public void renderGeometry(Geometry g) { - if (g.isIgnoreTransform()) { - setWorldMatrix(Matrix4f.IDENTITY); - } else { - setWorldMatrix(g.getWorldMatrix()); - } - - //if forcedTechnique we try to force it for render, - //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null - //else the geom is not rendered - if (forcedTechnique != null) { - if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) { - tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default"; - g.getMaterial().selectTechnique(forcedTechnique, this); - // use geometry's material - g.getMaterial().render(g, this); - g.getMaterial().selectTechnique(tmpTech, this); - //Reverted this part from revision 6197 - //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered - } else if (forcedMaterial != null) { - // use forced material - forcedMaterial.render(g, this); - } - } else if (forcedMaterial != null) { - // use forced material - forcedMaterial.render(g, this); - } else { - g.getMaterial().render(g, this); - } - } - - /** - * Renders the given GeometryList. - * <p> - * For every geometry in the list, the - * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called. - * - * @param gl The geometry list to render. - * - * @see GeometryList - * @see #renderGeometry(com.jme3.scene.Geometry) - */ - public void renderGeometryList(GeometryList gl) { - for (int i = 0; i < gl.size(); i++) { - renderGeometry(gl.get(i)); - } - } - - /** - * If a spatial is not inside the eye frustum, it - * is still rendered in the shadow frustum (shadow casting queue) - * through this recursive method. - */ - private void renderShadow(Spatial s, RenderQueue rq) { - if (s instanceof Node) { - Node n = (Node) s; - List<Spatial> children = n.getChildren(); - for (int i = 0; i < children.size(); i++) { - renderShadow(children.get(i), rq); - } - } else if (s instanceof Geometry) { - Geometry gm = (Geometry) s; - - RenderQueue.ShadowMode shadowMode = s.getShadowMode(); - if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) { - //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue - rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast); - } - } - } - - /** - * Preloads a scene for rendering. - * <p> - * After invocation of this method, the underlying - * renderer would have uploaded any textures, shaders and meshes - * used by the given scene to the video driver. - * Using this method is useful when wishing to avoid the initial pause - * when rendering a scene for the first time. Note that it is not - * guaranteed that the underlying renderer will actually choose to upload - * the data to the GPU so some pause is still to be expected. - * - * @param scene The scene to preload - */ - public void preloadScene(Spatial scene) { - if (scene instanceof Node) { - // recurse for all children - Node n = (Node) scene; - List<Spatial> children = n.getChildren(); - for (int i = 0; i < children.size(); i++) { - preloadScene(children.get(i)); - } - } else if (scene instanceof Geometry) { - // add to the render queue - Geometry gm = (Geometry) scene; - if (gm.getMaterial() == null) { - throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); - } - - gm.getMaterial().preload(this); - Mesh mesh = gm.getMesh(); - if (mesh != null) { - for (Entry<VertexBuffer> entry : mesh.getBuffers()) { - VertexBuffer buf = entry.getValue(); - if (buf.getData() != null) { - renderer.updateBufferData(buf); - } - } - } - } - } - - /** - * Flattens the given scene graph into the ViewPort's RenderQueue, - * checking for culling as the call goes down the graph recursively. - * <p> - * First, the scene is checked for culling based on the <code>Spatial</code>s - * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint}, - * if the camera frustum contains the scene, then this method is recursively - * called on its children. - * <p> - * When the scene's leaves or {@link Geometry geometries} are reached, - * they are each enqueued into the - * {@link ViewPort#getQueue() ViewPort's render queue}. - * <p> - * In addition to enqueuing the visible geometries, this method - * also scenes which cast or receive shadows, by putting them into the - * RenderQueue's - * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode) - * shadow queue}. Each Spatial which has its - * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode} - * set to not off, will be put into the appropriate shadow queue, note that - * this process does not check for frustum culling on any - * {@link ShadowMode#Cast shadow casters}, as they don't have to be - * in the eye camera frustum to cast shadows on objects that are inside it. - * - * @param scene The scene to flatten into the queue - * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera} - * used for culling and the {@link ViewPort#getQueue() queue} used to - * contain the flattened scene graph. - */ - public void renderScene(Spatial scene, ViewPort vp) { - if (scene.getParent() == null) { - vp.getCamera().setPlaneState(0); - } - // check culling first. - if (!scene.checkCulling(vp.getCamera())) { - // move on to shadow-only render - if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) { - renderShadow(scene, vp.getQueue()); - } - return; - } - - scene.runControlRender(this, vp); - if (scene instanceof Node) { - // recurse for all children - Node n = (Node) scene; - List<Spatial> children = n.getChildren(); - //saving cam state for culling - int camState = vp.getCamera().getPlaneState(); - for (int i = 0; i < children.size(); i++) { - //restoring cam state before proceeding children recusively - vp.getCamera().setPlaneState(camState); - renderScene(children.get(i), vp); - - } - } else if (scene instanceof Geometry) { - - // add to the render queue - Geometry gm = (Geometry) scene; - if (gm.getMaterial() == null) { - throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); - } - - vp.getQueue().addToQueue(gm, scene.getQueueBucket()); - - // add to shadow queue if needed - RenderQueue.ShadowMode shadowMode = scene.getShadowMode(); - if (shadowMode != RenderQueue.ShadowMode.Off) { - vp.getQueue().addToShadowQueue(gm, shadowMode); - } - } - } - - /** - * Returns the camera currently used for rendering. - * <p> - * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }. - * - * @return the camera currently used for rendering. - */ - public Camera getCurrentCamera() { - return prevCam; - } - - /** - * The renderer implementation used for rendering operations. - * - * @return The renderer implementation - * - * @see #RenderManager(com.jme3.renderer.Renderer) - * @see Renderer - */ - public Renderer getRenderer() { - return renderer; - } - - /** - * Flushes the ViewPort's {@link ViewPort#getQueue() render queue} - * by rendering each of its visible buckets. - * By default the queues will automatically be cleared after rendering, - * so there's no need to clear them manually. - * - * @param vp The ViewPort of which the queue will be flushed - * - * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera) - * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList) - */ - public void flushQueue(ViewPort vp) { - renderViewPortQueues(vp, true); - } - - /** - * Clears the queue of the given ViewPort. - * Simply calls {@link RenderQueue#clear() } on the ViewPort's - * {@link ViewPort#getQueue() render queue}. - * - * @param vp The ViewPort of which the queue will be cleared. - * - * @see RenderQueue#clear() - * @see ViewPort#getQueue() - */ - public void clearQueue(ViewPort vp) { - vp.getQueue().clear(); - } - - /** - * Render the given viewport queues. - * <p> - * Changes the {@link Renderer#setDepthRange(float, float) depth range} - * appropriately as expected by each queue and then calls - * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) } - * on the queue. Makes sure to restore the depth range to [0, 1] - * at the end of the call. - * Note that the {@link Bucket#Translucent translucent bucket} is NOT - * rendered by this method. Instead the user should call - * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) } - * after this call. - * - * @param vp the viewport of which queue should be rendered - * @param flush If true, the queues will be cleared after - * rendering. - * - * @see RenderQueue - * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort) - */ - public void renderViewPortQueues(ViewPort vp, boolean flush) { - RenderQueue rq = vp.getQueue(); - Camera cam = vp.getCamera(); - boolean depthRangeChanged = false; - - // render opaque objects with default depth range - // opaque objects are sorted front-to-back, reducing overdraw - rq.renderQueue(Bucket.Opaque, this, cam, flush); - - // render the sky, with depth range set to the farthest - if (!rq.isQueueEmpty(Bucket.Sky)) { - renderer.setDepthRange(1, 1); - rq.renderQueue(Bucket.Sky, this, cam, flush); - depthRangeChanged = true; - } - - - // transparent objects are last because they require blending with the - // rest of the scene's objects. Consequently, they are sorted - // back-to-front. - if (!rq.isQueueEmpty(Bucket.Transparent)) { - if (depthRangeChanged) { - renderer.setDepthRange(0, 1); - depthRangeChanged = false; - } - - rq.renderQueue(Bucket.Transparent, this, cam, flush); - } - - if (!rq.isQueueEmpty(Bucket.Gui)) { - renderer.setDepthRange(0, 0); - setCamera(cam, true); - rq.renderQueue(Bucket.Gui, this, cam, flush); - setCamera(cam, false); - depthRangeChanged = true; - } - - // restore range to default - if (depthRangeChanged) { - renderer.setDepthRange(0, 1); - } - } - - /** - * Renders the {@link Bucket#Translucent translucent queue} on the viewPort. - * <p> - * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) } - * is set to true. This method clears the translucent queue after rendering - * it. - * - * @param vp The viewport of which the translucent queue should be rendered. - * - * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) - * @see #setHandleTranslucentBucket(boolean) - */ - public void renderTranslucentQueue(ViewPort vp) { - RenderQueue rq = vp.getQueue(); - if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) { - rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true); - } - } - - private void setViewPort(Camera cam) { - // this will make sure to update viewport only if needed - if (cam != prevCam || cam.isViewportChanged()) { - viewX = (int) (cam.getViewPortLeft() * cam.getWidth()); - viewY = (int) (cam.getViewPortBottom() * cam.getHeight()); - viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth()); - viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight()); - renderer.setViewPort(viewX, viewY, viewWidth, viewHeight); - renderer.setClipRect(viewX, viewY, viewWidth, viewHeight); - cam.clearViewportChanged(); - prevCam = cam; - -// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX); -// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY); -// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX); -// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY); -// -// orthoMatrix.loadIdentity(); -// orthoMatrix.setTranslation(translateX, translateY, 0); -// orthoMatrix.setScale(scaleX, scaleY, 0); - - orthoMatrix.loadIdentity(); - orthoMatrix.setTranslation(-1f, -1f, 0f); - orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f); - } - } - - private void setViewProjection(Camera cam, boolean ortho) { - if (shader) { - if (ortho) { - viewMatrix.set(Matrix4f.IDENTITY); - projMatrix.set(orthoMatrix); - viewProjMatrix.set(orthoMatrix); - } else { - viewMatrix.set(cam.getViewMatrix()); - projMatrix.set(cam.getProjectionMatrix()); - viewProjMatrix.set(cam.getViewProjectionMatrix()); - } - - camLoc.set(cam.getLocation()); - cam.getLeft(camLeft); - cam.getUp(camUp); - cam.getDirection(camDir); - - near = cam.getFrustumNear(); - far = cam.getFrustumFar(); - } else { - if (ortho) { - renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix); - } else { - renderer.setViewProjectionMatrices(cam.getViewMatrix(), - cam.getProjectionMatrix()); - } - - } - } - - /** - * Set the camera to use for rendering. - * <p> - * First, the camera's - * {@link Camera#setViewPort(float, float, float, float) view port parameters} - * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and - * {@link Camera#getProjectionMatrix() projection} matrices are set - * on the renderer. If <code>ortho</code> is <code>true</code>, then - * instead of using the camera's view and projection matrices, an ortho - * matrix is computed and used instead of the view projection matrix. - * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1) - * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1). - * - * @param cam The camera to set - * @param ortho True if to use orthographic projection (for GUI rendering), - * false if to use the camera's view and projection matrices. - */ - public void setCamera(Camera cam, boolean ortho) { - setViewPort(cam); - setViewProjection(cam, ortho); - } - - /** - * Draws the viewport but without notifying {@link SceneProcessor scene - * processors} of any rendering events. - * - * @param vp The ViewPort to render - * - * @see #renderViewPort(com.jme3.renderer.ViewPort, float) - */ - public void renderViewPortRaw(ViewPort vp) { - setCamera(vp.getCamera(), false); - List<Spatial> scenes = vp.getScenes(); - for (int i = scenes.size() - 1; i >= 0; i--) { - renderScene(scenes.get(i), vp); - } - flushQueue(vp); - } - - /** - * Renders the {@link ViewPort}. - * <p> - * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method - * returns immediately. Otherwise, the ViewPort is rendered by - * the following process:<br> - * <ul> - * <li>All {@link SceneProcessor scene processors} that are attached - * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}. - * </li> - * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method - * is called.</li> - * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer} - * is set on the Renderer</li> - * <li>The camera is set on the renderer, including its view port parameters. - * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li> - * <li>Any buffers that the ViewPort requests to be cleared are cleared - * and the {@link ViewPort#getBackgroundColor() background color} is set</li> - * <li>Every scene that is attached to the ViewPort is flattened into - * the ViewPort's render queue - * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) }) - * </li> - * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) } - * method is called.</li> - * <li>The render queue is sorted and then flushed, sending - * rendering commands to the underlying Renderer implementation. - * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li> - * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) } - * method is called.</li> - * <li>The translucent queue of the ViewPort is sorted and then flushed - * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li> - * <li>If any objects remained in the render queue, they are removed - * from the queue. This is generally objects added to the - * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) - * shadow queue} - * which were not rendered because of a missing shadow renderer.</li> - * </ul> - * - * @param vp - * @param tpf - */ - public void renderViewPort(ViewPort vp, float tpf) { - if (!vp.isEnabled()) { - return; - } - List<SceneProcessor> processors = vp.getProcessors(); - if (processors.isEmpty()) { - processors = null; - } - - if (processors != null) { - for (SceneProcessor proc : processors) { - if (!proc.isInitialized()) { - proc.initialize(this, vp); - } - proc.preFrame(tpf); - } - } - - renderer.setFrameBuffer(vp.getOutputFrameBuffer()); - setCamera(vp.getCamera(), false); - if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) { - if (vp.isClearColor()) { - renderer.setBackgroundColor(vp.getBackgroundColor()); - } - renderer.clearBuffers(vp.isClearColor(), - vp.isClearDepth(), - vp.isClearStencil()); - } - - List<Spatial> scenes = vp.getScenes(); - for (int i = scenes.size() - 1; i >= 0; i--) { - renderScene(scenes.get(i), vp); - } - - if (processors != null) { - for (SceneProcessor proc : processors) { - proc.postQueue(vp.getQueue()); - } - } - - flushQueue(vp); - - if (processors != null) { - for (SceneProcessor proc : processors) { - proc.postFrame(vp.getOutputFrameBuffer()); - } - } - //renders the translucent objects queue after processors have been rendered - renderTranslucentQueue(vp); - // clear any remaining spatials that were not rendered. - clearQueue(vp); - } - - /** - * Called by the application to render any ViewPorts - * added to this RenderManager. - * <p> - * Renders any viewports that were added using the following methods: - * <ul> - * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li> - * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li> - * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li> - * </ul> - * - * @param tpf Time per frame value - */ - public void render(float tpf, boolean mainFrameBufferActive) { - if (renderer instanceof NullRenderer) { - return; - } - - this.shader = renderer.getCaps().contains(Caps.GLSL100); - - for (int i = 0; i < preViewPorts.size(); i++) { - ViewPort vp = preViewPorts.get(i); - if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ - renderViewPort(vp, tpf); - } - } - for (int i = 0; i < viewPorts.size(); i++) { - ViewPort vp = viewPorts.get(i); - if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ - renderViewPort(vp, tpf); - } - } - for (int i = 0; i < postViewPorts.size(); i++) { - ViewPort vp = postViewPorts.get(i); - if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ - renderViewPort(vp, tpf); - } - } - } -} +/*
+ * 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.renderer;
+
+import com.jme3.material.Material;
+import com.jme3.material.MaterialDef;
+import com.jme3.material.RenderState;
+import com.jme3.material.Technique;
+import com.jme3.math.*;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.*;
+import com.jme3.shader.Uniform;
+import com.jme3.shader.UniformBinding;
+import com.jme3.shader.VarType;
+import com.jme3.system.NullRenderer;
+import com.jme3.system.Timer;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.TempVars;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * <code>RenderManager</code> is a high-level rendering interface that is
+ * above the Renderer implementation. RenderManager takes care
+ * of rendering the scene graphs attached to each viewport and
+ * handling SceneProcessors.
+ *
+ * @see SceneProcessor
+ * @see ViewPort
+ * @see Spatial
+ */
+public class RenderManager {
+
+ private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
+
+ private Renderer renderer;
+ private Timer timer;
+ private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
+ private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
+ private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
+ private Camera prevCam = null;
+ private Material forcedMaterial = null;
+ private String forcedTechnique = null;
+ private RenderState forcedRenderState = null;
+ private boolean shader;
+ private int viewX, viewY, viewWidth, viewHeight;
+ private float near, far;
+ private Matrix4f orthoMatrix = new Matrix4f();
+ private Matrix4f viewMatrix = new Matrix4f();
+ private Matrix4f projMatrix = new Matrix4f();
+ private Matrix4f viewProjMatrix = new Matrix4f();
+ private Matrix4f worldMatrix = new Matrix4f();
+ private Vector3f camUp = new Vector3f(),
+ camLeft = new Vector3f(),
+ camDir = new Vector3f(),
+ camLoc = new Vector3f();
+ //temp technique
+ private String tmpTech;
+ private boolean handleTranlucentBucket = true;
+
+ /**
+ * Create a high-level rendering interface over the
+ * low-level rendering interface.
+ * @param renderer
+ */
+ public RenderManager(Renderer renderer) {
+ this.renderer = renderer;
+ //this.shader = renderer.getCaps().contains(Caps.GLSL100);
+ }
+
+ /**
+ * Returns the pre ViewPort with the given name.
+ *
+ * @param viewName The name of the pre ViewPort to look up
+ * @return The ViewPort, or null if not found.
+ *
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public ViewPort getPreView(String viewName) {
+ for (int i = 0; i < preViewPorts.size(); i++) {
+ if (preViewPorts.get(i).getName().equals(viewName)) {
+ return preViewPorts.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the specified pre ViewPort.
+ *
+ * @param view The pre ViewPort to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removePreView(ViewPort view) {
+ return preViewPorts.remove(view);
+ }
+
+ /**
+ * Returns the main ViewPort with the given name.
+ *
+ * @param viewName The name of the main ViewPort to look up
+ * @return The ViewPort, or null if not found.
+ *
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public ViewPort getMainView(String viewName) {
+ for (int i = 0; i < viewPorts.size(); i++) {
+ if (viewPorts.get(i).getName().equals(viewName)) {
+ return viewPorts.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the main ViewPort with the specified name.
+ *
+ * @param viewName The main ViewPort name to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removeMainView(String viewName) {
+ for (int i = 0; i < viewPorts.size(); i++) {
+ if (viewPorts.get(i).getName().equals(viewName)) {
+ viewPorts.remove(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the specified main ViewPort.
+ *
+ * @param view The main ViewPort to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removeMainView(ViewPort view) {
+ return viewPorts.remove(view);
+ }
+
+ /**
+ * Returns the post ViewPort with the given name.
+ *
+ * @param viewName The name of the post ViewPort to look up
+ * @return The ViewPort, or null if not found.
+ *
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public ViewPort getPostView(String viewName) {
+ for (int i = 0; i < postViewPorts.size(); i++) {
+ if (postViewPorts.get(i).getName().equals(viewName)) {
+ return postViewPorts.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the post ViewPort with the specified name.
+ *
+ * @param viewName The post ViewPort name to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removePostView(String viewName) {
+ for (int i = 0; i < postViewPorts.size(); i++) {
+ if (postViewPorts.get(i).getName().equals(viewName)) {
+ postViewPorts.remove(i);
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the specified post ViewPort.
+ *
+ * @param view The post ViewPort to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removePostView(ViewPort view) {
+ return postViewPorts.remove(view);
+ }
+
+ /**
+ * Returns a read-only list of all pre ViewPorts
+ * @return a read-only list of all pre ViewPorts
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public List<ViewPort> getPreViews() {
+ return Collections.unmodifiableList(preViewPorts);
+ }
+
+ /**
+ * Returns a read-only list of all main ViewPorts
+ * @return a read-only list of all main ViewPorts
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public List<ViewPort> getMainViews() {
+ return Collections.unmodifiableList(viewPorts);
+ }
+
+ /**
+ * Returns a read-only list of all post ViewPorts
+ * @return a read-only list of all post ViewPorts
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public List<ViewPort> getPostViews() {
+ return Collections.unmodifiableList(postViewPorts);
+ }
+
+ /**
+ * Creates a new pre ViewPort, to display the given camera's content.
+ * <p>
+ * The view will be processed before the main and post viewports.
+ */
+ public ViewPort createPreView(String viewName, Camera cam) {
+ ViewPort vp = new ViewPort(viewName, cam);
+ preViewPorts.add(vp);
+ return vp;
+ }
+
+ /**
+ * Creates a new main ViewPort, to display the given camera's content.
+ * <p>
+ * The view will be processed before the post viewports but after
+ * the pre viewports.
+ */
+ public ViewPort createMainView(String viewName, Camera cam) {
+ ViewPort vp = new ViewPort(viewName, cam);
+ viewPorts.add(vp);
+ return vp;
+ }
+
+ /**
+ * Creates a new post ViewPort, to display the given camera's content.
+ * <p>
+ * The view will be processed after the pre and main viewports.
+ */
+ public ViewPort createPostView(String viewName, Camera cam) {
+ ViewPort vp = new ViewPort(viewName, cam);
+ postViewPorts.add(vp);
+ return vp;
+ }
+
+ private void notifyReshape(ViewPort vp, int w, int h) {
+ List<SceneProcessor> processors = vp.getProcessors();
+ for (SceneProcessor proc : processors) {
+ if (!proc.isInitialized()) {
+ proc.initialize(this, vp);
+ } else {
+ proc.reshape(vp, w, h);
+ }
+ }
+ }
+
+ /**
+ * Internal use only.
+ * Updates the resolution of all on-screen cameras to match
+ * the given width and height.
+ */
+ public void notifyReshape(int w, int h) {
+ for (ViewPort vp : preViewPorts) {
+ if (vp.getOutputFrameBuffer() == null) {
+ Camera cam = vp.getCamera();
+ cam.resize(w, h, true);
+ }
+ notifyReshape(vp, w, h);
+ }
+ for (ViewPort vp : viewPorts) {
+ if (vp.getOutputFrameBuffer() == null) {
+ Camera cam = vp.getCamera();
+ cam.resize(w, h, true);
+ }
+ notifyReshape(vp, w, h);
+ }
+ for (ViewPort vp : postViewPorts) {
+ if (vp.getOutputFrameBuffer() == null) {
+ Camera cam = vp.getCamera();
+ cam.resize(w, h, true);
+ }
+ notifyReshape(vp, w, h);
+ }
+ }
+
+ /**
+ * Internal use only.
+ * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
+ * based on the current world state.
+ */
+ public void updateUniformBindings(List<Uniform> params) {
+ // assums worldMatrix is properly set.
+ TempVars vars = TempVars.get();
+
+ Matrix4f tempMat4 = vars.tempMat4;
+ Matrix3f tempMat3 = vars.tempMat3;
+ Vector2f tempVec2 = vars.vect2d;
+ Quaternion tempVec4 = vars.quat1;
+
+ for (int i = 0; i < params.size(); i++) {
+ Uniform u = params.get(i);
+ switch (u.getBinding()) {
+ case WorldMatrix:
+ u.setValue(VarType.Matrix4, worldMatrix);
+ break;
+ case ViewMatrix:
+ u.setValue(VarType.Matrix4, viewMatrix);
+ break;
+ case ProjectionMatrix:
+ u.setValue(VarType.Matrix4, projMatrix);
+ break;
+ case ViewProjectionMatrix:
+ u.setValue(VarType.Matrix4, viewProjMatrix);
+ break;
+ case WorldViewMatrix:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case NormalMatrix:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.toRotationMatrix(tempMat3);
+ tempMat3.invertLocal();
+ tempMat3.transposeLocal();
+ u.setValue(VarType.Matrix3, tempMat3);
+ break;
+ case WorldViewProjectionMatrix:
+ tempMat4.set(viewProjMatrix);
+ tempMat4.multLocal(worldMatrix);
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case WorldMatrixInverse:
+ tempMat4.set(worldMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case WorldMatrixInverseTranspose:
+ worldMatrix.toRotationMatrix(tempMat3);
+ tempMat3.invertLocal().transposeLocal();
+ u.setValue(VarType.Matrix3, tempMat3);
+ break;
+ case ViewMatrixInverse:
+ tempMat4.set(viewMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ProjectionMatrixInverse:
+ tempMat4.set(projMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ViewProjectionMatrixInverse:
+ tempMat4.set(viewProjMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case WorldViewMatrixInverse:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case NormalMatrixInverse:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.toRotationMatrix(tempMat3);
+ tempMat3.invertLocal();
+ tempMat3.transposeLocal();
+ tempMat3.invertLocal();
+ u.setValue(VarType.Matrix3, tempMat3);
+ break;
+ case WorldViewProjectionMatrixInverse:
+ tempMat4.set(viewProjMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ViewPort:
+ tempVec4.set(viewX, viewY, viewWidth, viewHeight);
+ u.setValue(VarType.Vector4, tempVec4);
+ break;
+ case Resolution:
+ tempVec2.set(viewWidth, viewHeight);
+ u.setValue(VarType.Vector2, tempVec2);
+ break;
+ case ResolutionInverse:
+ tempVec2.set(1f / viewWidth, 1f / viewHeight);
+ u.setValue(VarType.Vector2, tempVec2);
+ break;
+ case Aspect:
+ float aspect = ((float) viewWidth) / viewHeight;
+ u.setValue(VarType.Float, aspect);
+ break;
+ case FrustumNearFar:
+ tempVec2.set(near, far);
+ u.setValue(VarType.Vector2, tempVec2);
+ break;
+ case CameraPosition:
+ u.setValue(VarType.Vector3, camLoc);
+ break;
+ case CameraDirection:
+ u.setValue(VarType.Vector3, camDir);
+ break;
+ case CameraLeft:
+ u.setValue(VarType.Vector3, camLeft);
+ break;
+ case CameraUp:
+ u.setValue(VarType.Vector3, camUp);
+ break;
+ case Time:
+ u.setValue(VarType.Float, timer.getTimeInSeconds());
+ break;
+ case Tpf:
+ u.setValue(VarType.Float, timer.getTimePerFrame());
+ break;
+ case FrameRate:
+ u.setValue(VarType.Float, timer.getFrameRate());
+ break;
+ }
+ }
+
+ vars.release();
+ }
+
+ /**
+ * Set the material to use to render all future objects.
+ * This overrides the material set on the geometry and renders
+ * with the provided material instead.
+ * Use null to clear the material and return renderer to normal
+ * functionality.
+ * @param mat The forced material to set, or null to return to normal
+ */
+ public void setForcedMaterial(Material mat) {
+ forcedMaterial = mat;
+ }
+
+ /**
+ * Returns the forced render state previously set with
+ * {@link #setForcedRenderState(com.jme3.material.RenderState) }.
+ * @return the forced render state
+ */
+ public RenderState getForcedRenderState() {
+ return forcedRenderState;
+ }
+
+ /**
+ * Set the render state to use for all future objects.
+ * This overrides the render state set on the material and instead
+ * forces this render state to be applied for all future materials
+ * rendered. Set to null to return to normal functionality.
+ *
+ * @param forcedRenderState The forced render state to set, or null
+ * to return to normal
+ */
+ public void setForcedRenderState(RenderState forcedRenderState) {
+ this.forcedRenderState = forcedRenderState;
+ }
+
+ /**
+ * Set the timer that should be used to query the time based
+ * {@link UniformBinding}s for material world parameters.
+ *
+ * @param timer The timer to query time world parameters
+ */
+ public void setTimer(Timer timer) {
+ this.timer = timer;
+ }
+
+ /**
+ * Returns the forced technique name set.
+ *
+ * @return the forced technique name set.
+ *
+ * @see #setForcedTechnique(java.lang.String)
+ */
+ public String getForcedTechnique() {
+ return forcedTechnique;
+ }
+
+ /**
+ * Sets the forced technique to use when rendering geometries.
+ * <p>
+ * If the specified technique name is available on the geometry's
+ * material, then it is used, otherwise, the
+ * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used.
+ * If a forced material is not set and the forced technique name cannot
+ * be found on the material, the geometry will <em>not</em> be rendered.
+ *
+ * @param forcedTechnique The forced technique name to use, set to null
+ * to return to normal functionality.
+ *
+ * @see #renderGeometry(com.jme3.scene.Geometry)
+ */
+ public void setForcedTechnique(String forcedTechnique) {
+ this.forcedTechnique = forcedTechnique;
+ }
+
+ /**
+ * Enable or disable alpha-to-coverage.
+ * <p>
+ * When alpha to coverage is enabled and the renderer implementation
+ * supports it, then alpha blending will be replaced with alpha dissolve
+ * if multi-sampling is also set on the renderer.
+ * This feature allows avoiding of alpha blending artifacts due to
+ * lack of triangle-level back-to-front sorting.
+ *
+ * @param value True to enable alpha-to-coverage, false otherwise.
+ */
+ public void setAlphaToCoverage(boolean value) {
+ renderer.setAlphaToCoverage(value);
+ }
+
+ /**
+ * True if the translucent bucket should automatically be rendered
+ * by the RenderManager.
+ *
+ * @return Whether or not the translucent bucket is rendered.
+ *
+ * @see #setHandleTranslucentBucket(boolean)
+ */
+ public boolean isHandleTranslucentBucket() {
+ return handleTranlucentBucket;
+ }
+
+ /**
+ * Enable or disable rendering of the
+ * {@link Bucket#Translucent translucent bucket}
+ * by the RenderManager. The default is enabled.
+ *
+ * @param handleTranslucentBucket Whether or not the translucent bucket should
+ * be rendered.
+ */
+ public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {
+ this.handleTranlucentBucket = handleTranslucentBucket;
+ }
+
+ /**
+ * Internal use only. Sets the world matrix to use for future
+ * rendering. This has no effect unless objects are rendered manually
+ * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }.
+ * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will
+ * override this value.
+ *
+ * @param mat The world matrix to set
+ */
+ public void setWorldMatrix(Matrix4f mat) {
+ if (shader) {
+ worldMatrix.set(mat);
+ } else {
+ renderer.setWorldMatrix(mat);
+ }
+ }
+
+ /**
+ * Renders the given geometry.
+ * <p>
+ * First the proper world matrix is set, if
+ * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform}
+ * feature is enabled, the identity world matrix is used, otherwise, the
+ * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used.
+ * <p>
+ * Once the world matrix is applied, the proper material is chosen for rendering.
+ * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is
+ * set on this RenderManager, then it is used for rendering the geometry,
+ * otherwise, the {@link Geometry#getMaterial() geometry's material} is used.
+ * <p>
+ * If a {@link #setForcedTechnique(java.lang.String) forced technique} is
+ * set on this RenderManager, then it is selected automatically
+ * on the geometry's material and is used for rendering. Otherwise, one
+ * of the {@link MaterialDef#getDefaultTechniques() default techniques} is
+ * used.
+ * <p>
+ * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced
+ * render state} is set on this RenderManager, then it is used
+ * for rendering the material, and the material's own render state is ignored.
+ * Otherwise, the material's render state is used as intended.
+ *
+ * @param g The geometry to render
+ *
+ * @see Technique
+ * @see RenderState
+ * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
+ * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
+ */
+ public void renderGeometry(Geometry g) {
+ if (g.isIgnoreTransform()) {
+ setWorldMatrix(Matrix4f.IDENTITY);
+ } else {
+ setWorldMatrix(g.getWorldMatrix());
+ }
+
+ //if forcedTechnique we try to force it for render,
+ //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
+ //else the geom is not rendered
+ if (forcedTechnique != null) {
+ if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
+ tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
+ g.getMaterial().selectTechnique(forcedTechnique, this);
+ // use geometry's material
+ g.getMaterial().render(g, this);
+ g.getMaterial().selectTechnique(tmpTech, this);
+ //Reverted this part from revision 6197
+ //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered
+ } else if (forcedMaterial != null) {
+ // use forced material
+ forcedMaterial.render(g, this);
+ }
+ } else if (forcedMaterial != null) {
+ // use forced material
+ forcedMaterial.render(g, this);
+ } else {
+ g.getMaterial().render(g, this);
+ }
+ }
+
+ /**
+ * Renders the given GeometryList.
+ * <p>
+ * For every geometry in the list, the
+ * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.
+ *
+ * @param gl The geometry list to render.
+ *
+ * @see GeometryList
+ * @see #renderGeometry(com.jme3.scene.Geometry)
+ */
+ public void renderGeometryList(GeometryList gl) {
+ for (int i = 0; i < gl.size(); i++) {
+ renderGeometry(gl.get(i));
+ }
+ }
+
+ /**
+ * If a spatial is not inside the eye frustum, it
+ * is still rendered in the shadow frustum (shadow casting queue)
+ * through this recursive method.
+ */
+ private void renderShadow(Spatial s, RenderQueue rq) {
+ if (s instanceof Node) {
+ Node n = (Node) s;
+ List<Spatial> children = n.getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ renderShadow(children.get(i), rq);
+ }
+ } else if (s instanceof Geometry) {
+ Geometry gm = (Geometry) s;
+
+ RenderQueue.ShadowMode shadowMode = s.getShadowMode();
+ if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
+ //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
+ rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
+ }
+ }
+ }
+
+ /**
+ * Preloads a scene for rendering.
+ * <p>
+ * After invocation of this method, the underlying
+ * renderer would have uploaded any textures, shaders and meshes
+ * used by the given scene to the video driver.
+ * Using this method is useful when wishing to avoid the initial pause
+ * when rendering a scene for the first time. Note that it is not
+ * guaranteed that the underlying renderer will actually choose to upload
+ * the data to the GPU so some pause is still to be expected.
+ *
+ * @param scene The scene to preload
+ */
+ public void preloadScene(Spatial scene) {
+ if (scene instanceof Node) {
+ // recurse for all children
+ Node n = (Node) scene;
+ List<Spatial> children = n.getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ preloadScene(children.get(i));
+ }
+ } else if (scene instanceof Geometry) {
+ // add to the render queue
+ Geometry gm = (Geometry) scene;
+ if (gm.getMaterial() == null) {
+ throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
+ }
+
+ gm.getMaterial().preload(this);
+ Mesh mesh = gm.getMesh();
+ if (mesh != null) {
+ for (VertexBuffer vb : mesh.getBufferList().getArray()) {
+ if (vb.getData() != null) {
+ renderer.updateBufferData(vb);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Flattens the given scene graph into the ViewPort's RenderQueue,
+ * checking for culling as the call goes down the graph recursively.
+ * <p>
+ * First, the scene is checked for culling based on the <code>Spatial</code>s
+ * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},
+ * if the camera frustum contains the scene, then this method is recursively
+ * called on its children.
+ * <p>
+ * When the scene's leaves or {@link Geometry geometries} are reached,
+ * they are each enqueued into the
+ * {@link ViewPort#getQueue() ViewPort's render queue}.
+ * <p>
+ * In addition to enqueuing the visible geometries, this method
+ * also scenes which cast or receive shadows, by putting them into the
+ * RenderQueue's
+ * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
+ * shadow queue}. Each Spatial which has its
+ * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
+ * set to not off, will be put into the appropriate shadow queue, note that
+ * this process does not check for frustum culling on any
+ * {@link ShadowMode#Cast shadow casters}, as they don't have to be
+ * in the eye camera frustum to cast shadows on objects that are inside it.
+ *
+ * @param scene The scene to flatten into the queue
+ * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}
+ * used for culling and the {@link ViewPort#getQueue() queue} used to
+ * contain the flattened scene graph.
+ */
+ public void renderScene(Spatial scene, ViewPort vp) {
+ if (scene.getParent() == null) {
+ vp.getCamera().setPlaneState(0);
+ }
+ // check culling first.
+ if (!scene.checkCulling(vp.getCamera())) {
+ // move on to shadow-only render
+ if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) {
+ renderShadow(scene, vp.getQueue());
+ }
+ return;
+ }
+
+ scene.runControlRender(this, vp);
+ if (scene instanceof Node) {
+ // recurse for all children
+ Node n = (Node) scene;
+ List<Spatial> children = n.getChildren();
+ //saving cam state for culling
+ int camState = vp.getCamera().getPlaneState();
+ for (int i = 0; i < children.size(); i++) {
+ //restoring cam state before proceeding children recusively
+ vp.getCamera().setPlaneState(camState);
+ renderScene(children.get(i), vp);
+
+ }
+ } else if (scene instanceof Geometry) {
+
+ // add to the render queue
+ Geometry gm = (Geometry) scene;
+ if (gm.getMaterial() == null) {
+ throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
+ }
+
+ vp.getQueue().addToQueue(gm, scene.getQueueBucket());
+
+ // add to shadow queue if needed
+ RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
+ if (shadowMode != RenderQueue.ShadowMode.Off) {
+ vp.getQueue().addToShadowQueue(gm, shadowMode);
+ }
+ }
+ }
+
+ /**
+ * Returns the camera currently used for rendering.
+ * <p>
+ * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.
+ *
+ * @return the camera currently used for rendering.
+ */
+ public Camera getCurrentCamera() {
+ return prevCam;
+ }
+
+ /**
+ * The renderer implementation used for rendering operations.
+ *
+ * @return The renderer implementation
+ *
+ * @see #RenderManager(com.jme3.renderer.Renderer)
+ * @see Renderer
+ */
+ public Renderer getRenderer() {
+ return renderer;
+ }
+
+ /**
+ * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}
+ * by rendering each of its visible buckets.
+ * By default the queues will automatically be cleared after rendering,
+ * so there's no need to clear them manually.
+ *
+ * @param vp The ViewPort of which the queue will be flushed
+ *
+ * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera)
+ * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList)
+ */
+ public void flushQueue(ViewPort vp) {
+ renderViewPortQueues(vp, true);
+ }
+
+ /**
+ * Clears the queue of the given ViewPort.
+ * Simply calls {@link RenderQueue#clear() } on the ViewPort's
+ * {@link ViewPort#getQueue() render queue}.
+ *
+ * @param vp The ViewPort of which the queue will be cleared.
+ *
+ * @see RenderQueue#clear()
+ * @see ViewPort#getQueue()
+ */
+ public void clearQueue(ViewPort vp) {
+ vp.getQueue().clear();
+ }
+
+ /**
+ * Render the given viewport queues.
+ * <p>
+ * Changes the {@link Renderer#setDepthRange(float, float) depth range}
+ * appropriately as expected by each queue and then calls
+ * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }
+ * on the queue. Makes sure to restore the depth range to [0, 1]
+ * at the end of the call.
+ * Note that the {@link Bucket#Translucent translucent bucket} is NOT
+ * rendered by this method. Instead the user should call
+ * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }
+ * after this call.
+ *
+ * @param vp the viewport of which queue should be rendered
+ * @param flush If true, the queues will be cleared after
+ * rendering.
+ *
+ * @see RenderQueue
+ * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort)
+ */
+ public void renderViewPortQueues(ViewPort vp, boolean flush) {
+ RenderQueue rq = vp.getQueue();
+ Camera cam = vp.getCamera();
+ boolean depthRangeChanged = false;
+
+ // render opaque objects with default depth range
+ // opaque objects are sorted front-to-back, reducing overdraw
+ rq.renderQueue(Bucket.Opaque, this, cam, flush);
+
+ // render the sky, with depth range set to the farthest
+ if (!rq.isQueueEmpty(Bucket.Sky)) {
+ renderer.setDepthRange(1, 1);
+ rq.renderQueue(Bucket.Sky, this, cam, flush);
+ depthRangeChanged = true;
+ }
+
+
+ // transparent objects are last because they require blending with the
+ // rest of the scene's objects. Consequently, they are sorted
+ // back-to-front.
+ if (!rq.isQueueEmpty(Bucket.Transparent)) {
+ if (depthRangeChanged) {
+ renderer.setDepthRange(0, 1);
+ depthRangeChanged = false;
+ }
+
+ rq.renderQueue(Bucket.Transparent, this, cam, flush);
+ }
+
+ if (!rq.isQueueEmpty(Bucket.Gui)) {
+ renderer.setDepthRange(0, 0);
+ setCamera(cam, true);
+ rq.renderQueue(Bucket.Gui, this, cam, flush);
+ setCamera(cam, false);
+ depthRangeChanged = true;
+ }
+
+ // restore range to default
+ if (depthRangeChanged) {
+ renderer.setDepthRange(0, 1);
+ }
+ }
+
+ /**
+ * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.
+ * <p>
+ * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }
+ * is set to true. This method clears the translucent queue after rendering
+ * it.
+ *
+ * @param vp The viewport of which the translucent queue should be rendered.
+ *
+ * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean)
+ * @see #setHandleTranslucentBucket(boolean)
+ */
+ public void renderTranslucentQueue(ViewPort vp) {
+ RenderQueue rq = vp.getQueue();
+ if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {
+ rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);
+ }
+ }
+
+ private void setViewPort(Camera cam) {
+ // this will make sure to update viewport only if needed
+ if (cam != prevCam || cam.isViewportChanged()) {
+ viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
+ viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
+ viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
+ viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
+ renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
+ renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
+ cam.clearViewportChanged();
+ prevCam = cam;
+
+// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
+// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
+// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
+// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
+//
+// orthoMatrix.loadIdentity();
+// orthoMatrix.setTranslation(translateX, translateY, 0);
+// orthoMatrix.setScale(scaleX, scaleY, 0);
+
+ orthoMatrix.loadIdentity();
+ orthoMatrix.setTranslation(-1f, -1f, 0f);
+ orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);
+ }
+ }
+
+ private void setViewProjection(Camera cam, boolean ortho) {
+ if (shader) {
+ if (ortho) {
+ viewMatrix.set(Matrix4f.IDENTITY);
+ projMatrix.set(orthoMatrix);
+ viewProjMatrix.set(orthoMatrix);
+ } else {
+ viewMatrix.set(cam.getViewMatrix());
+ projMatrix.set(cam.getProjectionMatrix());
+ viewProjMatrix.set(cam.getViewProjectionMatrix());
+ }
+
+ camLoc.set(cam.getLocation());
+ cam.getLeft(camLeft);
+ cam.getUp(camUp);
+ cam.getDirection(camDir);
+
+ near = cam.getFrustumNear();
+ far = cam.getFrustumFar();
+ } else {
+ if (ortho) {
+ renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
+ } else {
+ renderer.setViewProjectionMatrices(cam.getViewMatrix(),
+ cam.getProjectionMatrix());
+ }
+
+ }
+ }
+
+ /**
+ * Set the camera to use for rendering.
+ * <p>
+ * First, the camera's
+ * {@link Camera#setViewPort(float, float, float, float) view port parameters}
+ * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and
+ * {@link Camera#getProjectionMatrix() projection} matrices are set
+ * on the renderer. If <code>ortho</code> is <code>true</code>, then
+ * instead of using the camera's view and projection matrices, an ortho
+ * matrix is computed and used instead of the view projection matrix.
+ * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)
+ * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).
+ *
+ * @param cam The camera to set
+ * @param ortho True if to use orthographic projection (for GUI rendering),
+ * false if to use the camera's view and projection matrices.
+ */
+ public void setCamera(Camera cam, boolean ortho) {
+ setViewPort(cam);
+ setViewProjection(cam, ortho);
+ }
+
+ /**
+ * Draws the viewport but without notifying {@link SceneProcessor scene
+ * processors} of any rendering events.
+ *
+ * @param vp The ViewPort to render
+ *
+ * @see #renderViewPort(com.jme3.renderer.ViewPort, float)
+ */
+ public void renderViewPortRaw(ViewPort vp) {
+ setCamera(vp.getCamera(), false);
+ List<Spatial> scenes = vp.getScenes();
+ for (int i = scenes.size() - 1; i >= 0; i--) {
+ renderScene(scenes.get(i), vp);
+ }
+ flushQueue(vp);
+ }
+
+ /**
+ * Renders the {@link ViewPort}.
+ * <p>
+ * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method
+ * returns immediately. Otherwise, the ViewPort is rendered by
+ * the following process:<br>
+ * <ul>
+ * <li>All {@link SceneProcessor scene processors} that are attached
+ * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.
+ * </li>
+ * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method
+ * is called.</li>
+ * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}
+ * is set on the Renderer</li>
+ * <li>The camera is set on the renderer, including its view port parameters.
+ * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>
+ * <li>Any buffers that the ViewPort requests to be cleared are cleared
+ * and the {@link ViewPort#getBackgroundColor() background color} is set</li>
+ * <li>Every scene that is attached to the ViewPort is flattened into
+ * the ViewPort's render queue
+ * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })
+ * </li>
+ * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }
+ * method is called.</li>
+ * <li>The render queue is sorted and then flushed, sending
+ * rendering commands to the underlying Renderer implementation.
+ * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>
+ * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }
+ * method is called.</li>
+ * <li>The translucent queue of the ViewPort is sorted and then flushed
+ * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
+ * <li>If any objects remained in the render queue, they are removed
+ * from the queue. This is generally objects added to the
+ * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
+ * shadow queue}
+ * which were not rendered because of a missing shadow renderer.</li>
+ * </ul>
+ *
+ * @param vp
+ * @param tpf
+ */
+ public void renderViewPort(ViewPort vp, float tpf) {
+ if (!vp.isEnabled()) {
+ return;
+ }
+ List<SceneProcessor> processors = vp.getProcessors();
+ if (processors.isEmpty()) {
+ processors = null;
+ }
+
+ if (processors != null) {
+ for (SceneProcessor proc : processors) {
+ if (!proc.isInitialized()) {
+ proc.initialize(this, vp);
+ }
+ proc.preFrame(tpf);
+ }
+ }
+
+ renderer.setFrameBuffer(vp.getOutputFrameBuffer());
+ setCamera(vp.getCamera(), false);
+ if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {
+ if (vp.isClearColor()) {
+ renderer.setBackgroundColor(vp.getBackgroundColor());
+ }
+ renderer.clearBuffers(vp.isClearColor(),
+ vp.isClearDepth(),
+ vp.isClearStencil());
+ }
+
+ List<Spatial> scenes = vp.getScenes();
+ for (int i = scenes.size() - 1; i >= 0; i--) {
+ renderScene(scenes.get(i), vp);
+ }
+
+ if (processors != null) {
+ for (SceneProcessor proc : processors) {
+ proc.postQueue(vp.getQueue());
+ }
+ }
+
+ flushQueue(vp);
+
+ if (processors != null) {
+ for (SceneProcessor proc : processors) {
+ proc.postFrame(vp.getOutputFrameBuffer());
+ }
+ }
+ //renders the translucent objects queue after processors have been rendered
+ renderTranslucentQueue(vp);
+ // clear any remaining spatials that were not rendered.
+ clearQueue(vp);
+ }
+
+ /**
+ * Called by the application to render any ViewPorts
+ * added to this RenderManager.
+ * <p>
+ * Renders any viewports that were added using the following methods:
+ * <ul>
+ * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * </ul>
+ *
+ * @param tpf Time per frame value
+ */
+ public void render(float tpf, boolean mainFrameBufferActive) {
+ if (renderer instanceof NullRenderer) {
+ return;
+ }
+
+ this.shader = renderer.getCaps().contains(Caps.GLSL100);
+
+ for (int i = 0; i < preViewPorts.size(); i++) {
+ ViewPort vp = preViewPorts.get(i);
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
+ renderViewPort(vp, tpf);
+ }
+ }
+ for (int i = 0; i < viewPorts.size(); i++) {
+ ViewPort vp = viewPorts.get(i);
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
+ renderViewPort(vp, tpf);
+ }
+ }
+ for (int i = 0; i < postViewPorts.size(); i++) {
+ ViewPort vp = postViewPorts.get(i);
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
+ renderViewPort(vp, tpf);
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/scene/BatchNode.java b/engine/src/core/com/jme3/scene/BatchNode.java index bc1b2cb..303de7a 100644 --- a/engine/src/core/com/jme3/scene/BatchNode.java +++ b/engine/src/core/com/jme3/scene/BatchNode.java @@ -46,6 +46,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -190,10 +191,10 @@ public class BatchNode extends Node implements Savable { } batches.clear(); } - - for (Material material : matMap.keySet()) { + for (Map.Entry<Material, List<Geometry>> entry : matMap.entrySet()) { Mesh m = new Mesh(); - List<Geometry> list = matMap.get(material); + Material material = entry.getKey(); + List<Geometry> list = entry.getValue(); nbGeoms += list.size(); if (!needsFullRebatch) { list.add(batches.get(material).geometry); @@ -408,9 +409,9 @@ public class BatchNode extends Node implements Savable { throw new UnsupportedOperationException(); } - for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) { - compsForBuf[entry.getKey()] = entry.getValue().getNumComponents(); - formatForBuf[entry.getKey()] = entry.getValue().getFormat(); + for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) { + compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents(); + formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat(); } if (mode != null && mode != listMode) { diff --git a/engine/src/core/com/jme3/scene/Mesh.java b/engine/src/core/com/jme3/scene/Mesh.java index 6c587f2..036e525 100644 --- a/engine/src/core/com/jme3/scene/Mesh.java +++ b/engine/src/core/com/jme3/scene/Mesh.java @@ -43,7 +43,6 @@ import com.jme3.math.Matrix4f; import com.jme3.math.Triangle; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; -import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer.Format; import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Usage; @@ -55,8 +54,6 @@ import com.jme3.util.SafeArrayList; import java.io.IOException; import java.nio.*; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; /** * <code>Mesh</code> is used to store rendering data. @@ -237,9 +234,9 @@ public class Mesh implements Savable, Cloneable { clone.buffers = new IntMap<VertexBuffer>(); clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class); - for (Entry<VertexBuffer> ent : buffers){ - VertexBuffer bufClone = ent.getValue().clone(); - clone.buffers.put(ent.getKey(), bufClone); + for (VertexBuffer vb : buffersList.getArray()){ + VertexBuffer bufClone = vb.clone(); + clone.buffers.put(vb.getBufferType().ordinal(), bufClone); clone.buffersList.add(bufClone); } @@ -540,8 +537,8 @@ public class Mesh implements Savable, Cloneable { * for all {@link VertexBuffer vertex buffers} on this Mesh. */ public void setStatic() { - for (Entry<VertexBuffer> entry : buffers){ - entry.getValue().setUsage(Usage.Static); + for (VertexBuffer vb : buffersList.getArray()){ + vb.setUsage(Usage.Static); } } @@ -551,8 +548,8 @@ public class Mesh implements Savable, Cloneable { * for all {@link VertexBuffer vertex buffers} on this Mesh. */ public void setDynamic() { - for (Entry<VertexBuffer> entry : buffers){ - entry.getValue().setUsage(Usage.Dynamic); + for (VertexBuffer vb : buffersList.getArray()){ + vb.setUsage(Usage.Dynamic); } } @@ -562,8 +559,8 @@ public class Mesh implements Savable, Cloneable { * for all {@link VertexBuffer vertex buffers} on this Mesh. */ public void setStreamed(){ - for (Entry<VertexBuffer> entry : buffers){ - entry.getValue().setUsage(Usage.Stream); + for (VertexBuffer vb : buffersList.getArray()){ + vb.setUsage(Usage.Stream); } } @@ -572,11 +569,11 @@ public class Mesh implements Savable, Cloneable { * Some GPUs may prefer the data in this format, however it is a good idea * to <em>avoid</em> using this method as it disables some engine features. */ + @Deprecated public void setInterleaved(){ ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(); - for (Entry<VertexBuffer> entry : buffers){ - vbs.add(entry.getValue()); - } + vbs.addAll(buffersList); + // ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values()); // index buffer not included when interleaving vbs.remove(getBuffer(Type.Index)); @@ -860,6 +857,65 @@ public class Mesh implements Savable, Cloneable { } /** + * Sets the {@link VertexBuffer} on the mesh. + * This will update the vertex/triangle counts if needed. + * + * @param vb The buffer to set + * @throws IllegalArgumentException If the buffer type is already set + */ + public void setBuffer(VertexBuffer vb){ + if (buffers.containsKey(vb.getBufferType().ordinal())) + throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType()); + + buffers.put(vb.getBufferType().ordinal(), vb); + buffersList.add(vb); + updateCounts(); + } + + /** + * Unsets the {@link VertexBuffer} set on this mesh + * with the given type. Does nothing if the vertex buffer type is not set + * initially. + * + * @param type The buffer type to remove + */ + public void clearBuffer(VertexBuffer.Type type){ + VertexBuffer vb = buffers.remove(type.ordinal()); + if (vb != null){ + buffersList.remove(vb); + updateCounts(); + } + } + + /** + * Creates a {@link VertexBuffer} for the mesh or modifies + * the existing one per the parameters given. + * + * @param type The type of the buffer + * @param components Number of components + * @param format Data format + * @param buf The buffer data + * + * @throws UnsupportedOperationException If the buffer already set is + * incompatible with the parameters given. + */ + public void setBuffer(Type type, int components, Format format, Buffer buf){ + VertexBuffer vb = buffers.get(type.ordinal()); + if (vb == null){ + vb = new VertexBuffer(type); + vb.setupData(Usage.Dynamic, components, format, buf); + setBuffer(vb); + }else{ + if (vb.getNumComponents() != components || vb.getFormat() != format){ + throw new UnsupportedOperationException("The buffer already set " + + "is incompatible with the given parameters"); + } + vb.updateData(buf); + updateCounts(); + } + } + + /** * Set a floating point {@link VertexBuffer} on the mesh. * * @param type The type of {@link VertexBuffer}, @@ -871,21 +927,7 @@ public class Mesh implements Savable, Cloneable { * @param buf The floating point data to contain */ public void setBuffer(Type type, int components, FloatBuffer buf) { -// VertexBuffer vb = buffers.get(type); - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - if (buf == null) - return; - - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.Float, buf); -// buffers.put(type, vb); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - }else{ - vb.setupData(Usage.Dynamic, components, Format.Float, buf); - } - updateCounts(); + setBuffer(type, components, Format.Float, buf); } public void setBuffer(Type type, int components, float[] buf){ @@ -893,14 +935,7 @@ public class Mesh implements Savable, Cloneable { } public void setBuffer(Type type, int components, IntBuffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.UnsignedInt, buf); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } + setBuffer(type, components, Format.UnsignedInt, buf); } public void setBuffer(Type type, int components, int[] buf){ @@ -908,14 +943,7 @@ public class Mesh implements Savable, Cloneable { } public void setBuffer(Type type, int components, ShortBuffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.UnsignedShort, buf); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } + setBuffer(type, components, Format.UnsignedShort, buf); } public void setBuffer(Type type, int components, byte[] buf){ @@ -923,38 +951,7 @@ public class Mesh implements Savable, Cloneable { } public void setBuffer(Type type, int components, ByteBuffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.UnsignedByte, buf); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } - } - - public void setBuffer(VertexBuffer vb){ - if (buffers.containsKey(vb.getBufferType().ordinal())) - throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType()); - - buffers.put(vb.getBufferType().ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } - - /** - * Clears or unsets the {@link VertexBuffer} set on this mesh - * with the given type. - * Does nothing if the vertex buffer type is not set initially - * - * @param type The type to remove - */ - public void clearBuffer(VertexBuffer.Type type){ - VertexBuffer vb = buffers.remove(type.ordinal()); - if (vb != null){ - buffersList.remove(vb); - updateCounts(); - } + setBuffer(type, components, Format.UnsignedByte, buf); } public void setBuffer(Type type, int components, short[] buf){ diff --git a/engine/src/core/com/jme3/shader/UniformBinding.java b/engine/src/core/com/jme3/shader/UniformBinding.java index bdca35b..6bd4ca9 100644 --- a/engine/src/core/com/jme3/shader/UniformBinding.java +++ b/engine/src/core/com/jme3/shader/UniformBinding.java @@ -1,162 +1,176 @@ -/* - * Copyright (c) 2009-2010 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; - -public enum UniformBinding { - - /** - * The world matrix. Converts Model space to World space. - * Type: mat4 - */ - WorldMatrix, - - /** - * The view matrix. Converts World space to View space. - * Type: mat4 - */ - ViewMatrix, - - /** - * The projection matrix. Converts View space to Clip/Projection space. - * Type: mat4 - */ - ProjectionMatrix, - - /** - * The world view matrix. Converts Model space to View space. - * Type: mat4 - */ - WorldViewMatrix, - - /** - * The normal matrix. The inverse transpose of the worldview matrix. - * Converts normals from model space to view space. - * Type: mat3 - */ - NormalMatrix, - - /** - * The world view projection matrix. Converts Model space to Clip/Projection - * space. - * Type: mat4 - */ - WorldViewProjectionMatrix, - - /** - * The view projection matrix. Converts Model space to Clip/Projection - * space. - * Type: mat4 - */ - ViewProjectionMatrix, - - - WorldMatrixInverse, - ViewMatrixInverse, - ProjectionMatrixInverse, - ViewProjectionMatrixInverse, - WorldViewMatrixInverse, - NormalMatrixInverse, - WorldViewProjectionMatrixInverse, - - /** - * Contains the four viewport parameters in this order: - * X = Left, - * Y = Top, - * Z = Right, - * W = Bottom. - * Type: vec4 - */ - ViewPort, - - /** - * The near and far values for the camera frustum. - * X = Near - * Y = Far. - * Type: vec2 - */ - FrustumNearFar, - - /** - * The width and height of the camera. - * Type: vec2 - */ - Resolution, - - /** - * Aspect ratio of the resolution currently set. Width/Height. - * Type: float - */ - Aspect, - - /** - * Camera position in world space. - * Type: vec3 - */ - CameraPosition, - - /** - * Direction of the camera. - * Type: vec3 - */ - CameraDirection, - - /** - * Left vector of the camera. - * Type: vec3 - */ - CameraLeft, - - /** - * Up vector of the camera. - * Type: vec3 - */ - CameraUp, - - /** - * Time in seconds since the application was started. - * Type: float - */ - Time, - - /** - * Time in seconds that the last frame took. - * Type: float - */ - Tpf, - - /** - * Frames per second. - * Type: float - */ - FrameRate, -} +/*
+ * Copyright (c) 2009-2010 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;
+
+public enum UniformBinding {
+
+ /**
+ * The world matrix. Converts Model space to World space.
+ * Type: mat4
+ */
+ WorldMatrix,
+
+ /**
+ * The view matrix. Converts World space to View space.
+ * Type: mat4
+ */
+ ViewMatrix,
+
+ /**
+ * The projection matrix. Converts View space to Clip/Projection space.
+ * Type: mat4
+ */
+ ProjectionMatrix,
+
+ /**
+ * The world view matrix. Converts Model space to View space.
+ * Type: mat4
+ */
+ WorldViewMatrix,
+
+ /**
+ * The normal matrix. The inverse transpose of the worldview matrix.
+ * Converts normals from model space to view space.
+ * Type: mat3
+ */
+ NormalMatrix,
+
+ /**
+ * The world view projection matrix. Converts Model space to Clip/Projection
+ * space.
+ * Type: mat4
+ */
+ WorldViewProjectionMatrix,
+
+ /**
+ * The view projection matrix. Converts World space to Clip/Projection
+ * space.
+ * Type: mat4
+ */
+ ViewProjectionMatrix,
+
+ /**
+ * The world matrix inverse transpose. Converts a normals from Model space
+ * to world space.
+ * Type: mat3
+ */
+ WorldMatrixInverseTranspose,
+
+
+
+ WorldMatrixInverse,
+ ViewMatrixInverse,
+ ProjectionMatrixInverse,
+ ViewProjectionMatrixInverse,
+ WorldViewMatrixInverse,
+ NormalMatrixInverse,
+ WorldViewProjectionMatrixInverse,
+
+ /**
+ * Contains the four viewport parameters in this order:
+ * X = Left,
+ * Y = Top,
+ * Z = Right,
+ * W = Bottom.
+ * Type: vec4
+ */
+ ViewPort,
+
+ /**
+ * The near and far values for the camera frustum.
+ * X = Near
+ * Y = Far.
+ * Type: vec2
+ */
+ FrustumNearFar,
+
+ /**
+ * The width and height of the camera.
+ * Type: vec2
+ */
+ Resolution,
+
+ /**
+ * The inverse of the resolution, 1/width and 1/height.
+ * Type: vec2
+ */
+ ResolutionInverse,
+
+ /**
+ * Aspect ratio of the resolution currently set. Width/Height.
+ * Type: float
+ */
+ Aspect,
+
+ /**
+ * Camera position in world space.
+ * Type: vec3
+ */
+ CameraPosition,
+
+ /**
+ * Direction of the camera.
+ * Type: vec3
+ */
+ CameraDirection,
+
+ /**
+ * Left vector of the camera.
+ * Type: vec3
+ */
+ CameraLeft,
+
+ /**
+ * Up vector of the camera.
+ * Type: vec3
+ */
+ CameraUp,
+
+ /**
+ * Time in seconds since the application was started.
+ * Type: float
+ */
+ Time,
+
+ /**
+ * Time in seconds that the last frame took.
+ * Type: float
+ */
+ Tpf,
+
+ /**
+ * Frames per second.
+ * Type: float
+ */
+ FrameRate,
+}
diff --git a/engine/src/core/com/jme3/system/JmeSystem.java b/engine/src/core/com/jme3/system/JmeSystem.java index 1a13a42..2e2d79a 100644 --- a/engine/src/core/com/jme3/system/JmeSystem.java +++ b/engine/src/core/com/jme3/system/JmeSystem.java @@ -1,136 +1,147 @@ -/* - * Copyright (c) 2009-2010 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.AssetManager; -import com.jme3.audio.AudioRenderer; -import java.io.File; -import java.io.InputStream; -import java.net.URL; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class JmeSystem { - - private static JmeSystemDelegate systemDelegate; - - public static void setSystemDelegate(JmeSystemDelegate systemDelegate) { - JmeSystem.systemDelegate = systemDelegate; - } - - public static synchronized File getStorageFolder() { - checkDelegate(); - return systemDelegate.getStorageFolder(); - } - - public static String getFullName() { - checkDelegate(); - return systemDelegate.getFullName(); - } - - public static InputStream getResourceAsStream(String name) { - checkDelegate(); - return systemDelegate.getResourceAsStream(name); - } - - public static URL getResource(String name) { - checkDelegate(); - return systemDelegate.getResource(name); - } - - public static boolean trackDirectMemory() { - checkDelegate(); - return systemDelegate.trackDirectMemory(); - } - - public static void setLowPermissions(boolean lowPerm) { - checkDelegate(); - systemDelegate.setLowPermissions(lowPerm); - } - - public static boolean isLowPermissions() { - checkDelegate(); - return systemDelegate.isLowPermissions(); - } - - public static AssetManager newAssetManager(URL configFile) { - checkDelegate(); - return systemDelegate.newAssetManager(configFile); - } - - public static AssetManager newAssetManager() { - checkDelegate(); - return systemDelegate.newAssetManager(); - } - - public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) { - checkDelegate(); - return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry); - } - - public static Platform getPlatform() { - checkDelegate(); - return systemDelegate.getPlatform(); - } - - public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) { - checkDelegate(); - return systemDelegate.newContext(settings, contextType); - } - - public static AudioRenderer newAudioRenderer(AppSettings settings) { - checkDelegate(); - return systemDelegate.newAudioRenderer(settings); - } - - public static void initialize(AppSettings settings) { - checkDelegate(); - systemDelegate.initialize(settings); - } - - @SuppressWarnings("unchecked") - private static void checkDelegate() { - if (systemDelegate == null) { - Class<JmeSystemDelegate> systemDelegateClass; - try { - systemDelegateClass = (Class<JmeSystemDelegate>) Class.forName("com.jme3.system.JmeDesktopSystem"); - systemDelegate = systemDelegateClass.newInstance(); - } catch (InstantiationException ex) { - Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); - } catch (IllegalAccessException ex) { - Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); - } catch (ClassNotFoundException ex) { - Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); - } - } - } -} +/*
+ * Copyright (c) 2009-2010 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.AssetManager;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.input.SoftTextDialogInput;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class JmeSystem {
+
+ private static JmeSystemDelegate systemDelegate;
+
+ public static void setSystemDelegate(JmeSystemDelegate systemDelegate) {
+ JmeSystem.systemDelegate = systemDelegate;
+ }
+
+ public static synchronized File getStorageFolder() {
+ checkDelegate();
+ return systemDelegate.getStorageFolder();
+ }
+
+ public static String getFullName() {
+ checkDelegate();
+ return systemDelegate.getFullName();
+ }
+
+ public static InputStream getResourceAsStream(String name) {
+ checkDelegate();
+ return systemDelegate.getResourceAsStream(name);
+ }
+
+ public static URL getResource(String name) {
+ checkDelegate();
+ return systemDelegate.getResource(name);
+ }
+
+ public static boolean trackDirectMemory() {
+ checkDelegate();
+ return systemDelegate.trackDirectMemory();
+ }
+
+ public static void setLowPermissions(boolean lowPerm) {
+ checkDelegate();
+ systemDelegate.setLowPermissions(lowPerm);
+ }
+
+ public static boolean isLowPermissions() {
+ checkDelegate();
+ return systemDelegate.isLowPermissions();
+ }
+
+ public static void setSoftTextDialogInput(SoftTextDialogInput input) {
+ checkDelegate();
+ systemDelegate.setSoftTextDialogInput(input);
+ }
+
+ public static SoftTextDialogInput getSoftTextDialogInput() {
+ checkDelegate();
+ return systemDelegate.getSoftTextDialogInput();
+ }
+
+ public static AssetManager newAssetManager(URL configFile) {
+ checkDelegate();
+ return systemDelegate.newAssetManager(configFile);
+ }
+
+ public static AssetManager newAssetManager() {
+ checkDelegate();
+ return systemDelegate.newAssetManager();
+ }
+
+ public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) {
+ checkDelegate();
+ return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry);
+ }
+
+ public static Platform getPlatform() {
+ checkDelegate();
+ return systemDelegate.getPlatform();
+ }
+
+ public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) {
+ checkDelegate();
+ return systemDelegate.newContext(settings, contextType);
+ }
+
+ public static AudioRenderer newAudioRenderer(AppSettings settings) {
+ checkDelegate();
+ return systemDelegate.newAudioRenderer(settings);
+ }
+
+ public static void initialize(AppSettings settings) {
+ checkDelegate();
+ systemDelegate.initialize(settings);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void checkDelegate() {
+ if (systemDelegate == null) {
+ Class<JmeSystemDelegate> systemDelegateClass;
+ try {
+ systemDelegateClass = (Class<JmeSystemDelegate>) Class.forName("com.jme3.system.JmeDesktopSystem");
+ systemDelegate = systemDelegateClass.newInstance();
+ } catch (InstantiationException ex) {
+ Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);
+ } catch (IllegalAccessException ex) {
+ Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);
+ } catch (ClassNotFoundException ex) {
+ Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex);
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/system/JmeSystemDelegate.java b/engine/src/core/com/jme3/system/JmeSystemDelegate.java index 60265ae..e0f6fa5 100644 --- a/engine/src/core/com/jme3/system/JmeSystemDelegate.java +++ b/engine/src/core/com/jme3/system/JmeSystemDelegate.java @@ -1,140 +1,149 @@ -/* - * Copyright (c) 2009-2010 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.AssetManager; -import com.jme3.audio.AudioRenderer; -import java.io.File; -import java.io.InputStream; -import java.net.URL; -import java.util.logging.Logger; - -/** - * - * @author Kirill Vainer, normenhansen - */ -public abstract class JmeSystemDelegate { - - protected final Logger logger = Logger.getLogger(JmeSystem.class.getName()); - protected boolean initialized = false; - protected boolean lowPermissions = false; - protected File storageFolder = null; - - public synchronized File getStorageFolder() { - if (lowPermissions) { - throw new UnsupportedOperationException("File system access restricted"); - } - if (storageFolder == null) { - // Initialize storage folder - storageFolder = new File(System.getProperty("user.home"), ".jme3"); - if (!storageFolder.exists()) { - storageFolder.mkdir(); - } - } - return storageFolder; - } - - public String getFullName() { - return JmeVersion.FULL_NAME; - } - - public InputStream getResourceAsStream(String name) { - return this.getClass().getResourceAsStream(name); - } - - public URL getResource(String name) { - return this.getClass().getResource(name); - } - - public boolean trackDirectMemory() { - return false; - } - - public void setLowPermissions(boolean lowPerm) { - lowPermissions = lowPerm; - } - - public boolean isLowPermissions() { - return lowPermissions; - } - - public abstract AssetManager newAssetManager(URL configFile); - - public abstract AssetManager newAssetManager(); - - public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry); - - private boolean is64Bit(String arch) { - if (arch.equals("x86")) { - return false; - } else if (arch.equals("amd64")) { - return true; - } else if (arch.equals("x86_64")) { - return true; - } else if (arch.equals("ppc") || arch.equals("PowerPC")) { - return false; - } else if (arch.equals("ppc64")) { - return true; - } else if (arch.equals("i386") || arch.equals("i686")) { - return false; - } else if (arch.equals("universal")) { - return false; - } else { - throw new UnsupportedOperationException("Unsupported architecture: " + arch); - } - } - - public Platform getPlatform() { - String os = System.getProperty("os.name").toLowerCase(); - String arch = System.getProperty("os.arch").toLowerCase(); - boolean is64 = is64Bit(arch); - if (os.contains("windows")) { - return is64 ? Platform.Windows64 : Platform.Windows32; - } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) { - return is64 ? Platform.Linux64 : Platform.Linux32; - } else if (os.contains("mac os x") || os.contains("darwin")) { - if (arch.startsWith("ppc")) { - return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32; - } else { - return is64 ? Platform.MacOSX64 : Platform.MacOSX32; - } - } else { - throw new UnsupportedOperationException("The specified platform: " + os + " is not supported."); - } - } - - public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType); - - public abstract AudioRenderer newAudioRenderer(AppSettings settings); - - public abstract void initialize(AppSettings settings); -} +/*
+ * Copyright (c) 2009-2010 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.AssetManager;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.input.SoftTextDialogInput;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Kirill Vainer, normenhansen
+ */
+public abstract class JmeSystemDelegate {
+
+ protected final Logger logger = Logger.getLogger(JmeSystem.class.getName());
+ protected boolean initialized = false;
+ protected boolean lowPermissions = false;
+ protected File storageFolder = null;
+ protected SoftTextDialogInput softTextDialogInput = null;
+
+ public synchronized File getStorageFolder() {
+ if (lowPermissions) {
+ throw new UnsupportedOperationException("File system access restricted");
+ }
+ if (storageFolder == null) {
+ // Initialize storage folder
+ storageFolder = new File(System.getProperty("user.home"), ".jme3");
+ if (!storageFolder.exists()) {
+ storageFolder.mkdir();
+ }
+ }
+ return storageFolder;
+ }
+
+ public String getFullName() {
+ return JmeVersion.FULL_NAME;
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return this.getClass().getResourceAsStream(name);
+ }
+
+ public URL getResource(String name) {
+ return this.getClass().getResource(name);
+ }
+
+ public boolean trackDirectMemory() {
+ return false;
+ }
+
+ public void setLowPermissions(boolean lowPerm) {
+ lowPermissions = lowPerm;
+ }
+
+ public boolean isLowPermissions() {
+ return lowPermissions;
+ }
+
+ public void setSoftTextDialogInput(SoftTextDialogInput input) {
+ softTextDialogInput = input;
+ }
+ public SoftTextDialogInput getSoftTextDialogInput() {
+ return softTextDialogInput;
+ }
+
+ public abstract AssetManager newAssetManager(URL configFile);
+
+ public abstract AssetManager newAssetManager();
+
+ public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry);
+
+ private boolean is64Bit(String arch) {
+ if (arch.equals("x86")) {
+ return false;
+ } else if (arch.equals("amd64")) {
+ return true;
+ } else if (arch.equals("x86_64")) {
+ return true;
+ } else if (arch.equals("ppc") || arch.equals("PowerPC")) {
+ return false;
+ } else if (arch.equals("ppc64")) {
+ return true;
+ } else if (arch.equals("i386") || arch.equals("i686")) {
+ return false;
+ } else if (arch.equals("universal")) {
+ return false;
+ } else {
+ throw new UnsupportedOperationException("Unsupported architecture: " + arch);
+ }
+ }
+
+ public Platform getPlatform() {
+ String os = System.getProperty("os.name").toLowerCase();
+ String arch = System.getProperty("os.arch").toLowerCase();
+ boolean is64 = is64Bit(arch);
+ if (os.contains("windows")) {
+ return is64 ? Platform.Windows64 : Platform.Windows32;
+ } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) {
+ return is64 ? Platform.Linux64 : Platform.Linux32;
+ } else if (os.contains("mac os x") || os.contains("darwin")) {
+ if (arch.startsWith("ppc")) {
+ return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32;
+ } else {
+ return is64 ? Platform.MacOSX64 : Platform.MacOSX32;
+ }
+ } else {
+ throw new UnsupportedOperationException("The specified platform: " + os + " is not supported.");
+ }
+ }
+
+ public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType);
+
+ public abstract AudioRenderer newAudioRenderer(AppSettings settings);
+
+ public abstract void initialize(AppSettings settings);
+}
diff --git a/engine/src/core/com/jme3/util/BufferUtils.java b/engine/src/core/com/jme3/util/BufferUtils.java index f0cc698..2e59e97 100644 --- a/engine/src/core/com/jme3/util/BufferUtils.java +++ b/engine/src/core/com/jme3/util/BufferUtils.java @@ -42,6 +42,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; @@ -1144,6 +1145,47 @@ public final class BufferUtils { } } + private static final AtomicBoolean loadedMethods = new AtomicBoolean(false); + private static Method cleanerMethod = null; + private static Method cleanMethod = null; + private static Method viewedBufferMethod = null; + private static Method freeMethod = null; + + private static Method loadMethod(String className, String methodName){ + try { + Method method = Class.forName(className).getMethod(methodName); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException ex) { + return null; // the method was not found + } catch (SecurityException ex) { + return null; // setAccessible not allowed by security policy + } catch (ClassNotFoundException ex) { + return null; // the direct buffer implementation was not found + } + } + + private static void loadCleanerMethods() { + // If its already true, exit, if not, set it to true. + if (loadedMethods.getAndSet(true)) { + return; + } + // This could potentially be called many times if used from multiple + // threads + synchronized (loadedMethods) { + // Oracle JRE / OpenJDK + cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner"); + cleanMethod = loadMethod("sun.misc.Cleaner", "clean"); + viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer"); + + // Apache Harmony + freeMethod = loadMethod("org.apache.harmony.nio.internal.DirectBuffer", "free"); + + // GUN Classpath (not likely) + //finalizeMethod = loadMethod("java.nio.DirectByteBufferImpl", "finalize"); + } + } + /** * Direct buffers are garbage collected by using a phantom reference and a * reference queue. Every once a while, the JVM checks the reference queue and @@ -1157,27 +1199,27 @@ public final class BufferUtils { * */ public static void destroyDirectBuffer(Buffer toBeDestroyed) { - if (!toBeDestroyed.isDirect()) { return; } + + loadCleanerMethods(); + try { - Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner"); - cleanerMethod.setAccessible(true); - Object cleaner = cleanerMethod.invoke(toBeDestroyed); - if (cleaner != null) { - Method cleanMethod = cleaner.getClass().getMethod("clean"); - cleanMethod.setAccessible(true); - cleanMethod.invoke(cleaner); + if (freeMethod != null) { + freeMethod.invoke(toBeDestroyed); } else { - // Try the alternate approach of getting the viewed buffer - Method viewedBufferMethod = toBeDestroyed.getClass().getMethod("viewedBuffer"); - viewedBufferMethod.setAccessible(true); - Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed); - if (viewedBuffer != null) { - destroyDirectBuffer( (Buffer)viewedBuffer ); + Object cleaner = cleanerMethod.invoke(toBeDestroyed); + if (cleaner != null) { + cleanMethod.invoke(cleaner); } else { - Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed); + // Try the alternate approach of getting the viewed buffer first + Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed); + if (viewedBuffer != null) { + destroyDirectBuffer((Buffer) viewedBuffer); + } else { + Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed); + } } } } catch (IllegalAccessException ex) { @@ -1186,11 +1228,8 @@ public final class BufferUtils { Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); } catch (InvocationTargetException ex) { Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); - } catch (NoSuchMethodException ex) { - Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); } catch (SecurityException ex) { Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); } } - } diff --git a/engine/src/core/com/jme3/util/IntMap.java b/engine/src/core/com/jme3/util/IntMap.java index edf659b..1b91119 100644 --- a/engine/src/core/com/jme3/util/IntMap.java +++ b/engine/src/core/com/jme3/util/IntMap.java @@ -44,8 +44,6 @@ import java.util.Map; * @author Nate */ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable { - - private final IntMapIterator iterator = new IntMapIterator(); private Entry[] table; private final float loadFactor; @@ -200,8 +198,9 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable { } public Iterator<Entry<T>> iterator() { - iterator.beginUse(); - return iterator; + IntMapIterator it = new IntMapIterator(); + it.beginUse(); + return it; } final class IntMapIterator implements Iterator<Entry<T>> { diff --git a/engine/src/core/com/jme3/util/TangentBinormalGenerator.java b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java index 88f6822..8816b91 100644 --- a/engine/src/core/com/jme3/util/TangentBinormalGenerator.java +++ b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java @@ -117,7 +117,13 @@ public class TangentBinormalGenerator { } } else { Geometry geom = (Geometry) scene; - generate(geom.getMesh()); + Mesh mesh = geom.getMesh(); + + // Check to ensure mesh has texcoords and normals before generating + if (mesh.getBuffer(Type.TexCoord) != null + && mesh.getBuffer(Type.Normal) != null){ + generate(geom.getMesh()); + } } } @@ -640,7 +646,7 @@ public class TangentBinormalGenerator { lineMesh.setBuffer(Type.Color, 4, lineColor); lineMesh.setStatic(); - lineMesh.setInterleaved(); + //lineMesh.setInterleaved(); return lineMesh; } @@ -733,7 +739,7 @@ public class TangentBinormalGenerator { lineMesh.setBuffer(Type.Color, 4, lineColor); lineMesh.setStatic(); - lineMesh.setInterleaved(); + //lineMesh.setInterleaved(); return lineMesh; } } |