Picking Buffer always empty.

Started by Wavewash, January 05, 2006, 22:09:56

Previous topic - Next topic

Wavewash

I have been playing with picking for a few days now and have not been able to get it to work. I can get it so that the computer responds when my mouse is over geometry which is in the picking buffer but I cannot get a select buffer which has the numbers from the name stack in it.

The following is my selection code, basically cut and pasted from the Nehe lesson 32. Except for the rendering code is my own. I also removed the check to see whether the mouse is down before the selection is done. So it just does a constant check where ever the mouse is. The hit count actually increses when over geometry that I have defined but when I go to check the selection buffer it's empty and only prints out 0's. Since I am able to get an increase in hit count when over geometry I think that my picking projection matrix is set up properly . My only problem is getting the selection buffer to have information in it with the hit records.

private void selection() {                                            // This Is Where Selection Is Done
        int buffer[] = new int[512];                                        // Set Up A Selection Buffer
        int hits;                                               // The Number Of Objects That We Selected

        // The Size Of The Viewport. [0] Is <x>, [1] Is <y>, [2] Is <length>, [3] Is <width>

        int viewport[] = new int[4];

        // This Sets The Array <viewport> To The Size And Location Of The Screen Relative To The Window
        IntBuffer temp = ByteBuffer.allocateDirect(64).order(ByteOrder.nativeOrder()).asIntBuffer();
        temp.order();
        GL11.glGetInteger(GL11.GL_VIEWPORT, temp);
        temp.get(viewport);
        temp = ByteBuffer.allocateDirect(2048).asIntBuffer();
        GL11.glSelectBuffer(temp);                                // Tell OpenGL To Use Our Array For Selection
        temp.get(buffer);

        // Puts OpenGL In Selection Mode. Nothing Will Be Drawn.  Object ID's and Extents Are Stored In The Buffer.
        GL11.glRenderMode(GL11.GL_SELECT);

        GL11.glInitNames();                                              // Initializes The Name Stack
        GL11.glPushName(0);                                              // Push 0 (At Least One Entry) Onto The Stack

        GL11.glMatrixMode(GL11.GL_PROJECTION);                                // Selects The Projection Matrix
        GL11.glPushMatrix();                                             // Push The Projection Matrix
        GL11.glLoadIdentity();                                           // Resets The Matrix
        
        int mouse_x=Mouse.getX();
        int mouse_y=Mouse.getY();

        // This Creates A Matrix That Will Zoom Up To A Small Portion Of The Screen, Where The Mouse Is.
        GLU.gluPickMatrix((float) mouse_x, (float) (viewport[3] - mouse_y), 1.0f, 1.0f, viewport);

        // Apply The Perspective Matrix
        GLU.gluPerspective(45.0f, (float) (viewport[2] - viewport[0]) / (float) (viewport[3] - viewport[1]), 0.1f, 100.0f);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);                                 // Select The Modelview Matrix
        
        
        GL11.glLoadIdentity();
        GL11.glLoadName(5);
        WUnit.setpos(0.0f, 0.0f, -2.0f);
        GL11.glColor3f(1.0f, 1.0f, 1.0f);
        WUnit.DrawTextBG();
        GL11.glColor3f(0.0f, 1.0f, 0.0f);
        WUnit.DrawText();
        
        GL11.glLoadIdentity();
        GL11.glLoadName(6);
        WUnit.setpos(1.0f, 0.0f, -2.0f);
        GL11.glColor3f(1.0f, 1.0f, 1.0f);
        WUnit.DrawTextBG();
        GL11.glColor3f(0.0f, 1.0f, 0.0f);
        WUnit.DrawText();
        
        
        
        GL11.glMatrixMode(GL11.GL_PROJECTION);                                // Select The Projection Matrix
        GL11.glPopMatrix();                                              // Pop The Projection Matrix
        GL11.glMatrixMode(GL11.GL_MODELVIEW);                                 // Select The Modelview Matrix
        hits = GL11.glRenderMode(GL11.GL_RENDER);                               // Switch To Render Mode, Find Out How Many
                                                                    // Objects Were Drawn Where The Mouse Was
        if(hits > 0) {                                               // If There Were More Than 0 Hits
            int choose = buffer[3];                                 // Make Our Selection The First Object
            int depth = buffer[1];                                  // Store How Far Away It Is

            for (int i = 1; i < hits; i++) {                // Loop Through All The Detected Hits
                // If This Object Is Closer To Us Than The One We Have Selected
                if (buffer[i * 4 + 1] < (int)depth) {
                    choose = buffer[i * 4 + 3];                      // Select The Closer Object
                    depth = buffer[i * 4 + 1];                       // Store How Far Away It Is
                }
            }
            
            System.out.println(choose);

        }
    }


Any help would be appreciated, thanks!

Wavewash

I've tried the Picking with LWJGL for awhile now. Has anyone gotten this to work? The Nehe picking demo translated over to LWJGL is broken so I have no way to verify that LWJGL is broken. Please let me know if you have got picking to working using GL11.glRenderMode(GL11.GL_SELECT);. Thanks

Evil-Devil

The last time i tried picking was as i translated the RedBook Picking example to lwjgl and IIRC it worked.

seven_dc

Quote from: "Evil-Devil"The last time i tried picking was as i translated the RedBook Picking example to lwjgl and IIRC it worked.
Care to share the source code with us?

Wavewash

Quote from: "seven_dc"
Quote from: "Evil-Devil"The last time i tried picking was as i translated the RedBook Picking example to lwjgl and IIRC it worked.
Care to share the source code with us?

Let's see if I can do the same. I'll post my code.

~Mo

thirstynellan

I, too, have had some troubles getting picking to work.  The sample code from the red book (in C) works just fine for me, but when I try to do picking in LWJGL, I get a null pointer exception when I call:
GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);

According to the Eclipse debugger, the exception is occurring on line 1198 in GL11.java (version 0.99):
long function_pointer = GLContext.getCapabilities().GL11_glGetIntegerv_pointer;

Has anyone else seen this kind of thing before?

BTW, the previous entry in this thread said something about sample code being available, but I'm not sure where to find it.

Thanks in advance for any advice anyone could offer!
--Geoff

Fool Running

Make sure you call Display.Create() and are using OpenGL entirely in one thread. Those are the 2 most common causes of the null exception you are getting.
Programmers will, one day, rule the world... and the world won't notice until its too late.Just testing the marquee option ;D

darkprophet

Quote
Make sure you call Display.Create() and are using OpenGL entirely in one thread.

Not unless your using CVS LWJGL, you can call Display.releaseContext() from the thread that did the Display.create(); and then call Display.makeCurrent(); on the rendering thread...But thats getting complicated :)

DP

thirstynellan

Ah... I was doing two things wrong that caused the NullPointerException:
(1) I was calling glGetInteger from outside the correct thread
(2) I was not allocating enough space in my IntBuffer to receive the result.
So now myprogram doesn't crash anymore.  :)

However, now I've run into the problem that started this thread in the first place; namely, the pick buffer comes up empty.  :?  Does anyone have some sample picking code in LWJGL that I could refer to?  There was some mentioned in an earlier post, but I'm not sure where to find it.

Thanks in advance for any input/guidance/wisdom you could offer!
--Geoff

thirstynellan

Eureka!  :D  I found out why my code was producing an empty picking buffer, and it's probably the same reason that Wavewash had trouble too.  Here's the analysis:

Notice that in his code (I use masculine pronouns for convenience), he's using passing an IntBuffer object to glSelectBuffer() as mandated by LWJGL. He then immediately puts the contents of the IntBuffer into an int array, and then uses that array in the picking-evaluation code.  However, OpenGL is writing the contents of the pick buffer to the original IntBuffer object, *not* to the int array.  Wavewash should change his code to read the pick buffer from the IntBuffer directly; or at least copy the IntBuffer into an array *after* glRenderMode(GL_RENDER) returns.

Thoughts?  Does that make sense?  Or did I misunderstand the issue?

--Geoff

laDanz

*up*

has anyone a working solution on the problem???

i 'll find it great if you could post the code

Thanxxx ...


amoeba

I am trying to get this picking working. I am registering hits correctly, but cant seem to get the pick buffer working correctly.

The whole intbuffer confuses me (why do we have to use this), but I am using the NeHe picking demo code (http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=32) LWJGL port code. I cant get the port working - assume it is broken. The author is Mark Bernard, date: 07-Sept-2004.

Are there any working demo's/tutorials/examples please?

amoeba

Right, after THREE years (I gave up 3 years ago!) I have finally got it working!

It looks to me like a fundemental problem with the IntBuffer code;

    int buffer[] = new int[256];  // Set Up A Selection Buffer
    IntBuffer temp = ByteBuffer.allocateDirect(64).order(ByteOrder.nativeOrder()).asIntBuffer();
    temp.order();
    GL11.glGetInteger(GL11.GL_VIEWPORT, temp);
    temp.get(viewport);
//    temp = ByteBuffer.allocateDirect(1024).asIntBuffer();
    temp = ByteBuffer.allocateDirect(1024).order(ByteOrder.nativeOrder()).asIntBuffer();
    GL11.glSelectBuffer(temp);	// Tell OpenGL To Use Our Array For Selection
    //temp.get(buffer);


So as you can see, I have changed the 2nd initialising of the temp IntBuffer. I also commented out the final temp.get(buffer), and moved it way down to immediately before I check the buffer for testing - i.e. after all rendering.

This seems to work for me - anyone wanting more information give me a private msg.

yahivin

I spent all day trying to get picking and no one had posted a working code sample for LWJGL, so I figure I can save others some time.

Picking Test:

/* 
 * Copyright (c) 2002-2004 LWJGL Project
 * 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 'LWJGL' 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.
 */

/*
 * PickingTest.java
 * This was modified from some example code on LWJGL sample and forums code.
 * Created on Sep 6, 2007, 4:00:20 PM
 */

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.glu.GLU;

/**
 *
 * @author tomu
 */

public class PickingTest {
	/** The normal title of the window */
	private String WINDOW_TITLE = "Picking Test";

	/** The width of the game display area */
	private int	width	= 800;

	/** The height of the game display area */
	private int	height = 600;
	
	/** The time at which the last rendering looped started from the point of view of the game logic */
	private long lastLoopTime = getTime();

	/** The time since the last record of fps */
	private long lastFpsTime = 0;

	/** The recorded fps */
	private int	fps;

	private static long timerTicksPerSecond = Sys.getTimerResolution();
	
	private boolean lastMouseDown = false;

      /** Creates a new instance of Main */
      public PickingTest() {
		initialize();
      }

      /**
       * @param args the command line arguments
       */
      public static void main(String[] args) {
		new PickingTest().execute();
      }
	
	private void execute() {
		try {
			gameLoop();
		} catch(Exception ex) {
			ex.printStackTrace();
		} finally {
			Display.destroy();
		}
	}
	
	/**
	 * Run the main game loop. This method keeps rendering the scene
	 * and requesting that the callback update its screen.
	 */
	private void gameLoop() {
		while (Game.gameRunning) {
			// Game Logic
			gameLogic();
			
			// Game Rendering
			// clear screen
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
			
			GL11.glMatrixMode(GL11.GL_MODELVIEW);
			GL11.glLoadIdentity();
			DrawEntities();
		
			// update window contents
			Display.update();
		}
	}
	/**
	 * Notification that a frame is being rendered. Responsible for
	 * running game logic and rendering the scene.
	 */
	public void gameLogic() {
		int mx = Mouse.getX(), my = Mouse.getY();
			
		//SystemTimer.sleep(lastLoopTime+10-SystemTimer.getTime());
		Display.sync(60);

		// work out how long its been since the last update, this
		// will be used to calculate how far the entities should
		// move this loop
		long delta = getTime() - lastLoopTime;
		lastLoopTime = getTime();
		lastFpsTime += delta;
		fps++;
		
		// update our FPS counter if a second has passed
		if (lastFpsTime >= 1000) {
			Display.setTitle(WINDOW_TITLE + " (FPS: " + fps + "; mx: "+mx+"; my: "+my+")");
			lastFpsTime = 0;
			fps = 0;
		}
		
		// Selection Magic
		if(Mouse.isButtonDown(0) && !lastMouseDown) {
			selection(mx, my);
		}
		
		lastMouseDown = Mouse.isButtonDown(0);

		// if escape has been pressed, stop the game
		if (Display.isCloseRequested() || Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
			Game.gameRunning = false;
		}
	}
	
	/**
	 * Intialise the common elements for the game
	 */
	public void initialize() {
		// initialize the window beforehand
		try {
			setDisplayMode();
			Display.setTitle(WINDOW_TITLE);
			Display.setFullscreen(false);
			Display.create();
      
			// Allow cursor to appear
			Mouse.setGrabbed(false);
			
			GL11.glDepthRange(0.0, 1.0);

			GL11.glMatrixMode(GL11.GL_PROJECTION);
			GL11.glLoadIdentity();

			GL11.glOrtho(0, 8, 0, 8, -0.5, 2.5);


		} catch (LWJGLException le) {
			System.out.println("Game exiting - exception in initialization:");
			le.printStackTrace();
			Game.gameRunning = false;
			return;
		}
	}
	/**
	 * Sets the display mode for fullscreen mode
	 */
	private boolean setDisplayMode() {
	    try {
		// get modes
		DisplayMode[] dm = org.lwjgl.util.Display.getAvailableDisplayModes(width, height, -1, -1, -1, -1, 60, 60);

		org.lwjgl.util.Display.setDisplayMode(dm, new String[] {
		    "width=" + width,
		    "height=" + height,
		    "freq=" + 60,
		    "bpp=" + org.lwjgl.opengl.Display.getDisplayMode().getBitsPerPixel()
		   });
		
		return true;
		
	    } catch (Exception e) {
		e.printStackTrace();
		System.out.println("Unable to enter fullscreen, continuing in windowed mode");
	    }

		return false;
	}
	
	/**
	 * Get the high resolution time in milliseconds
	 * 
	 * @return The high resolution time in milliseconds
	 */
	public static long getTime() {
		// we get the "timer ticks" from the high resolution timer
		// multiply by 1000 so our end result is in milliseconds
		// then divide by the number of ticks in a second giving
		// us a nice clear time in milliseconds
		return (Sys.getTime() * 1000) / timerTicksPerSecond;
	}

      /**
       *
       */
      private void DrawEntities() {
            // cycle round drawing all the entities we have in the game
            GL11.glLoadName(1);
            GL11.glBegin(GL11.GL_QUADS);
            GL11.glColor3f(1.0f, 1.0f, 0.0f);
            GL11.glVertex3i(2, 0, 0);
            GL11.glVertex3i(2, 6, 0);
            GL11.glVertex3i(6, 6, 0);
            GL11.glVertex3i(6, 0, 0);
            GL11.glEnd();

            GL11.glLoadName(2);
            GL11.glBegin(GL11.GL_QUADS);
            GL11.glColor3f(0.0f, 1.0f, 1.0f);
            GL11.glVertex3i(3, 2, -1);
            GL11.glVertex3i(3, 8, -1);
            GL11.glVertex3i(8, 8, -1);
            GL11.glVertex3i(8, 2, -1);
            GL11.glEnd();

            GL11.glLoadName(3);
            GL11.glBegin(GL11.GL_QUADS);
            GL11.glColor3f(1.0f, 0.0f, 1.0f);
            GL11.glVertex3i(0, 2, -2);
            GL11.glVertex3i(0, 7, -2);
            GL11.glVertex3i(5, 7, -2);
            GL11.glVertex3i(5, 2, -2);
            GL11.glEnd();
      }

      /**
       * The selection magic happens here.
       * @param mouse_x
       * @param mouse_y
       */
      private void selection(int mouse_x, int mouse_y) {
		// The selection buffer
		IntBuffer selBuffer = ByteBuffer.allocateDirect(1024).order(ByteOrder.nativeOrder()).asIntBuffer();
		int buffer[] = new int[256];
		
		IntBuffer vpBuffer = ByteBuffer.allocateDirect(64).order(ByteOrder.nativeOrder()).asIntBuffer();
		// The size of the viewport. [0] Is <x>, [1] Is <y>, [2] Is <width>, [3] Is <height>
            int[] viewport = new int[4];
		
		// The number of "hits" (objects within the pick area).
		int hits;

		// Get the viewport info
            GL11.glGetInteger(GL11.GL_VIEWPORT, vpBuffer);
            vpBuffer.get(viewport);
		
		// Set the buffer that OpenGL uses for selection to our buffer
		GL11.glSelectBuffer(selBuffer);
		
		// Change to selection mode
		GL11.glRenderMode(GL11.GL_SELECT);
		
		// Initialize the name stack (used for identifying which object was selected)
		GL11.glInitNames();
		GL11.glPushName(0);

		
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPushMatrix();
		GL11.glLoadIdentity();
		
		/*  create 5x5 pixel picking region near cursor location */
		GLU.gluPickMatrix( (float) mouse_x, (float) mouse_y, 5.0f, 5.0f, viewport);
		
		GL11.glOrtho(0.0, 8.0, 0.0, 8.0, -0.5, 2.5);
		DrawEntities();
		GL11.glPopMatrix();

		// Exit selection mode and return to render mode, returns number selected
		hits = GL11.glRenderMode(GL11.GL_RENDER);
		System.out.println("Number: " + hits);
		
		selBuffer.get(buffer);
            // Objects Were Drawn Where The Mouse Was
            if (hits > 0) {
                  // If There Were More Than 0 Hits
                  int choose = buffer[3]; // Make Our Selection The First Object
                  int depth = buffer[1]; // Store How Far Away It Is
                  for (int i = 1; i < hits; i++) {
                        // Loop Through All The Detected Hits
				// If This Object Is Closer To Us Than The One We Have Selected
                        if (buffer[i * 4 + 1] < (int) depth) {
                              choose = buffer[i * 4 + 3]; // Select The Closer Object
                              depth = buffer[i * 4 + 1]; // Store How Far Away It Is
                        }
                  }

                  System.out.println("Chosen: " + choose);
            }
	}
            
}


There may be a small bug with the layering, let me know if this is helpful or if you have any suggestions.