Strange Picking Problem

I am migrating my existing code to new Xith Version but picking is giving me hard time ???

This is small test-Case.


import java.awt.AWTEvent;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;
import java.util.List;

import javax.vecmath.Color3f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import org.xith3d.geometry.Line;
import org.xith3d.geometry.Sphere;
import org.xith3d.picking.PickResult;
import org.xith3d.render.Canvas3D;
import org.xith3d.render.CanvasPeer;
import org.xith3d.render.Option;
import org.xith3d.render.RenderPeer;
import org.xith3d.render.base.Xith3DEnvironment;
import org.xith3d.render.jsr231.RenderPeerAWTImpl;
import org.xith3d.scenegraph.BranchGroup;
import org.xith3d.scenegraph.Locale;
import org.xith3d.scenegraph.Transform3D;
import org.xith3d.scenegraph.TransformGroup;
import org.xith3d.scenegraph.View;
import org.xith3d.scenegraph.VirtualUniverse;

public class PickingTest {

	private TransformGroup objTranslate;

	private Transform3D translate;

	Canvas3D canvas;

	View view;

	/**
	 * Synchronization object to ensure that only one thread has access to pick
	 * params
	 */
	private Object pickParamsMutex = new Object();

	private boolean picked = false;

	private int pickX;

	private int pickY;

	/**
	 * Draws a cube.
	 */
	public PickingTest() {

		VirtualUniverse universe = new VirtualUniverse();
		view = new View();
		view.setProjectionPolicy(View.PARALLEL_PROJECTION);
		view.setScreenScale(100);
		universe.addView(view);

		Locale locale = new Locale();
		universe.addLocale(locale);
		

		/*
		 * Problem : why i have to Instantiate this.
		 */
		// Xith3DEnvironment env = new Xith3DEnvironment();
		locale.addBranchGraph(createScene());
		

		RenderPeer rp = new RenderPeerAWTImpl();
		CanvasPeer cp = rp.makeCanvas(null, 640, 480, 32, false);
		cp.getRenderOptions().setOption(Option.ENABLE_WIREFRAME_MODE, true);

		canvas = new Canvas3D();
		canvas.set3DPeer(cp);

		view.addCanvas3D(canvas);
		view.getTransform().lookAt(new Point3f(0, 0, 3), new Point3f(0, 0, 0),
				new Vector3f(0, 1, 0));

		Toolkit.getDefaultToolkit().addAWTEventListener(
				new EventListener(),
				AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK
						| AWTEvent.MOUSE_MOTION_EVENT_MASK);

		// main rendering loop
		while (true) {
			view.renderOnce();
			synchronized (pickParamsMutex) {
				if (picked) {
					// Pick object at mouse point and within 3 pixels around it
					System.out.println("Picking at " + pickX + ", " + pickY);

					List<PickResult> results = canvas.pickAll(pickX, pickY, 3,3);

					if (results == null) {
						System.out.println("Pick results are null");
					} else {
						System.out.println("Detected " + results.size()
								+ " hits");

						int i = 0;
						for (PickResult pr : results) {
							System.out.println("  Hit (" + i+++ "): Shape3D: \""
									+ pr.getShape().toString());
						}
					}

					picked = false;
				}
			}
		}
	}

	/**
	 * Starts the application.
	 * 
	 * @param args
	 *   command line parameters
	 */
	public static void main(String[] args) {
		new PickingTest();
	}

	public void onMousePressed(int x, int y) {
		synchronized (pickParamsMutex) {
			this.pickX = x;
			this.pickY = y;
			picked = true;
		}

	}

	/**
	 * Our own EventListener.
	 */
	class EventListener implements AWTEventListener {
		public void eventDispatched(AWTEvent event) {
			if (event instanceof MouseEvent) {
				MouseEvent e = (MouseEvent) event;
				switch (e.getID()) {
				case MouseEvent.MOUSE_PRESSED:
					onMousePressed(e.getX(), e.getY());
					break;
				}
			}
		}
	}

	private BranchGroup createScene() {
		// create a BranchGroup
		BranchGroup scene = new BranchGroup();

		scene.setPickable(true);

		// let objects along this path translate
		translate = new Transform3D();
		objTranslate = new TransformGroup(translate);
		scene.addChild(objTranslate);

		Sphere sph = new Sphere(5, 5, 1, 20);
		sph.setPickable(true);
		objTranslate.addChild(sph);

		Line line = new Line(new Point3f(30, 0, 0), new Point3f(100, 0, 0),
				new Color3f(1, 1, 1));
		line.setPickable(true);

		objTranslate.addChild(line);
		return scene;
	}
}

On mouse click scene disappears but strangely if i just Instantiate Xith3DEnvironment object even though i am not using it
the problem goes away .
Just uncomment the instantiation line in constructor.

This is because the default pickable value of Nodes (which is false). You can change the default (prior to any Node instantiation) by calling Xith3DDefaults.setDefaultNodePickable(true). Xith3DEnvironment does that for you :). So that’s the reason, why this made a difference. Anyway, it was a bug, that the scene didn’t get rerendered correctly, which is now fixed. Please update xith3d and xith-tk from SVN, if you want get rid of this bug.

Why don’t you use all the wrapper classes? If you want to shrink your code a lot, do it like the following:


import java.util.List;

import javax.vecmath.Color3f;
import javax.vecmath.Point3f;

import net.jtank.input.KeyCode;

import org.xith3d.geometry.Line;
import org.xith3d.geometry.Sphere;
import org.xith3d.picking.PickResult;
import org.xith3d.render.RenderPass;
import org.xith3d.render.CanvasPeer.OpenGLLayer;
import org.xith3d.render.base.Xith3DEnvironment;
import org.xith3d.render.canvas.Canvas3DWrapper;
import org.xith3d.render.canvas.Canvas3DWrapper.ColorDepth;
import org.xith3d.render.canvas.Canvas3DWrapper.Resolution;
import org.xith3d.render.loop.ExtRenderLoop;
import org.xith3d.scenegraph.BranchGroup;
import org.xith3d.scenegraph.TransformGroup;

public class PickingTest extends ExtRenderLoop {
    
    private Xith3DEnvironment env;
    
    /**
     * Picking test
     */
    public PickingTest() {
        
        super(128L);
        
        this.env = new Xith3DEnvironment(new Point3f(0, 0, 3),
                                         new Point3f(0, 0, 0),
                                         new Point3f(0, 1, 0),
                                         this);
        
        RenderPass pass = env.addParallelBranch(createScene());
        pass.getConfig().setScreenScale(100.0f);
        
        Canvas3DWrapper canvas = Canvas3DWrapper.createStandalone(OpenGLLayer.JOGL_AWT,
                                                                  Resolution.RES_640X480,
                                                                  ColorDepth.B32,
                                                                  "PickingTest");
        
        //canvas.setRenderOption(Option.ENABLE_WIREFRAME_MODE, true);
        canvas.setWireframeMode(true);
        
        env.addCanvas(canvas);
        
        this.registerKeyboardAndMouse(canvas);
        
        this.begin();
    }
    
    /**
     * Starts the application.
     * 
     * @param args
     *   command line parameters
     */
    public static void main(String[] args) {
        new PickingTest();
    }
    
    protected void onKeyPressed(int key) {
        switch (key) {
            case KeyCode.VK_ESCAPE:
                this.end();
                break;
        }
    }
    
    protected void onMouseButtonPressed(int button, int x, int y) {
        synchronized (env.getUniverse()) {
            // Pick object at mouse point and within 3 pixels around it
            System.out.println("Picking at " + x + ", " + y);
            
            List<PickResult> results = env.getCanvas().pickAll(x, y, 3, 3);
            
            if (results == null) {
                System.out.println("Pick results are null");
            } else {
                System.out.println("Detected " + results.size() + " hits");
                
                int i = 0;
                for (PickResult pr : results) {
                    System.out.println("  Hit (" + i+++ "): Shape3D: \""
                            + pr.getShape().toString());
                }
            }
        }

    }
    
    private BranchGroup createScene() {
        // create a BranchGroup
        BranchGroup scene = new BranchGroup();
        
        // let objects along this path translate
        TransformGroup objTranslate = new TransformGroup();
        scene.addChild(objTranslate);
        
        Sphere sph = new Sphere(5, 5, 1, 20);
        objTranslate.addChild(sph);
        
        Line line = new Line(new Point3f(30, 0, 0), new Point3f(100, 0, 0),
                new Color3f(1, 1, 1));
        
        objTranslate.addChild(line);
        return scene;
    }
}

You can use env.getUniverse() as the mutex for synchronization with the rendering thread, since the render method uses the currently rendered VirtualUniverse as its synchronization mutex.

Marvin

EDIT: Anyway, I strongly advise you to use scheduled picking through ExtXith3DEnvironment. It is the only picking method, that never caused a deadlock in my tests. The other methods sometimes do it. And GLSelect picking is very, very slow, if you use it for more than a few Nodes. Scheduled picking uses PickingLibrary, which is way faster.

Thanks a lot ,it drove me crazy as why the scene is disappearing , took 3 hours as firstly i was checking my code and than i made this test case ;D

basically i have a lot of code depending upon previous version so i am making small test-cases to see
how i will refactor my existing code to be compatible with current release.

For example : I don’t render the scene continuously but i do rendering in separate thread -->something like this


     while(true){
			if(renderingFlag){
                              renderingFlag=false;
				synchronized (lock) {
					view.renderOnce();
				}
				
			}
		}

So only if i have made some changes in Scene -Graph i make renderingFlag=true .
The scene i am rendering are very big so i am doing this.

I hope there must be some easy was to handle all this in newer version thats why i am
first trying with test cases.

basically was very busy with my development that so not able to keep pace with xith development.

The RenderLoop class is capable of being controlled from outside. It implements the RenderLoopController interface which has the nextIteration() method. This makes the RenderLoop to do the next step.

Alternatively, if you want to use the RenderLoop’s threading, you can also override the loopIteration(lomg, long) method and just call the super method, if you want to.

So it could look like this:


while (true)
{
    if (renderFlag)
    {
        renderFlag = false;
        this.nextIteration(); // or myRenderLoopController.nextIteration()
    }
}

or:


@Override
protected void loopIteration(long gameTime, long frameTime)
{
    if (renderFlag)
    {
        renderFlag = false;
        super.loopIteration( gameTime, frameTime );
    }
}

Marvin

I’ve just modified the RenderLoop class by adding a new RunMode. It should now perfectly meet your needs.

Just start the RenderLoop with the following line:


rl.begin( RunMode.RUN_IN_SEPARATE_THREAD_AND_WAIT );

Then any time you call


rl.nextIteration();

the next frame will be rendered in a separate thread. Cool, he? :).

Marvin

btw: Do you know our new board? If you want to stay tuned, you should register there. Internal things are all dicussed there. JGO is kept up-to-date about updates, but most discussions are done there.

Oh Thanks , i was about to dig into xith new code to see how this can be achieved ,but you just saved my time :slight_smile: .

I just figured out one more problem .

Picking is working fine when scene has only 2 shapes to render .
But if we add more objects then picking is not returning correct result ,

Say if i have created three objects line and sphere and Quad than when i pick sphere , its returning Quad .

After debugging i found that picking part is happening properly but when it extract the selected Object from
selectBuffer in method convertSelectBuffer of CanvasPeer there its extracting wrong object.



import java.util.List;

import javax.vecmath.Color3f;
import javax.vecmath.Point3f;

import net.jtank.input.KeyCode;

import org.xith3d.geometry.Line;
import org.xith3d.geometry.Quad;
import org.xith3d.geometry.Sphere;
import org.xith3d.picking.PickResult;
import org.xith3d.render.base.Xith3DEnvironment;
import org.xith3d.render.canvas.Canvas3DWrapper;
import org.xith3d.render.canvas.Canvas3DWrapper.Resolution;
import org.xith3d.render.loop.RenderLoop;
import org.xith3d.scenegraph.BranchGroup;
import org.xith3d.scenegraph.Transform3D;
import org.xith3d.scenegraph.TransformGroup;

public class PickingTest extends RenderLoop {
	// translation part of our scenegraph
	private TransformGroup objTranslate;
	
	private TransformGroup objRotate;

	private Transform3D translate;

	Canvas3DWrapper canvas;
	
	 /**
     * Synchronization object to ensure that only one thread has access to pick params 
     */
    private Object pickParamsMutex = new Object();
	
	   private boolean picked = false;
	    private int pickX;
	    private int pickY;



	private BranchGroup createScene() {
		// create a BranchGroup
		BranchGroup scene = new BranchGroup();

		scene.setPickable(true);

		// let objects along this path translate
		translate = new Transform3D();
		objTranslate = new TransformGroup(translate);
		
		objRotate= new TransformGroup();
		objTranslate.addChild(objRotate);
		scene.addChild(objTranslate);
		
		Line line = new Line(new Point3f(30, 0, 0), new Point3f(100, 0, 0),new Color3f(1, 1, 1));
		line.setPickable(true);
		objRotate.addChild(line);
		
		Sphere sph = new Sphere(10, 10, 1, 20);
		sph.setPickable(true);
		objTranslate.addChild(sph);
		
		Point3f[] vertexArray={new Point3f(30, 10, 0), new Point3f(50, 10, 0),new Point3f(50, 40, 0), new Point3f(30, 40, 0)};
		Quad quad = new Quad(new Color3f(1, 1, 1),0,vertexArray);
		quad.setPickable(true);
		objTranslate.addChild(quad);

		return scene;
	}

	public PickingTest() {

		Xith3DEnvironment env = new Xith3DEnvironment(this);
		env.getView().setScreenScale(100);
		env.getView().setProjectionPolicy(env.getView().PARALLEL_PROJECTION);
		canvas = Canvas3DWrapper.createStandalone(Resolution.RES_800X600,"My empty scene");
		this.registerKeyboardAndMouse(canvas);
		env.addCanvas(canvas);
		env.addBranchGraph(createScene());

		this.begin();
	}

	public static void main(String[] args) {
		new PickingTest();
	}
	 protected void loopIteration(long gameTime, long frameTime)
	    {
	        super.loopIteration(gameTime, frameTime);
	        
	        synchronized (pickParamsMutex)
	        {
	            if (picked)
	            {
	                // Pick object at mouse point and within 3 pixels around it
	                System.out.println("Picking at " + pickX + ", " + pickY);
	                
	                List<PickResult> results = canvas.pickAll(pickX, pickY, 3, 3);
	                
	                if (results == null)
	                {
	                    System.out.println("Pick results are null");
	                }
	                else
	                {
	                    System.out.println("Detected " + results.size() + " hits");
	                    
	                    int i = 0;
	                    for (PickResult pr: results)
	                    {
	                        System.out.println("  Hit (" + i++ + "): Shape3D: \"" + pr.getShape().toString() );
	                    }
	                }
	                
	                picked = false;
	            }
	        }
	    }
	public void onMouseButtonPressed(int button, int x, int y) {

		   // Transfer mouse event parameters to rendering thread
        synchronized (pickParamsMutex)
        {
            this.pickX = x;
            this.pickY = y;
            
            picked = true;
        }
 
	}
		public void onKeyReleased(int key) {
			switch (key) {
			case KeyCode.VK_ESCAPE:
				System.exit(0);
				break;
			}
		}
}


Thanks for the modification you have done

yaa its quite clean and cool :wink: