Screen-Koordinaten in 3D-Koordinaten umwandeln

Started by BFK, February 01, 2013, 01:47:14 PM

Previous topic - Next topic

BFK

Hallo,
Ich experementiere gerade mit der jPCT-Engine ein wenig rum und stehe schon vor einem wesentlichen wichtigem Problem. Ich möchte gern Screen-Koordinaten in 3D-Koordinaten umwandeln (Das umgekehrte funktioniert einwandfrei). Mein Ziel ist es ein Object3D an der gedrückten stelle zu bewegen. Ich weiss es gibt viele Beiträge zu diesem Thema. Habe mir zum Beispiel den Picking-Beitrag gelesen http://www.jpct.net/wiki/index.php/Picking , aber auch andere Beiträge hier im Forum. Leider kriege ich das nicht zum Laufen. Vielleicht könnte mir jemand dabei helfen.

Hier erstmal der Code:

private SimpleVector convertScreenPixelsTo3D(int x, int y)
{
        SimpleVector direction = new SimpleVector( Interact2D.reproject2D3DWS(world.getCamera(), fb, x, y ) ).normalize();

        float distance = world.calcMinDistance(world.getCamera().getPosition(), direction, 10000 );
       
        SimpleVector point = new SimpleVector( direction );
        point.scalarMul( distance );
        point.add(world.getCamera().getPosition() );
       
       
        return point;
     }


Bei onTouch-Event "ACTION_DOWN" habe ich dann folgendes:

int x = (int)me.getX();
        int y = (int)me.getY();
       
        SimpleVector sv = convertScreenPixelsTo3D(x, y);
        Log.i(TEST-JPCT,"Point in Screen Pixels..: ("+x+","+y+")");
        Log.i(TEST-JPCT,"Point in 3D..: "+sv.toString());
       
        poly.clearTranslation();
        poly.translate(sv);


wobei "poly" ein Object3D ist.

Wie es aussieht sind die Berechnungen von "convertScreenPixelsTo3D" falsch, denn poly bewegt sich zwar, aber lerider nicht an der gewünschten (ge-touchten Stelle).

Was mache ich falsch..?

EgonOlsen

Hmm...sieht auf den ersten Blick eigentlich ok aus. Hier ist mal ein einfaches Beispiel dazu, vielleicht hilft das weiter:


import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.JFrame;

import com.threed.jpct.*;
import com.threed.jpct.util.Light;


public class MouseFollowDemo
   extends JFrame
   implements MouseMotionListener, MouseListener
{

   private static final long serialVersionUID = 1L;
   private Graphics g = null;
   private FrameBuffer fb = null;
   private World world = null;
   private Object3D plane = null;
   private Object3D ramp = null;
   private Object3D player = null;
   private Object3D cube2 = null;
   private Object3D sphere = null;

   private boolean doloop = true;

   private int mouseX = 320;
   private int mouseY = 240;


   public MouseFollowDemo()
   {
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     pack();
     setSize(640, 480);
     setResizable(false);
     setLocationRelativeTo(null);
     setVisible(true);
     addMouseMotionListener(this);
     addMouseListener(this);
     g = getGraphics();
   }


   @Override
   public void mouseMoved(MouseEvent m)
   {
     mouseX = m.getX();
     mouseY = m.getY();
   }


   @Override
   public void mouseDragged(MouseEvent m)
   {
   //
   }


   @Override
   public void mouseClicked(MouseEvent e)
   {
   //
   }


   @Override
   public void mouseEntered(MouseEvent e)
   {
   //
   }


   @Override
   public void mouseExited(MouseEvent e)
   {
   //
   }


   @Override
   public void mousePressed(MouseEvent e)
   {
     //
   }


   @Override
   public void mouseReleased(MouseEvent e)
   {
   //
   }


   private void initStuff()
   {
     fb = new FrameBuffer(640, 480, FrameBuffer.SAMPLINGMODE_NORMAL);
     world = new World();
     fb.enableRenderer(IRenderer.RENDERER_SOFTWARE);

     ramp = Primitives.getCube(20);
     ramp.setAdditionalColor(Color.RED);
     plane = Primitives.getPlane(20, 10);
     plane.setAdditionalColor(Color.GREEN);
     sphere = Primitives.getSphere(30);
     sphere.setAdditionalColor(Color.CYAN);
     sphere.translate(-50, 10, 50);
     cube2 = Primitives.getCube(20);
     cube2.setAdditionalColor(Color.ORANGE);
     cube2.translate(60, -20, 60);

     plane.rotateX((float) Math.PI / 2f);
     ramp.rotateX((float) Math.PI / 2f);

     player = Primitives.getCone(3);
     player.rotateX((float) Math.PI / 2f);
     player.rotateMesh();
     player.clearRotation();

     plane.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
     ramp.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
     sphere.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
     cube2.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);

     cube2.setBillboarding(true);

     world.addObject(plane);
     world.addObject(ramp);
     world.addObject(sphere);
     world.addObject(cube2);
     world.addObject(player);

     player.translate(-50, -10, -50);

     Light light = new Light(world);
     light.setPosition(new SimpleVector(0, -80, 0));
     light.setIntensity(40, 25, 22);

     world.setAmbientLight(10, 10, 10);
     world.buildAllObjects();
   }


   private void relocate()
   {
     SimpleVector pos = getWorldPosition();
     if (pos != null)
     {
       player.clearTranslation();
       player.translate(pos);
     }
   }


   private SimpleVector getWorldPosition()
   {
     SimpleVector pos = null;
     SimpleVector ray = Interact2D.reproject2D3DWS(world.getCamera(), fb, mouseX, mouseY);
     if (ray != null)
     {
       SimpleVector norm = ray.normalize(); // Just to be sure...

       float f = world.calcMinDistance(world.getCamera().getPosition(), norm, 1000);
       if (f != Object3D.COLLISION_NONE)
       {
         SimpleVector offset = new SimpleVector(norm);
         norm.scalarMul(f);
         norm = norm.calcSub(offset);
         pos = new SimpleVector(norm);
         pos.add(world.getCamera().getPosition());
       }
     }
     return pos;
   }


   private void doIt()
     throws Exception
   {
     Camera cam = world.getCamera();
     cam.moveCamera(Camera.CAMERA_MOVEOUT, 100);
     cam.moveCamera(Camera.CAMERA_MOVEUP, 160);
     cam.lookAt(plane.getTransformedCenter());

     world.getLights().setOverbrightLighting(Lights.OVERBRIGHT_LIGHTING_DISABLED);

     while (doloop)
     {
       relocate();
       fb.clear();
       world.renderScene(fb);
       world.draw(fb);
       fb.update();
       fb.display(g);
       Thread.sleep(10);
     }
     fb.disableRenderer(IRenderer.RENDERER_OPENGL);
     System.exit(0);
   }


   public static void main(String[] args)
     throws Exception
   {
     MouseFollowDemo cd = new MouseFollowDemo();
     cd.initStuff();
     cd.doIt();
   }
}

BFK

Hallo EgonOlsen und danke für deine schnelle Antwort..!

Habe dein Beispiel mal ausprobiert und es funktioniert einwandfrei.

Habe auch die getWorldPosition-Methode bei mir ausprobiert und die liefert die gleichen Werte.

Entweder ist mir Androids onTouch-Koordinaten zu ungenau oder ich weiss auch nicht was noch falsch sein kann.

Hätte aber noch ne Frage. Wenn ich ein Object3D per Hand verschieben will, welche Weise ist denn da am Besten..? Die standart ACTION_DOWN, ACTION_MOVE, ACTION_UP - Methode oder gibt es da was spezifisches für jPCT..?




EgonOlsen

Quote from: BFK on February 01, 2013, 06:15:10 PM
Entweder ist mir Androids onTouch-Koordinaten zu ungenau oder ich weiss auch nicht was noch falsch sein kann.
Leider beschreibst du nicht genau, wie sich das Problem bei dir äußert. Ich rate mal ein wenig: Du hast die Notification Bar sichtbar? In dem Fall beginnt der Bildschirm koordinatentechnisch unter der Leiste, aber die Touchkoordinaten starten erst darunter. Du musst die Größe der Leiste zu den Daten des Events addieren. Wenn es das nicht ist, beschreib doch bitte mal genauer, was das Problem ist.
Quote from: BFK on February 01, 2013, 06:15:10 PM
Hätte aber noch ne Frage. Wenn ich ein Object3D per Hand verschieben will, welche Weise ist denn da am Besten..? Die standart ACTION_DOWN, ACTION_MOVE, ACTION_UP - Methode oder gibt es da was spezifisches für jPCT..?
Das hängt letztendlich von deinen Anforderungen ab, aber irgendwas spezielles gibt es da nicht. jPCT interessiert sich nicht für Touchevents und arbeitet einfach mit den Werten, die du übergibst. Wo die herkommen, obliegt dir. Achte nur darauf, dass du nicht direkt in den Eventhandlern an jPCT-Objekten arbeitest, weil die Events parallel zum Renderthread laufen und du sonst alle Arten von "lustigen" Seiteneffekten bekommst. Es ist am besten, in den Eventhandler-Methoden nur Flags/Werte zu setzen und diese in onDrawFrame bzw. auszuwerten.

BFK

Quote from: EgonOlsen on February 01, 2013, 09:51:12 PM
Leider beschreibst du nicht genau, wie sich das Problem bei dir äußert. Ich rate mal ein wenig: Du hast die Notification Bar sichtbar? In dem Fall beginnt der Bildschirm koordinatentechnisch unter der Leiste, aber die Touchkoordinaten starten erst darunter. Du musst die Größe der Leiste zu den Daten des Events addieren. Wenn es das nicht ist, beschreib doch bitte mal genauer, was das Problem ist.
Gut geraten...! Das war mein Problem. Bei Fullscreen funktioniert alles einwandfrei..!

Quote from: EgonOlsen on February 01, 2013, 09:51:12 PM
Das hängt letztendlich von deinen Anforderungen ab, aber irgendwas spezielles gibt es da nicht. jPCT interessiert sich nicht für Touchevents und arbeitet einfach mit den Werten, die du übergibst. Wo die herkommen, obliegt dir. Achte nur darauf, dass du nicht direkt in den Eventhandlern an jPCT-Objekten arbeitest, weil die Events parallel zum Renderthread laufen und du sonst alle Arten von "lustigen" Seiteneffekten bekommst. Es ist am besten, in den Eventhandler-Methoden nur Flags/Werte zu setzen und diese in onDrawFrame bzw. auszuwerten.
Werd ich mir merken..!

Und nochmals vielen, vielen DANK für deine Hilfe..!

BFK

Hallo,
Sorry, dass ich wieder nerven muss, aber damit ich keinen neuen Thread aufmache, hab ich mir gedacht meine Frage hier zu stellen.

Ich habe ein komisches Problem, wenn ich ein Object3D verschiebe. Und zwar verschiebt sich mein Object3D ohne Probleme, aber nach einer Weile verschwindet es von der Bildfläche.

Hier mal der Codeabschnitt:

public boolean onTouchEvent(MotionEvent me) {

if (me.getAction() == MotionEvent.ACTION_DOWN)
{
xpos = me.getX();
ypos = me.getY();

touched_poly = check_which_poly_clicked((int)xpos, (int)ypos);

if(touched_poly != null) // nutzer touches ein polygon
{
move = true;
}

return true;
}
if (me.getAction() == MotionEvent.ACTION_UP) {
move = false;

return true;
}

if (me.getAction() == MotionEvent.ACTION_MOVE)
{
xpos = me.getX();
ypos = me.getY();


return true;
}


return super.onTouchEvent(me);
}




...wobei ich erklären muss, dass "check_which_poly_clicked" eine Methode ist, die prüft, welches "Object3D" berührt wurde und es auch dann zurückliefert (wenn wirklich auf ein Objekt getouched wurde).

Meine "onDrawFrame"-Methode sieht dann so aus..:

public void onDrawFrame(GL10 gl) {


fb.clear(back);
world.renderScene(fb);
world.draw(fb);
fb.display();


if(move && touched_poly != null)
{
SimpleVector movePoint = convertScreenPixelsTo3D((int)xpos, (int)ypos);
movePoint.z = 0;

touched_poly.clearTranslation();
touched_poly.translate(movePoint);

}
...
}


Der obere Code funktioniert leider nicht einwandfrei. Wenn man zuviel das Objekt hin und her bewegt, verschwindet es irgendwann ganz. Was mache ich falsch..?
Wäre auf ein Hinweis dankbar..!

EgonOlsen

So ist das schwer zu sagen. Ich denke mal, du machst irgendwas, was movePoint aus dem Ruder laufen lässt. Dazu wäre es sinnvoll zu wissen, welchen Wert movePoint denn jeweils hat.

BFK

Das ist ja das Komsiche. Nachdem das Object3D verschwindet, sehe ich, dass seine Position (also der Wert von getTranslation) immer zu gross ist, zum Beispiel sieht er so aus..:

(X,Y,Z): (7.578068E10,3.752948E11,0.0)

Meistens passiert es wenn ich das Objekt zu schnell bewege.

EgonOlsen

Ja, dann liefert dein convertScreenPixelsTo3D() vermutlich Nonsense. Kannst du den Code der Methode mal posten?

BFK

Ja, hier der Code-Abschnitt..:


private SimpleVector convertScreenPixelsTo3D(int x, int y)
{

        SimpleVector direction = new SimpleVector( Interact2D.reproject2D3DWS(world.getCamera(), fb, x, y ) ).normalize();


        float distance = world.calcMinDistance(world.getCamera().getPosition(), direction, 10000 );
       
        SimpleVector point = new SimpleVector( direction );
        point.scalarMul( distance );
        point.add(world.getCamera().getPosition() );
       
       
        return point;
}

EgonOlsen

Du musst prüfen, ob calcMinDistance überhaupt was trifft. Der Rückgabewert ist beim Nicht-Treffen Object3D.COLLISION_NONE und damit sollte man keinen Vektor multiplizieren... ;)

BFK