# Picking Selection auf ein Objekt begrenzen !



## JavaKing (10. Sep 2008)

Hallo zusammen,

ich habe leider wieder ein Problem :


```
public void display(GLAutoDrawable drawable) {

        gl.glSelectBuffer(MAXSELECT, selectBuf); //Den Puffer zuordnen
        gl.glRenderMode(GL_SELECT);
        // clear name stack
        gl.glInitNames(); // Name-Stack initialisieren
        gl.glPushName(~0); // "Name" "0" auf diesen Stack gelegt wird. Nötig sonst Exception
        
        IntBuffer vp = IntBuffer.allocate(15);
        gl.glGetIntegerv(GL_VIEWPORT, vp); //Die Sicht speichern
        
        gl.glMatrixMode(GL.GL_PROJECTION); 
        gl.glPushMatrix(); //Um die Matrix zu sichern
        gl.glLoadIdentity(); //Und dieselbige wieder zurückzusetzen
        glu.gluPickMatrix(x_mouse, vp.get(3) - y_mouse,  5.0, 5.0, vp);
        glu.gluPerspective(100.0,((float)width)/((float)height),0.1, 100); 
        gl.glMatrixMode(GL.GL_MODELVIEW);  // now back to model view so that we
        drawScene(gl,true);
        
        int hits = gl.glRenderMode(GL_RENDER);
        System.out.println("hits: "+hits);
          if (hits>1) {
            gl.glEnable(GL_LIGHT1);
            int index = (hits-1)*4+3;
            if ((index<0) || (index>selectBuf.capacity())) {
            } else {
                selectedName = selectBuf.get((hits-1)*4+3);
                if (selectedName==1) {
                    wu1 = true;
                    System.out.println("Name getroffen: "+selectedName+" ! ");
                }
                if (selectedName==2) {
                    wu2 = true;
                    System.out.print("Name getroffen: "+selectedName+" ! ");
                } 
                if (selectedName==3) {
                    wu3 = true;
                    System.out.print("Name getroffen: "+selectedName+" ! ");
                } 
              
            }
        }
        select = false; 
        gl.glMatrixMode(GL_PROJECTION);
        gl.glPopMatrix();  // vorherige Projektionsmatrix vom Stack laden
        gl.glMatrixMode(GL_MODELVIEW);       // now back to model view so that we
}
```


```
public void mousePressed(MouseEvent e) {
        x_mouse = e.getX();
        y_mouse = e.getY();
        //if(m!=null && !m.getWait()) 
        select = true;

       if ((e.getModifiers() & e.BUTTON1_MASK) != 0) {
             select = true;
       }
       if ((e.getModifiers() & e.BUTTON3_MASK) != 0) {
             menu.show(gui,x_mouse,y_mouse);
       }
    }
```


```
private void drawScene(GL gl, boolean select)
    { 
        gl.glPushMatrix();
        gl.glTranslated(0,-6.48,-5);
        if (select)
            gl.glLoadName(1);
        StartRotation(gl,gewuerfelt1);
        gl.glPopMatrix();
        gl.glPushMatrix();
             if(wu1){
                gl.glTranslatef(-0.6f, 0.0f,0.15f);
                 paintRotated(gl,gewuerfelt1);
            }
        gl.glPopMatrix(); 
        
        gl.glPushMatrix();
        gl.glTranslated(1,-6.48,-6);
            if (select)
                gl.glLoadName(2);
        StartRotation(gl,gewuerfelt2);
        gl.glPopMatrix();
        gl.glPushMatrix();
            if(wu2){
               gl.glTranslatef(-5.6f, 0.0f,0.15f);
               paintRotated(gl,gewuerfelt2);
            }
        gl.glPopMatrix(); 

}
```

Warum wird beim Mausklick nun egal WO ich hinklicke immer die paintRotated(gl,gewuerfelt1); ausgeführt ?? Also gl.glLoadName(1); ??
Er sagt mir immer Hits=2, das ist ja soweit klar, leider weiß ich nicht wie ich den Bereich eingrenzen kann. Den er soll das nur ausführen wenn Würfel1 geklickt wird.


----------



## Marco13 (10. Sep 2008)

Hab unter JOGL noch nichts mit Picking gemacht (nur mal ein bißchen, unter "normalem" OpenGL). Grob für den Überblick:

```
int index = (hits-1)*4+3;
            if ((index<0) || (index>selectBuf.capacity())) {
            } else {
                selectedName = selectBuf.get((hits-1)*4+3);
                if (selectedName==1) {
                    wu1 = true;
                    System.out.println("Name getroffen: "+selectedName+" ! ");
```

Was wird dort ausgegeben ... und was bedeutet das magische (hits-1)*4+3 !?!


----------



## JavaKing (10. Sep 2008)

Marco13 hat gesagt.:
			
		

> Hab unter JOGL noch nichts mit Picking gemacht (nur mal ein bißchen, unter "normalem" OpenGL). Grob für den Überblick:
> 
> ```
> int index = (hits-1)*4+3;
> ...



Ausgegeben wird :
hits: 3
Name getroffen: 2 ! 


Zur Erläuterung des "magischen " :

http://wiki.delphigl.com/index.php/Tutorial_Selection

Um nun nur das Objekt herauszubekommen, welches der Kamera am nächsten lag, ließt man nun in einer Schleife alle Z-Werte "Puffer[(i*4)+1]" der getroffenen Objekte aus und vergleicht sie mit dem bislang niedrigsten Wert. Ist der ausgelesene Wert niedriger, als der bislang kleinste, dann wird der neue Z-Wert gespeichert und der Name des bislang nächsten Objektes wird gespeichert.
Ist die Schleife durchgelaufen, dann steht am Ende also in der Variable "getroffen" der Integer-Name des nächsten, getroffenen Objektes, welches wir dann auch als Rückgabewert der Funktion verwenden. 



Soweit die Theorie *g*


----------



## Marco13 (11. Sep 2008)

Also gibt er bei "Name getroffen" doch das aus, was angeklickt wurde (d.h. die Nummer des Würfels)? Dann könnte man sich doch irgendwo merken, dass NUR der mit Index 1 ausgewählt werden können soll!?  ???:L


----------



## JavaKing (11. Sep 2008)

> Also gibt er bei "Name getroffen" doch das aus



Wie oben angegeben im Source Code. Ist mein Würfel 2 auch "Name 2". Mein Problem ist aber egal wo ich in die szene hinklicke ob auf würfel 1 oder würfel 2 oder sonst wohin was man sieht. Er gibt immer "Name 2" zurück !!
z.b. auch niemals Name 1.

Entweder ich speicher die Szene falsch ab :

```
gl.glGetIntegerv(GL_VIEWPORT, vp); //Die Sicht speichern
```

oder das klappt nicht wie gewünscht.


```
gl.glPushMatrix();
        gl.glTranslated(0,-6.48,-5);
[b]        if (select)
            gl.glLoadName(1);[/b]
        StartRotation(gl,gewuerfelt1);
        gl.glPopMatrix();
        gl.glPushMatrix();
             if(wu1){
                gl.glTranslatef(-0.6f, 0.0f,0.15f);
                 paintRotated(gl,gewuerfelt1);
            }
        gl.glPopMatrix();
```


----------



## Marco13 (11. Sep 2008)

Sorry, da müßt' ich mir selbst den Picking-Kram nochmal (an einem KSKB) ansehen, da werd' ich aber die nächsten Wochen kaum dazu kommen.... Im Moment kapier ich nichtmal warum er da "hits 3" ausgeben soll, aber dann nur EINE Zeile mit "Name getroffen" ...... Es müßten ja mehrerer Objekt-Infos im "Puffer" liegen (und du betrachtest immer nur die letzte, mit "hits-1")


----------



## Fancy (11. Sep 2008)

Moin,

also auf Anhieb sehe ich leider auch nicht warum sich der obige code nicht wie erwartet verhält. Aber beim drüber sehen ist mir folgendes aufgefallen:

1.	wu* wird immer nur auf true gesetzt, wird dies auch "sinnvoll" wieder zurückgesetzt?
2.	Du hast eine Objektvariable "select", diese wird gesetzt, aber nie gelesen.
3.	Obwohl Du mehrere hits hast, betrachtest Du immer nur den letzten.
4.	hat gluPerspective die selben Parameter wie im normal render mode?
5.	Du benutzt so häufig glPushMatrix() / glPopMatrix() dann könntest Du auch äquivalent glPushName() / glPopName() nutzen.
6.	Was macht die Methode "StartRotation()" zeichnet diese?
Falls nein: Warum wird ein glTranslate drauf angewendet?
Falls ja: Dir ist bewusst das glLoadName gültig bleibt, bis zur nächsten Änderung des NameStack? 

Wahrscheinlich nützt Dir dies jedoch alles nichts, da hits maximal Eins sein sollte (bei Dir) und keiner der oben angesprochenen Punkte führt unmittelbar dazu das zu viele hits gefunden werden.

Aber als Hintergrundinfo zur Analyse: Das Zusammenspiel von glRenderMode(GL_SELECT), gluPickMatrix() und gluPersective() dient einzig und alleine dazu die OpenGL Render Pipeline soweit einzugrenzen das nicht mehr die gesamte Szene gerendert wird, sondern nur noch ein (in Deinem Fall) 5x5 Pixel großes Rechteck um den letzten Mausklick herum. Alle Elemente die in diesem Rechteck sichtbar wären, bekommen dann einen Eintrag in Deinem "selectBuf".

So wie Du schreibst, sollte in diesem Rechteck nur ein Element sichtbar sein. OpenGL ist aber der Meinung es wären Drei. Dummerweise wird innerhalb des glRenderMode(GL_SELECT) nicht in den Framebuffer gerendert, so das nicht direkt sichtbar ist was OpenGL da "sieht". Wenn Du jedoch innerhalb Deiner display() das glRenderMode(GL_SELECT)  auskommentierst, wird wieder in den Framebuffer gerendert (dann geht das Picking natürlich nicht mehr). Wenn Du dann noch Dein gluPickMatrix() auf 100x100 Pixel änderst, siehst Du nach einem Mausklick exakt den Bereich aufblitzen den OpenGL für das Picking heranzieht (und zwar in voller Fenstergröße). Und dann kannst Du versuchen herauszubekommen was OpenGL da "sieht" und warum es nicht das ist was Du glaubst.

Alternativ kannst Du auch einen Blick auf das folgende Beispiel werfen und versuchen ob Du einen Unterschied zwischen Deinem Code und diesem Beispiel findest. Evtl. findest Du dann ja auch Deinen Fehler.


```
package fancy.jf.pick;

import java.awt.Frame;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.BufferUtil;

public class Pick implements GLEventListener, MouseListener {

    /*
     * quick and dirty inner named cube representation
     */
    private class Cube {

        private static final int animationFrames = 120;

        private final float[]    staticVertices  = new float[] {
            1.0f,  1.0f,  1.0f,  -1.0f,  1.0f,  1.0f,  -1.0f, -1.0f,  1.0f,   1.0f, -1.0f,  1.0f,
            1.0f, -1.0f, -1.0f,  -1.0f, -1.0f, -1.0f,  -1.0f,  1.0f, -1.0f,   1.0f,  1.0f, -1.0f};
        
        private final int[]      staticIndices   = new int[] {
            0, 1, 2, 3,  4, 5, 6, 7,  7, 6, 1, 0,  3, 2, 5, 4,  1, 6, 5, 2,  7, 0, 3, 4};
        
        
        private GL               gl              = null;

        private FloatBuffer      vaVertices      = null;
        private IntBuffer        vaIndices       = null;
        private int              angleRotate     = 0;
        private int              name            = 0;


        private Cube(GL gl, int name) {

            this.gl = gl;
            this.name = name;

            // prepare vertex array
            vaVertices = BufferUtil.newFloatBuffer(staticVertices.length);
            vaVertices.put(staticVertices, 0, staticVertices.length);
            vaVertices.rewind();

            vaIndices = BufferUtil.newIntBuffer(staticIndices.length);
            vaIndices.put(staticIndices, 0, staticIndices.length);
            vaIndices.rewind();
        }
        
        
        private void start() {

            angleRotate = animationFrames;
        }


        private void render() {

            if (angleRotate > 0) angleRotate--;

            // draw named cube
            gl.glPushMatrix();
            gl.glPushName(name);
            gl.glColor3f(0.9f, 0.9f, 0.9f);
            gl.glTranslatef(name * 3.0f - Pick.quantityCube * 1.5f + 1.5f, 0.0f, 0.0f);
            gl.glRotatef(angleRotate, 1.0f, 1.0f, 1.0f);
            gl.glVertexPointer(3, GL.GL_FLOAT, 0, vaVertices);
            gl.glDrawElements(GL.GL_QUADS, staticIndices.length, GL.GL_UNSIGNED_INT, vaIndices);
            gl.glPopName();
            gl.glPopMatrix();
        }

    }
    

    
    private static final int quantityCube = 5;
    private Cube[]           cubes        = null;

    private GL               gl           = null;
    private GLU              glu          = null;
    private IntBuffer        viewport     = null;
    private IntBuffer        selection    = null;

    private int              mouseX       = -1;
    private int              mouseY       = -1;


    @Override
    public void init(GLAutoDrawable drawable) {

        gl = drawable.getGL();
        glu = new GLU();

        // for GL_SELECT render mode
        viewport = BufferUtil.newIntBuffer(4);
        selection = BufferUtil.newIntBuffer(64);

        // basic gl initialization
        gl.glShadeModel(GL.GL_SMOOTH);
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
        gl.glClearDepth(1.0f);
        gl.glEnable(GL.GL_DEPTH_TEST);
        gl.glDepthFunc(GL.GL_LEQUAL);
        gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
        gl.glEnableClientState(GL.GL_VERTEX_ARRAY);

        gl.glPolygonMode(GL.GL_BACK, GL.GL_LINE);
        gl.glPolygonMode(GL.GL_FRONT, GL.GL_LINE);

        cubes = new Cube[quantityCube];
        for (int i = 0; i < quantityCube; i++)
            cubes[i] = new Cube(gl, i);
    }


    @Override
    public void display(GLAutoDrawable drawable) {

        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();
        glu.gluLookAt(0, 0, 12, 0, 0, 0, 0, 1, 0);

        // additional selection render pass if necessary
        if (mouseX >= 0 || mouseY >= 0) {

            gl.glSelectBuffer(selection.capacity(), selection);
            gl.glRenderMode(GL.GL_SELECT);
            gl.glInitNames();

            // setup new selection render matrices
            gl.glMatrixMode(GL.GL_PROJECTION);
            gl.glPushMatrix();
            gl.glLoadIdentity();
            glu.gluPickMatrix(mouseX, mouseY, 8, 8, viewport);
            glu.gluPerspective(45.0f, (float) viewport.get(2) / (float) viewport.get(3), 1.0, 50.0);
            gl.glMatrixMode(GL.GL_MODELVIEW);

            // render cubes to selection buffer
            for (final Cube cube : cubes)
                cube.render();

            // back to normal render mode
            gl.glMatrixMode(GL.GL_PROJECTION);
            gl.glPopMatrix();
            gl.glMatrixMode(GL.GL_MODELVIEW);
            gl.glFlush();

            final int hits = gl.glRenderMode(GL.GL_RENDER);

            // handle hits
            for (int i = 0; i < hits; i++)
                cubes[selection.get(i * 4 + 3)].start();

            // no more selection render pass necessary
            mouseX = -1;
            mouseY = -1;
        }

        // always render to normal backbuffer
        for (final Cube cube : cubes)
            cube.render();
    }


    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {

        if (height <= 0) height = 1;

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(45.0f, (float) width / (float) height, 1.0, 50.0);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();

        // save viewport for selection render pass
        gl.glGetIntegerv(GL.GL_VIEWPORT, viewport);
    }

    
    @Override
    public void mousePressed(MouseEvent e) {
        
        mouseX = e.getX();
        mouseY = viewport.get(3) - e.getY();
    }

    
    @Override
    public void mouseClicked(MouseEvent e) {}


    @Override
    public void mouseEntered(MouseEvent e) {}


    @Override
    public void mouseExited(MouseEvent e) {}


    @Override
    public void mouseReleased(MouseEvent e) {}


    @Override
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}


    public static void main(String[] args) {

        final Pick pick = new Pick();
        final Frame frame = new Frame();
        final GLCanvas canvas = new GLCanvas();
        final Animator animator = new Animator(canvas);
        canvas.addGLEventListener(pick);
        canvas.addMouseListener(pick);
        frame.add(canvas);
        frame.setSize(1000, 500);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                new Thread(new Runnable() {
                    public void run() {
                        animator.stop();
                        System.exit(0);
                    }
                }).start();
            }
        });
        frame.setVisible(true);
        animator.start();
        canvas.requestFocusInWindow();
    }
}
```

Gruß,
Michael


----------

