# MouseControl : JME Canvas in Swing Gui



## TheMuh (7. Jul 2011)

Ich habe die Tutorials bzgl. Auswahl eines Objektes und jme Canvas in einer Swing GUI verbunden und versuche die Steuerung zu optimieren.
Das Problem ist, dass die Maus im JME Fenster nur bei gedrückter Maustaste aktiviert wird, was ja aufgrund der Swing Umgebung erstmal wünschenswert ist, aber aufgrund der Tatsache das Objekte im JME Fenster per linken Mausclick auswählbar sein sollen leider nicht ausreichend ist.

Die Frage daher, wie setze ich die Steuerung auf, um in einer SWING Gui trotzdem durch einfachen linken Mausclick Objekte im jme Canvas anzuclicken ??
An flyCam.setDragToRotate(true) führt kein Weg vorbei, oder ?

Code:

```
import com.jme3.app.SimpleApplication;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
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.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** Sample 8 - how to let the user pick (select) objects in the scene
 * using the mouse or key presses. Can be used for shooting, opening doors, etc. */
public class HelloPicking extends SimpleApplication {
  public static void main(String[] args) {
    java.awt.EventQueue.invokeLater(new Runnable() {
      public void run() {
          AppSettings settings = new AppSettings(true);
        settings.setWidth(640);
        settings.setHeight(480);
        
        HelloPicking canvasApplication = new HelloPicking();
        canvasApplication.setSettings(settings);
        canvasApplication.createCanvas(); // create canvas!
        JmeCanvasContext ctx = (JmeCanvasContext) canvasApplication.getContext();
        ctx.setSystemListener(canvasApplication);
        Dimension dim = new Dimension(640, 480);
        ctx.getCanvas().setPreferredSize(dim);
        
        JFrame window = new JFrame("Swing Application");
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel(new FlowLayout()); // a panel
        panel.add(ctx.getCanvas());                  // add JME canvas
        panel.add(new JButton("Swing Component"));      // add some Swing
        window.add(panel);
        window.pack();

        window.setVisible(true);
        canvasApplication.startCanvas();

      }
    });
  }
  Node shootables;
  Geometry mark;
  @Override
  public void simpleInitApp() {
    flyCam.setDragToRotate(true);
    initCrossHairs(); // a "+" in the middle of the screen to help aiming
    initKeys();       // load custom key mappings
    initMark();       // a red sphere to mark the hit
    /** create four colored boxes and a floor to shoot at: */
    shootables = new Node("Shootables");
    rootNode.attachChild(shootables);
    shootables.attachChild(makeCube("a Dragon",    -2f, 0f, 1f));
    shootables.attachChild(makeCube("a tin can",    1f,-2f, 0f));
    shootables.attachChild(makeCube("the Sheriff",  0f, 1f,-2f));
    shootables.attachChild(makeCube("the Deputy",   1f, 0f,-4f));
    shootables.attachChild(makeFloor());
  }
  /** Declaring the "Shoot" action and mapping to its triggers. */
  private void initKeys() {
    inputManager.addMapping("Shoot",
      new KeyTrigger(KeyInput.KEY_SPACE), // trigger 1: spacebar
      new MouseButtonTrigger(0));         // trigger 2: left-button click
    inputManager.addListener(actionListener, "Shoot");
  }
  /** Defining the "Shoot" action: Determine what was hit and how to respond. */
  private ActionListener actionListener = new ActionListener() {
    @Override
    public void onAction(String name, boolean keyPressed, float tpf) {
      if (name.equals("Shoot") && !keyPressed) {
        // 1. Reset results list.
        CollisionResults results = new CollisionResults();
        // 2. Aim the ray from cam loc to cam direction.
        Ray ray = new Ray(cam.getLocation(), cam.getDirection());
        // 3. Collect intersections between Ray and Shootables in results list.
        shootables.collideWith(ray, results);
        // 4. Print the results.
        System.out.println("----- Collisions? " + results.size() + "-----");
        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.");
        }
        // 5. Use the results (we mark the hit object)
        if (results.size() > 0){
          // The closest collision point is what was truly hit:
          CollisionResult closest = results.getClosestCollision();
          // Let's interact - we mark the hit with a red dot.
          mark.setLocalTranslation(closest.getContactPoint());
          rootNode.attachChild(mark);
        } else {
        // No hits? Then remove the red mark.
          rootNode.detachChild(mark);
        }
      }
    }
  };
  /** A cube object for target practice */
  protected Geometry makeCube(String name, float x, float y, float z) {
    Box box = new Box(new Vector3f(x, y, z), 1, 1, 1);
    Geometry cube = new Geometry(name, box);
    Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat1.setColor("Color", ColorRGBA.randomColor());
    cube.setMaterial(mat1);
    return cube;
  }
  /** A floor to show that the "shot" can go through several objects. */
  protected Geometry makeFloor() {
    Box box = new Box(new Vector3f(0,-4,-5), 15,.2f,15);
    Geometry floor = new Geometry("the Floor", box);
    Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat1.setColor("Color", ColorRGBA.Gray);
    floor.setMaterial(mat1);
    return floor;
  }
  /** A red ball that marks the last spot that was "hit" by the "shot". */
  protected void initMark() {
    Sphere sphere = new Sphere(30, 30, 0.2f);
    mark = new Geometry("BOOM!", sphere);
    Material mark_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mark_mat.setColor("Color", ColorRGBA.Red);
    mark.setMaterial(mark_mat);
  }
  /** A centred plus sign to help the player aim. */
  protected void initCrossHairs() {
    guiNode.detachAllChildren();
    guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
    BitmapText ch = new BitmapText(guiFont, false);
    ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
    ch.setText("+"); // crosshairs
    ch.setLocalTranslation( // center
      settings.getWidth()/2 - guiFont.getCharSet().getRenderedSize()/3*2,
      settings.getHeight()/2 + ch.getLineHeight()/2, 0);
    guiNode.attachChild(ch);
  }
}
```


----------



## thewulf00 (7. Jul 2011)

Du versuchst, aus den vorgegebenen Systemen von jME auszubrechen, demnach solltest du weiter unten ansetzen, z.B. mal Application anstatt SimpleApplication nutzen (aber Achtung: Hier muss Du vieles selbst machen).
Wenn ich ausbrechen muss, dann versuche ich immer, nach der Initialisierung von jME in meinen eigenen Initialisierungen manches wieder aufzuheben. Du könntest das mit dem Mauscursor, dem MouseListener, usw. mal versuchen.


----------



## TheMuh (8. Jul 2011)

ok verstehe .... befinde mich ja noch in den anfängen jme zu verstehen ..

Ich will im Grunde nur eine Auskunft dahingehend OB es überhaupt realiserbar ist und ich nicht nach vielen Einarbeitungen vor vollendeten Tatsachen stehe und das schon grundsätzlich nicht möglich ist... die Zeit könnt ich dann anderweitig sinnvoller einsetzen ...

Also, kann ich davon ausgehen das es grundsätzlich in jedem Fall möglich ist eine vollwertige Maussteuerung für ein jme Canvas innerhalb einer Swing Umgebung zu erstellen ?
Mit Vollwertig mein ich, das ein einfacher Mausclick direkt an die jme Umgebung weitergegeben werden kann...   ???:L


----------



## Kr0e (8. Jul 2011)

Warum nochmal willst du JME mit Swing mischen ? JME hat ein natives OpenGL Fenster ... Mischen ist unperformant und sieht auch nciht schön aus... (Willst du ein Spiel amchen ?) Außerdem achte darauf, dass Swing in einem anderen Thread läufT! Du kannst also nicht einfach Node.attach(blabalabla) von einem MouseListener aus aufrufen.... Das erzeugt Inkonsistenz!

Das Problem mit dem Benutzerinterface ist eben immer da, wenn man mit openGL programmiert... Für C/C++ basierende OpenGL-Programme gäbs jetzt wieder tausend geniale Lösungen....


----------



## Empire Phoenix (8. Jul 2011)

Ne jme hat aber callables die er zum  manipulieren des Scenegraphen benutzen kann.

In jme3 nightly:
src/test/jme3test/TestCanvas.

Wenn du einen position x,y auf dem screen nun in die 3d welt übertragen willst kannst du diese entweder über cam auf eine fixe distanz umwandlen (aka 50 meter vor kamera unter mauszeiger) oder du aknnst mithifle von einem Ray die collision mit den grafischen objecten berechnen.


----------



## TheMuh (8. Jul 2011)

@Kr0e die Oberfläche von Civilization wäre ein Besipiel dafür, was ich realisieren will.
Ich denke halt das es für mich als jme Einsteiger leichter sein dürfte die GUI, die ja bei Civ einen grossen Teil ausmacht mittels Swing zu generien und die 3D Teile in das jme "auszulagern" ... der umgekehrte Weg verlangt von mir die ganzen Swing Komponenten in jme selber herzustellen, was mir ein zu grosser Aufwand ist ...
zb. allein ein Chat verlangt Texteingabe, ein scrollbares TextArea, einen Scrollbalken etc..etc... das is ja allein schon anspruchsvoller wie ein komplettes SpaceInvaders, naja ok fast, ich glaub du weisst was ich mein 

warum soll ich auf die swing komponenten verzichten, die mir defintiv viel Zeit einsparen würden, wenn die jme Implementation geht ...ja wenn halt ....


----------



## TheMuh (8. Jul 2011)

Empire Phoenix hat gesagt.:


> Ne jme hat aber callables die er zum  manipulieren des Scenegraphen benutzen kann.
> 
> In jme3 nightly:
> src/test/jme3test/TestCanvas.
> ...



ich habe mal probierweise in den obigen Script:
- implements RawInputListener für die HelloPicking Klasse 
- und das onMouseButtonEvent Event ... hier kann ich wohl die x und y Koordinaten auslesen

mit der "cam" variante meinst du nun die "position" auf die Mauskoordianten anpassen und die "direction" ist dann einfach eine angenommene entfernung von diesem punkt aus in die Bildfläche hinein ?
mit "ray" meinst du selbe vorgehensweise ohne den umweg über cam, sondern die mauswerte direkt in die Ray Klasse übergeben ?

nun die Koordinaten in einen Vektor zu übergeben ist ja kein Problem, aber wie passe ich die Koordianten der Maus auf die 3D Koordinaten an, wenn ich das einfach direkt in die ray übergebe trifft er ja nichts .... die xwerte der Maus sind ja etwas anderes als die xwerte des 3d raumes ...
???:L


----------



## Empire Phoenix (8. Jul 2011)

lies dir doch mal die methoden in der Camera durch, besonders diese hier:
    public Vector3f getWorldCoordinates(Vector2f screenPosition,
            float zPos, Vector3f store) 


Im wesentlichen kann mana cu mtihilfe der Rotation der Camera per quaternion rechnung den ray startpunk(der sit noch eifnach) und die direction berechnen, aber dabei ist auch mein Mathe am ende. (Aber wenn du nciht ein Mathegeni bist spar dir den aufwand lieber erstmal)


----------

