com.jme3.system.AWTFrameProcessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jme3-desktop Show documentation
Show all versions of jme3-desktop Show documentation
jMonkeyEngine is a 3-D game engine for adventurous Java developers
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.system;
import java.awt.Component;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import com.jme3.app.Application;
import com.jme3.input.AWTKeyInput;
import com.jme3.input.AWTMouseInput;
import com.jme3.post.SceneProcessor;
import com.jme3.profile.AppProfiler;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image;
/**
* A frame processor that enables to render JMonkey frame buffer onto an AWT component.
*
* This class is based on the JavaFX original code provided by Alexander Brui (see JME3-FX)
*
* @author Julien Seinturier - COMEX SA - http://www.seinturier.fr
* @author Alexander Brui (JavaSaBr)
*
*/
public class AWTFrameProcessor implements SceneProcessor, PropertyChangeListener {
public enum TransferMode {
ALWAYS,
ON_CHANGES
}
private Application application = null;
/**
* The width listener.
*/
protected PropertyChangeListener widthListener;
/**
* The height listener.
*/
protected PropertyChangeListener heightListener;
/**
* The ration listener.
*/
protected PropertyChangeListener rationListener;
/**
* The flag to decide when we should resize.
*/
private final AtomicInteger reshapeNeeded;
/**
* The render manager.
*/
private RenderManager renderManager;
/**
* The source view port.
*/
private ViewPort viewPort;
/**
* The frame transfer.
*/
private AWTComponentRenderer frameTransfer;
/**
* The transfer mode.
*/
private TransferMode transferMode;
/**
* The destination of jMe frames.
*/
protected volatile Component destination;
/**
* The flag is true if this processor is main.
*/
private volatile boolean main;
private int askWidth;
private int askHeight;
private boolean askFixAspect;
private boolean enabled;
@Override
public void initialize(RenderManager rm, ViewPort vp) {
this.renderManager = rm;
}
@Override
public void reshape(ViewPort vp, int w, int h) {
// TODO Auto-generated method stub
}
@Override
public boolean isInitialized() {
return frameTransfer != null;
}
@Override
public void preFrame(float tpf) {
// TODO Auto-generated method stub
}
@Override
public void postQueue(RenderQueue rq) {
// TODO Auto-generated method stub
}
@Override
public void postFrame(FrameBuffer out) {
if (!isEnabled()) {
return;
}
final AWTComponentRenderer frameTransfer = getFrameTransfer();
if (frameTransfer != null) {
frameTransfer.copyFrameBufferToImage(getRenderManager());
}
// for the next frame
if (hasDestination() && reshapeNeeded.get() > 0 && reshapeNeeded.decrementAndGet() >= 0) {
if (frameTransfer != null) {
frameTransfer.dispose();
}
setFrameTransfer(reshapeInThread(askWidth, askHeight, askFixAspect));
}
}
@Override
public void cleanup() {
final AWTComponentRenderer frameTransfer = getFrameTransfer();
if (frameTransfer != null) {
frameTransfer.dispose();
setFrameTransfer(null);
}
}
@Override
public void setProfiler(AppProfiler profiler) {
// not implemented
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println("Property changed: "+evt.getPropertyName()+" "+evt.getOldValue()+" -> "+evt.getNewValue());
}
public AWTFrameProcessor() {
transferMode = TransferMode.ALWAYS;
askWidth = 1;
askHeight = 1;
main = true;
reshapeNeeded = new AtomicInteger(2);
}
/**
* Notify about that the ratio was changed.
*
* @param newValue the new value of the ratio.
*/
protected void notifyChangedRatio(Boolean newValue) {
notifyComponentResized(destination.getWidth(), destination.getHeight(), newValue);
}
/**
* Notify about that the height was changed.
*
* @param newValue the new value of the height.
*/
protected void notifyChangedHeight(Number newValue) {
notifyComponentResized(destination.getWidth(), newValue.intValue(), isPreserveRatio());
}
/**
* Notify about that the width was changed.
*
* @param newValue the new value of the width.
*/
protected void notifyChangedWidth(Number newValue) {
notifyComponentResized(newValue.intValue(), destination.getHeight(), isPreserveRatio());
}
/**
* Gets the application.
*
* @return the application.
*/
protected Application getApplication() {
return application;
}
/**
* Sets the application.
*
* @param application the application.
*/
protected void setApplication(Application application) {
this.application = application;
}
/**
* Gets the current destination.
*
* @return the current destination.
*/
protected Component getDestination() {
return destination;
}
/**
* Sets the destination.
*
* @param destination the destination.
*/
protected void setDestination(Component destination) {
this.destination = destination;
}
/**
* Checks of existing destination.
* @return true if destination is exists.
*/
protected boolean hasDestination() {
return destination != null;
}
/**
* Checks of existing application.
* @return true if destination is exists.
*/
protected boolean hasApplication() {
return application != null;
}
/**
* Gets the frame transfer.
* @return the file transfer.
*/
protected AWTComponentRenderer getFrameTransfer() {
return frameTransfer;
}
/**
* Sets the frame transfer.
*
* @param frameTransfer the file transfer.
*/
protected void setFrameTransfer(AWTComponentRenderer frameTransfer) {
this.frameTransfer = frameTransfer;
}
/**
* Gets the view port.
*
* @return the view port.
*/
protected ViewPort getViewPort() {
return viewPort;
}
/**
* Gets the render manager.
*
* @return the render manager.
*/
protected RenderManager getRenderManager() {
return renderManager;
}
public boolean isMain() {
return main;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
/**
* Handle resizing.
*
* @param newWidth the new width.
* @param newHeight the new height.
* @param fixAspect true to fix the aspect ratio.
*/
protected void notifyComponentResized(int newWidth, int newHeight, boolean fixAspect) {
newWidth = Math.max(newWidth, 1);
newHeight = Math.max(newHeight, 1);
if (askWidth == newWidth && askWidth == newHeight && askFixAspect == fixAspect) {
return;
}
askWidth = newWidth;
askHeight = newHeight;
askFixAspect = fixAspect;
reshapeNeeded.set(2);
}
public void reshape() {
reshapeNeeded.set(2);
}
/**
* Is preserve ratio.
*
* @return is preserve ratio.
*/
protected boolean isPreserveRatio() {
return false;
}
/**
* Gets destination width.
*
* @return the destination width.
*/
protected int getDestinationWidth() {
return getDestination().getWidth();
}
/**
* Gets destination height.
*
* @return the destination height.
*/
protected int getDestinationHeight() {
return getDestination().getHeight();
}
/**
* Bind this processor.
*
* @param destination the destination.
* @param application the application.
*/
public void bind(Component destination, Application application) {
final RenderManager renderManager = application.getRenderManager();
if (renderManager == null) {
throw new RuntimeException("No render manager available from the application.");
}
List postViews = renderManager.getPostViews();
if (postViews.isEmpty()) {
throw new RuntimeException("the list of a post view is empty.");
}
bind(destination, application, postViews.get(postViews.size() - 1), true);
}
/**
* Bind this processor.
*
* @param destination the destination.
* @param application the application.
* @param viewPort the view port.
*/
public void bind(Component destination, Application application, ViewPort viewPort) {
bind(destination, application, viewPort, true);
}
/**
* Bind this processor.
*
* @param destination the destination.
* @param application the application.
* @param viewPort the view port.
* @param main true if this processor is main.
*/
public void bind(final Component destination, final Application application, ViewPort viewPort, boolean main) {
if (hasApplication()) {
throw new RuntimeException("This process is already bonded.");
}
setApplication(application);
setEnabled(true);
this.main = main;
this.viewPort = viewPort;
this.viewPort.addProcessor(this);
if (EventQueue.isDispatchThread()) {
bindDestination(application, destination);
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
bindDestination(application, destination);
}});
}
}
/**
* Unbind this processor from its current destination.
*/
public void unbind() {
if (viewPort != null) {
viewPort.removeProcessor(this);
viewPort = null;
}
if (EventQueue.isDispatchThread()) {
unbindDestination();
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
unbindDestination();
}});
}
}
/**
* Bind this processor.
*
* @param application the application.
* @param destination the destination.
*/
protected void bindDestination(Application application, Component destination) {
if (!EventQueue.isDispatchThread()) {
throw new RuntimeException("bind has to be done from the Event Dispatching thread.");
}
if (isMain()) {
if (application.getContext() != null) {
if (application.getContext() instanceof AWTContext) {
AWTContext context = (AWTContext) application.getContext();
AWTMouseInput mouseInput = context.getMouseInput();
mouseInput.bind(destination);
AWTKeyInput keyInput = context.getKeyInput();
keyInput.bind(destination);
setDestination(destination);
bindListeners();
notifyComponentResized(getDestinationWidth(), getDestinationHeight(), isPreserveRatio());
} else {
throw new IllegalArgumentException("Underlying application has to use AWTContext (actually using "+application.getContext().getClass().getSimpleName()+")");
}
} else {
throw new IllegalArgumentException("Underlying application has to use a valid AWTContext (context is null)");
}
}
}
/**
* Unbind this processor from destination.
*/
protected void unbindDestination() {
if (!EventQueue.isDispatchThread()) {
throw new RuntimeException("unbind has to be done from the Event Dispatching thread.");
}
if (hasApplication() && isMain()) {
final AWTContext context = (AWTContext) getApplication().getContext();
final AWTMouseInput mouseInput = context.getMouseInput();
mouseInput.unbind();
final AWTKeyInput keyInput = context.getKeyInput();
keyInput.unbind();
}
setApplication(null);
if (hasDestination()) {
unbindListeners();
setDestination(null);
}
}
protected void bindListeners() {
Component destination = getDestination();
destination.addPropertyChangeListener(this);
destination.addPropertyChangeListener(this);
}
protected void unbindListeners() {
Component destination = getDestination();
destination.removePropertyChangeListener(this);
destination.removePropertyChangeListener(this);
}
/**
* Reshape the current frame transfer for the new size.
*
* @param width the width.
* @param height the height.
* @param fixAspect true to fix the aspect ratio.
* @return the new frame transfer.
*/
protected AWTComponentRenderer reshapeInThread(final int width, final int height, final boolean fixAspect) {
reshapeCurrentViewPort(width, height);
ViewPort viewPort = getViewPort();
RenderManager renderManager = getRenderManager();
FrameBuffer frameBuffer = viewPort.getOutputFrameBuffer();
AWTComponentRenderer frameTransfer = createFrameTransfer(frameBuffer, width, height);
frameTransfer.init(renderManager.getRenderer(), isMain());
if (isMain()) {
AWTContext context = (AWTContext) getApplication().getContext();
context.setHeight(height);
context.setWidth(width);
}
return frameTransfer;
}
/**
* Create a new frame transfer.
*
* @param frameBuffer the frame buffer.
* @param width the width.
* @param height the height.
* @return the new frame transfer.
*/
protected AWTComponentRenderer createFrameTransfer(FrameBuffer frameBuffer, int width, int height) {
return new AWTComponentRenderer(getDestination(), getTransferMode(), isMain() ? null : frameBuffer, width, height);
}
/**
* Reshape the current view port.
*
* @param width the width.
* @param height the height.
*/
protected void reshapeCurrentViewPort(int width, int height) {
ViewPort viewPort = getViewPort();
Camera camera = viewPort.getCamera();
int cameraAngle = getCameraAngle();
float aspect = (float) camera.getWidth() / camera.getHeight();
if (isMain()) {
getRenderManager().notifyReshape(width, height);
camera.setFrustumPerspective(cameraAngle, aspect, 1f, 10000);
return;
}
camera.resize(width, height, true);
camera.setFrustumPerspective(cameraAngle, aspect, 1f, 10000);
final List processors = viewPort.getProcessors();
boolean found = false;
Iterator iter = processors.iterator();
while(!found && iter.hasNext()) {
if (!(iter.next() instanceof AWTFrameProcessor)) {
found = true;
}
}
if (found) {
FrameBuffer frameBuffer = new FrameBuffer(width, height, 1);
frameBuffer.setDepthBuffer(Image.Format.Depth);
frameBuffer.setColorBuffer(Image.Format.RGBA8);
frameBuffer.setSrgb(true);
viewPort.setOutputFrameBuffer(frameBuffer);
}
for (final SceneProcessor sceneProcessor : processors) {
if (!sceneProcessor.isInitialized()) {
sceneProcessor.initialize(renderManager, viewPort);
} else {
sceneProcessor.reshape(viewPort, width, height);
}
}
}
/**
* Gets camera angle.
*
* @return the camera angle.
*/
protected int getCameraAngle() {
final String angle = System.getProperty("awt.frame.transfer.camera.angle", "45");
return Integer.parseInt(angle);
}
public TransferMode getTransferMode() {
return transferMode;
}
public void setTransferMode(TransferMode transferMode) {
this.transferMode = transferMode;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy