This is something I setup for @pspeed.
EDIT: Forgot to mention this is an AppState.
Needs Lemur, slf4j, test-data.
•There is one line in initMiniMap that can be uncommented to show the difference when miniCam is full screen.
• addCollisionRoot is in initMiniMap.
•When miniCam is in the top right corner, click in the top left corner of main view to fire listener.
package mygame;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.ui.Picture;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.event.CursorButtonEvent;
import com.simsilica.lemur.event.CursorEventControl;
import com.simsilica.lemur.event.DefaultCursorListener;
import com.simsilica.lemur.event.PickState;
import com.simsilica.lemur.style.BaseStyles;
import java.util.logging.Level;
import java.util.logging.Logger;
public class TestGuiNodeState extends BaseAppState implements ActionListener {
private static final Logger LOG = Logger.getLogger(TestGuiNodeState.class.getName());
private CollisionResult closest;
private Geometry mark;
private Camera miniCam;
private Node miniMapNode;
private Node miniMapGuiNode;
@Override
protected void initialize(Application app) {
// Create a simple container for our elements
// Initialize the globals access so that the defualt
// components can find what they need.
GuiGlobals.initialize(app);
// Load the 'glass' style
BaseStyles.loadGlassStyle();
// Set 'glass' as the default style when not specified
GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");
Box b = new Box(1, 1, 1);
Geometry geom = new Geometry("Box", b);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Green);
geom.setMaterial(mat);
Node terrainNode = new Node("terrainNode");
terrainNode.setLocalTranslation(new Vector3f(-3.0f, 0.0f, 0.0f));
((SimpleApplication) app).getRootNode().attachChild(terrainNode);
terrainNode.attachChild(geom);
//setup minimap Nodes
miniMapNode = new Node("miniMapNode"); //self managed
miniMapGuiNode = new Node("miniMapGuiNode"); //self managed
miniMapGuiNode.setQueueBucket(RenderQueue.Bucket.Gui);
miniMapGuiNode.setCullHint(Spatial.CullHint.Never);
intiMiniMap(terrainNode);
initKeys();
initMark();
}
private void intiMiniMap(Node terrainNode) {
miniCam = getApplication().getCamera().clone();
miniCam.lookAt(terrainNode.getLocalTranslation(), miniCam.getUp().clone());
//Change to make full screen
miniCam.setViewPort(.5f, 1.0f, 0.5f, 1.0f);
// miniCam.setViewPort(0.0f, 1.0f, 0.0f, 1.0f);
ViewPort miniMapViewPort = getApplication().getRenderManager().createMainView("MiniMapView", miniCam);
miniMapViewPort.setClearFlags(true, true, true);
miniMapViewPort.setBackgroundColor(ColorRGBA.Gray);
//Attach scenes
miniMapViewPort.attachScene(miniMapNode);
miniMapViewPort.attachScene(terrainNode);
// Create a new cam for the guiNode
Camera miniMapGuiCam = miniCam.clone();
ViewPort miniMapGuiViewPort = getApplication().getRenderManager().createPostView("MiniMapGuiViewPort", miniMapGuiCam);
miniMapGuiViewPort.setClearFlags(false, false, false);
//Attach scenes
miniMapGuiViewPort.attachScene(miniMapGuiNode);
//Add picking to the guiNode
getStateManager().getState(PickState.class).addCollisionRoot(miniMapGuiNode, miniMapGuiViewPort, PickState.PICK_LAYER_GUI);
Node monkeyNode = addButton("Monkey", "Interface/icons/SmartMonkey128.png", 128, 128, 0.15f, 0.85f, true, miniCam);
miniMapGuiNode.attachChild(monkeyNode);
CursorEventControl.addListenersToSpatial(monkeyNode, new DefaultCursorListener() {
@Override
protected void click( CursorButtonEvent event, Spatial target, Spatial capture ) {
System.out.println("I've been clicked:" + target + " " + event.getViewPort().getName() + " " + event.getLocation() );
}
});
}
private void initKeys() {
getApplication().getInputManager().addMapping("Pick", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
getApplication().getInputManager().addListener(this, "Pick");
}
/**
* A red ball that marks the last spot that was "hit" by the "shot".
*/
protected void initMark() {
Sphere sphere = new Sphere(30, 30, 0.1f);
mark = new Geometry("BOOM!", sphere);
Material mark_mat = new Material(getApplication().getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mark_mat.setColor("Color", ColorRGBA.Red);
mark.setMaterial(mark_mat);
}
/**
* Creates and positions a picture to be attached to a gui node.
* @param name the string name for the picture. Will be appended with the
* word "Picture".
* @param asset the path of the asset to be used.
* @param butWidth the width of the picture. Scales to this width if not
* the actual width of image size.
* @param butHeight the height of the picture. Scales to this height if not
* the actual height of image size.
* @param screenWidth the position to place the picture in the cameras
* view. Must be a float between 0 and 1 where 0 = all the way to the left
* side of the view and 1 = all the way to the right side of the view.
* @param screenHeight the position to place the picture in the cameras
* view. Must be a float between 0 and 1 where 0 = all the way to the bottom
* of the view and 1 = all the way to the top of the view.
* @param alpha set to true if you want to use the supplied images Alpha
* channel.
* @param camera Cam used to calculate node position.
* @return returns a node that needs to be attached to a gui node.
*/
public Node addButton(String name, String asset, int butWidth, int butHeight,
float screenWidth, float screenHeight, boolean alpha, Camera camera) {
int width = (int) (camera.getWidth() * screenWidth);
int height = (int) (camera.getHeight()* screenHeight);
//Image to use for the button.
Picture button = new Picture(name + "Picture");
button.setImage(getApplication().getAssetManager(), asset, alpha);
button.setWidth(butWidth);
button.setHeight(butHeight);
//Center the image.
button.center();
//Add to the GUI bucket.
button.setQueueBucket(RenderQueue.Bucket.Gui);
Node node = new Node(name + "Node");
node.attachChild(button);
node.setLocalTranslation(width, height, 0);
return node;
}
/**
* Checks if the cursor position is within the given ViewPort.
* @param vp ViewPort to check cursor position against.
* @return true if the cursor is within the supplied ViewPort.
*/
private boolean miniViewHasFocus(ViewPort vp) {
//No ViewPort to check.
if (vp == null) {
LOG.log(Level.SEVERE, "viewPortHasFocus: Null ViewPort.");
return false;
}
float x1 = vp.getCamera().getViewPortLeft();
float x2 = vp.getCamera().getViewPortRight();
float y1 = vp.getCamera().getViewPortBottom();
float y2 = vp.getCamera().getViewPortTop();
int width = vp.getCamera().getWidth();
int height = vp.getCamera().getHeight();
float x = getApplication().getInputManager().getCursorPosition().getX();
float y = getApplication().getInputManager().getCursorPosition().getY();
return x >= (x1*width) && x <= (x2*width) && y >= (y1*height) && y <= (y2*height);
}
@Override
protected void cleanup(Application app) {
//TODO: clean up what you initialized in the initialize method,
//e.g. remove all spatials from rootNode
}
//onEnable()/onDisable() can be used for managing things that should
//only exist while the state is enabled. Prime examples would be scene
//graph attachment or input listener attachment.
@Override
protected void onEnable() {
//Called when the state is fully enabled, ie: is attached and
//isEnabled() is true or when the setEnabled() status changes after the
//state is attached.
}
@Override
protected void onDisable() {
//Called when the state was previously enabled but is now disabled
//either because setEnabled(false) was called or the state is being
//cleaned up.
}
@Override
public void update(float tpf) {
miniMapNode.updateLogicalState(tpf);
miniMapGuiNode.updateLogicalState(tpf);
}
@Override
public void render(RenderManager rm) {
miniMapNode.updateGeometricState();
miniMapGuiNode.updateGeometricState();
}
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (name.equals("Pick") && !isPressed && !miniViewHasFocus(getApplication().getRenderManager().getMainView("MiniMapView"))) {
CollisionResults results = new CollisionResults();
Vector2f click2d = getApplication().getInputManager().getCursorPosition().clone();
Vector3f click3d = getApplication().getCamera().getWorldCoordinates(click2d, 0f).clone();
Vector3f dir = getApplication().getCamera().getWorldCoordinates(
click2d, 1f).subtractLocal(click3d).normalizeLocal();
Ray ray = new Ray(click3d, dir);
((SimpleApplication) getApplication()).getRootNode().collideWith(ray, results);
for (int i = 0; i < results.size(); i++) {
// For each hit, we know distance, impact point, name of geometry.
float dist = results.getCollision(i).getDistance();
Vector3f pt = results.getCollision(i).getContactPoint();
String hit = results.getCollision(i).getGeometry().getName();
System.out.println("* Collision #" + i);
System.out.println(
" You shot " + hit
+ " at " + pt
+ ", " + dist + " wu away.");
}
if (results.size() > 0) {
// The closest collision point is what was truly hit:
closest = results.getClosestCollision();
// Let's interact - we mark the hit with a red dot.
mark.setLocalTranslation(closest.getContactPoint());
((SimpleApplication) getApplication()).getRootNode().attachChild(mark);
} else {
// No hits? Then remove the red mark.
((SimpleApplication) getApplication()).getRootNode().detachChild(mark);
}
}
if (name.equals("Pick") && !isPressed && miniViewHasFocus(getApplication().getRenderManager().getMainView("MiniMapView"))) {
CollisionResults results = new CollisionResults();
Vector2f click2d = getApplication().getInputManager().getCursorPosition().clone();
Vector3f click3d = miniCam.getWorldCoordinates(click2d, 0f).clone();
Vector3f dir = miniCam.getWorldCoordinates(
click2d, 1f).subtractLocal(click3d).normalizeLocal();
Ray ray = new Ray(click3d, dir);
((SimpleApplication) getApplication()).getRootNode().collideWith(ray, results);
for (int i = 0; i < results.size(); i++) {
// For each hit, we know distance, impact point, name of geometry.
float dist = results.getCollision(i).getDistance();
Vector3f pt = results.getCollision(i).getContactPoint();
String hit = results.getCollision(i).getGeometry().getName();
System.out.println("* Collision #" + i);
System.out.println(
" You shot miniMap " + hit
+ " at " + pt
+ ", " + dist + " wu away.");
}
if (results.size() > 0) {
// The closest collision point is what was truly hit:
closest = results.getClosestCollision();
// Let's interact - we mark the hit with a red dot.
mark.setLocalTranslation(closest.getContactPoint());
miniMapNode.attachChild(mark);
} else {
// No hits? Then remove the red mark.
miniMapNode.detachChild(mark);
}
}
}
}