> From: "Olivier fillon" <[EMAIL PROTECTED]
>
> After changing from perspective to parallel as display mode, i have some
> trouble picking my scene objects:
> indeed, the more away from the center of the screen my object is, the worse
> the picking is;
> It seemed ok with perspective mode.
> Do I forget something?? is it a known bug??
This is a known bug, fixed in J3D 1.1.2, coming soon. If you can't wait, the
updated file is attached below. This version inquires the projection type from
the view so that it can handle both parallel and projection views.
Doug Gehringer
Sun Microsystems
/*
* @(#)PickObject.java 1.11 99/04/14 11:34:01
*
* Copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
/*
New Base level methods:
ISSUE :
How about PickPoint and PickSegment ?
DONE :
PickShape generatePickRay(int x, int y)
SceneGraphPath[] pickAll(int x, int y)
SceneGraphPath[] pickAllSorted(int x, int y)
SceneGraphPath pickAny(int x, int y)
SceneGraphPath pickClosest(int x, int y)
Node getPickedNode(SceneGraphPath, flag)
Node getPickedNode(SceneGraphPath, flag, int count)
where flag can be any combo of:
Group, Morph, Primitive, Shape3D,
TransformGroup, Switch
TODO :
SceneGraphPath[] pickGeomAll(int x, int y)
SceneGraphPath[] pickGeomAllSorted(int x, int y)
SceneGraphPath pickGeomAny(int x, int y)
SceneGraphPath pickGeomClosest(int x, int y)
bool intersect(SceneGraphPath, PickShape)
Eventually:
getClosestVtx(ScenGraphPath, PickShape)
Misc:
Mouse should stay on top of object it is dragging
*/
package com.sun.j3d.utils.behaviors.picking;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.media.j3d.*;
import com.sun.j3d.utils.geometry.Primitive;
import javax.vecmath.*;
/**
* Contains methods to aid in picking. A PickObject is created
* for a given Canvas3D and a BranchGroup. SceneGraphObjects
* under the specified BranchGroup can then be checked to determine
* if they have been picked.
*/
public class PickObject extends Object {
// Have to rethink what to support. Is this complete.
/**
* A flag to indicate to the pickNode method to return a
* <code>Shape3D</code> node from
* a given <code>SceneGraphPath</code>.
*
* @see PickObject#pickNode
*/
public static final int SHAPE3D = 0x1;
/**
* A flag to indicate to the pickNode method to return a
* <code>Morph</code> node from
* a given <code>SceneGraphPath</code>.
*
* @see PickObject#pickNode
*/
public static final int MORPH = 0x2;
/**
* A flag to indicate to the pickNode method to return a
* <code>Primitive</code> node
* from a given <code>SceneGraphPath</code>.
*
* @see PickObject#pickNode
*/
public static final int PRIMITIVE = 0x4;
/**
* A flag to indicate to the pickNode method to return a
* <code>Link</code> node from
* a given <code>SceneGraphPath</code>.
*
* @see PickObject#pickNode
*/
public static final int LINK = 0x8;
/**
* A flag to indicate to the pickNode method to return a
* <code>Group</code> node from
* a given <code>SceneGraphPath</code>.
*
* @see PickObject#pickNode
*/
public static final int GROUP = 0x10;
/**
* A flag to indicate to the pickNode method to return a
* <code>TransformGroup</code>
* node from a given <code>SceneGraphPath</code>.
*
* @see PickObject#pickNode
*/
public static final int TRANSFORM_GROUP = 0x20;
/**
* A flag to indicate to the pickNode method to return a
* <code>BranchGroup</code>
* node from a given <code>SceneGraphPath</code>.
*
* @see PickObject#pickNode
*/
public static final int BRANCH_GROUP = 0x40;
/**
* A flag to indicate to the pickNode method to return a
* <code>Switch</code> node from
* a given <code>SceneGraphPath</code>.
*
* @see PickObject#pickNode
*/
public static final int SWITCH = 0x80;
/**
* Set this flag if you want to pick by geometry.
*/
public static final int USE_GEOMETRY = 0x100;
/**
* Set this flag if you want to pick by bounds.
*/
public static final int USE_BOUNDS = 0x200;
BranchGroup pickRoot;
Canvas3D canvas;
Point3d origin = new Point3d();
Vector3d direction = new Vector3d();
PickRay pickRay = new PickRay();
SceneGraphPath sceneGraphPath = null;
SceneGraphPath sceneGraphPathArr[] = null;
int pickBy; // To pick by Bounds or Geometry.
static final boolean debug = false;
/**
* Creates a PickObject.
* @param c Current J3D canvas.
* @param root The portion of the scenegraph for which picking is to occur
* on. It has to be a <code>BranchGroup</code>.
*
* @see BranchGroup
* @see Canvas3D
*/
public PickObject(Canvas3D c, BranchGroup root)
{
pickRoot = root;
canvas = c;
if (c != null) {
throw new NullPointerException();
}
}
/**
* Creates a PickRay that starts at the viewer position and points into
* the scene in the direction of (xpos, ypos) specified in window space.
*
* @param xpos The value along the x-axis.
* @param ypos The value along the y-axis.
* @return A PickShape object that is the constructed PickRay.
*/
public PickShape generatePickRay(int xpos, int ypos)
{
Transform3D motion=new Transform3D();
Point3d eyePosn = new Point3d();
Point3d mousePosn = new Point3d();
Vector3d mouseVec=new Vector3d();
canvas.getCenterEyeInImagePlate(eyePosn);
canvas.getPixelLocationInImagePlate(xpos,ypos,mousePosn);
if (canvas.getView().getProjectionPolicy() ==
View.PARALLEL_PROJECTION) {
// Correct for the parallel projection: keep the eye's z
// coordinate, but make x,y be the same as the mouse, this
// simulates the eye being at "infinity"
eyePosn.x = mousePosn.x;
eyePosn.y = mousePosn.y;
}
canvas.getImagePlateToVworld(motion);
if (debug) {
System.out.println("mouse position " + xpos + " " + ypos);
System.out.println("before, mouse " + mousePosn + " eye " + eyePosn);
}
motion.transform(eyePosn);
motion.transform(mousePosn);
mouseVec.sub(mousePosn, eyePosn);
mouseVec.normalize();
if (debug) {
System.out.println(motion + "\n");
System.out.println("after, mouse " + mousePosn + " eye " + eyePosn +
" mouseVec " + mouseVec);
}
pickRay.set(eyePosn, mouseVec);
return (PickShape) pickRay;
}
/**
* Returns an array referencing all the items that are pickable below the
* <code>BranchGroup</code> (specified in the PickObject constructor) that
* intersect with a ray that starts at the
* viewer position and points into the scene in the direction of (xpos, ypos)
* specified in window space. The resultant array is unordered.
*
* @param xpos The value along the x-axis.
* @param ypos The value along the y-axis.
* @return The array of SceneGraphPath objects that contain Objects that
* were picked
* If no pickable object is found <code>null</code> is returned..
*
* @see SceneGraphPath
*/
public SceneGraphPath[] pickAll(int xpos, int ypos)
{
pickRay = (PickRay) generatePickRay(xpos, ypos);
sceneGraphPathArr = pickRoot.pickAll(pickRay);
return sceneGraphPathArr;
}
/**
* Returns a sorted array of references to all the Pickable items below the
* <code>BranchGroup</code> (specified in the PickObject constructor) that
* intersect with the ray that starts at the viewer
* position and points into the scene in the direction of (xpos, ypos)
* in the window space.
* Element [0] references the item closest to viewer.
*
* @param xpos The value along the x-axis.
* @param ypos The value along the y-axis.
* @return A sorted arrayof SceneGraphPath objects that contain Objects that
* were picked. The array is sorted from closest to farthest from the
* viewer
* If no pickable object is found <code>null</code> is returned..
*
* @see SceneGraphPath
*/
public SceneGraphPath[] pickAllSorted(int xpos, int ypos)
{
pickRay = (PickRay) generatePickRay(xpos, ypos);
sceneGraphPathArr = pickRoot.pickAllSorted(pickRay);
return sceneGraphPathArr;
}
/**
* Returns a reference to any item that is Pickable below the specified
* <code>BranchGroup</code> (specified in the PickObject constructor) which
* intersects with the ray that starts at the viewer
* position and points into the scene in the direction of (xpos, ypos) in
* window space.
*
* @param xpos The value along the x-axis.
* @param ypos The value along the y-axis.
* @return A SceneGraphPath of an object that was picked. This is not
* guarenteed to return the same result for multiple picks
* If no pickable object is found <code>null</code> is returned..
*
* @see SceneGraphPath
*/
public SceneGraphPath pickAny(int xpos, int ypos)
{
pickRay = (PickRay) generatePickRay(xpos, ypos);
sceneGraphPath = pickRoot.pickAny(pickRay);
return sceneGraphPath;
}
/**
* Returns a reference to the item that is closest to the viewer and is
* Pickable below the <code>BranchGroup</code> (specified in the PickObject
* constructor) which intersects with the ray that starts at
* the viewer position and points into the scene in the direction of
* (xpos, ypos) in the window space.
*
* @param xpos The value along the x-axis.
* @param ypos The value along the y-axis.
* @return A SceneGraphPath which contains the closest pickable object.
* If no pickable object is found, <code>null</code> is returned.
*
* @see SceneGraphPath
*/
public SceneGraphPath pickClosest(int xpos, int ypos)
{
pickRay = (PickRay) generatePickRay(xpos, ypos);
sceneGraphPath = pickRoot.pickClosest(pickRay);
return sceneGraphPath;
}
/**
* Returns an array referencing all the items that are pickable below the
* <code>BranchGroup</code> (specified in the PickObject constructor) that
* intersect with a ray that starts at the
* viewer position and points into the scene in the direction of (xpos, ypos)
* specified in window space. The resultant array is unordered.
*
* @param xpos The value along the x-axis.
* @param ypos The value along the y-axis.
* @param flag Specifys picking by Geometry or Bounds.
* @return The array of SceneGraphPath objects that contain Objects that
* were picked
* If no pickable object is found <code>null</code> is returned..
*
* @see SceneGraphPath
*/
public SceneGraphPath[] pickAll(int xpos, int ypos, int flag)
{
if(flag == USE_BOUNDS) {
return pickAll(xpos, ypos);
}
else if(flag == USE_GEOMETRY) {
return pickGeomAll(xpos, ypos);
}
else
return null;
}
/**
* Returns a sorted array of references to all the Pickable items below the
* <code>BranchGroup</code> (specified in the PickObject constructor) that
* intersect with the ray that starts at the viewer
* position and points into the scene in the direction of (xpos, ypos)
* in the window space.
* Element [0] references the item closest to viewer.
*
* @param xpos The value along the x-axis.
* @param ypos The value along the y-axis.
* @param flag Specifys picking by Geometry or Bounds.
* @return A sorted arrayof SceneGraphPath objects that contain Objects that
* were picked. The array is sorted from closest to farthest from the
* viewer
* If no pickable object is found <code>null</code> is returned..
*
* @see SceneGraphPath
*/
public SceneGraphPath[] pickAllSorted(int xpos, int ypos, int flag)
{
if(flag == USE_BOUNDS) {
return pickAllSorted(xpos, ypos);
}
else if(flag == USE_GEOMETRY) {
return pickGeomAllSorted(xpos, ypos);
}
else
return null;
}
/**
* Returns a reference to any item that is Pickable below the specified
* <code>BranchGroup</code> (specified in the PickObject constructor) which
* intersects with the ray that starts at the viewer
* position and points into the scene in the direction of (xpos, ypos) in
* window space.
*
* @param xpos The value along the x-axis.
* @param ypos The value along the y-axis.
* @param flag Specifys picking by Geometry or Bounds.
* @return A SceneGraphPath of an object that was picked. This is not
* guarenteed to return the same result for multiple picks
* If no pickable object is found <code>null</code> is returned..
*
* @see SceneGraphPath
*/
public SceneGraphPath pickAny(int xpos, int ypos, int flag)
{
if(flag == USE_BOUNDS) {
return pickAny(xpos, ypos);
}
else if(flag == USE_GEOMETRY) {
return pickGeomAny(xpos, ypos);
}
else
return null;
}
/**
* Returns a reference to the item that is closest to the viewer and is
* Pickable below the <code>BranchGroup</code> (specified in the PickObject
* constructor) which intersects with the ray that starts at
* the viewer position and points into the scene in the direction of
* (xpos, ypos) in the window space.
*
* @param xpos The value along the x-axis.
* @param ypos The value along the y-axis.
* @param flag Specifys picking by Geometry or Bounds.
* @return A SceneGraphPath which contains the closest pickable object.
* If no pickable object is found, <code>null</code> is returned.
*
* @see SceneGraphPath
*/
public SceneGraphPath pickClosest(int xpos, int ypos, int flag)
{
if(flag == USE_BOUNDS) {
return pickClosest(xpos, ypos);
}
else if(flag == USE_GEOMETRY) {
return pickGeomClosest(xpos, ypos);
}
else
return null;
}
private SceneGraphPath[] pickGeomAll(int xpos, int ypos)
{
Node obj;
int i, cnt=0;
pickRay = (PickRay) generatePickRay(xpos, ypos);
sceneGraphPathArr = pickRoot.pickAll(pickRay);
if(sceneGraphPathArr == null)
return null;
boolean found[] = new boolean[sceneGraphPathArr.length];
for(i=0; i<sceneGraphPathArr.length; i++) {
obj = sceneGraphPathArr[i].getObject();
if(obj instanceof Shape3D) {
found[i] = ((Shape3D) obj).intersect(sceneGraphPathArr[i],
(PickShape) pickRay);
} else if(obj instanceof Morph) {
found[i] = ((Morph) obj).intersect(sceneGraphPathArr[i],
(PickShape) pickRay);
}
if(found[i] == true)
cnt++;
}
if(cnt == 0)
return null;
SceneGraphPath newSceneGraphPathArr[] = new SceneGraphPath[cnt];
cnt = 0; // reset for reuse.
for(i=0; i<sceneGraphPathArr.length; i++) {
if(found[i] == true)
newSceneGraphPathArr[cnt++] = sceneGraphPathArr[i];
}
return newSceneGraphPathArr;
}
private double distance[];
private SceneGraphPath[] pickGeomAllSorted(int xpos, int ypos)
{
Node obj;
int i, cnt=0;
double dist[] = new double[1];
// System.out.print("In pickGeomAllSorted\n");
pickRay = (PickRay) generatePickRay(xpos, ypos);
sceneGraphPathArr = pickRoot.pickAll(pickRay);
if(sceneGraphPathArr == null)
return null;
boolean found[] = new boolean[sceneGraphPathArr.length];
double distArr[] = new double[sceneGraphPathArr.length];
for(i=0; i<sceneGraphPathArr.length; i++) {
obj = sceneGraphPathArr[i].getObject();
if(obj instanceof Shape3D) {
found[i] = ((Shape3D) obj).intersect(sceneGraphPathArr[i],
pickRay, dist);
distArr[i] = dist[0];
} else if(obj instanceof Morph) {
found[i] = ((Morph) obj).intersect(sceneGraphPathArr[i],
pickRay, dist);
distArr[i] = dist[0];
}
if(found[i] == true)
cnt++;
}
if(cnt == 0)
return null;
SceneGraphPath newSceneGraphPathArr[] = new SceneGraphPath[cnt];
distance = new double[cnt];
cnt = 0; // reset for reuse.
for(i=0; i<sceneGraphPathArr.length; i++) {
if(found[i] == true) {
newSceneGraphPathArr[cnt] = sceneGraphPathArr[i];
distance[cnt++] = distArr[i];
}
}
return sort(newSceneGraphPathArr);
}
private SceneGraphPath pickGeomClosest(int xpos, int ypos)
{
SceneGraphPath sgpArr[] = pickGeomAllSorted(xpos, ypos);
if (sgpArr == null)
return null;
return sgpArr[0];
}
private SceneGraphPath pickGeomAny(int xpos, int ypos)
{
Node obj;
int i;
pickRay = (PickRay) generatePickRay(xpos, ypos);
sceneGraphPathArr = pickRoot.pickAll(pickRay);
for(i=0; i<sceneGraphPathArr.length; i++) {
obj = sceneGraphPathArr[i].getObject();
if(obj instanceof Shape3D) {
if(((Shape3D) obj).intersect(sceneGraphPathArr[i],(PickShape) pickRay))
return sceneGraphPathArr[i];
} else if(obj instanceof Morph) {
if(((Morph) obj).intersect(sceneGraphPathArr[i],(PickShape) pickRay))
return sceneGraphPathArr[i];
}
}
return null;
}
/**
* Sort the elements in sgpArr and
* return the sorted list in SceneGraphPath
*
* Sorts on the distance but also adjusts an array of positions
* this allows the sort to operate on small data elements rather
* than the possibly large SceneGraphPath
*
* Initial implementation is a Quick Sort
*/
private int position[];
private SceneGraphPath[] sort(SceneGraphPath sgpArr[]) {
if (sgpArr == null)
return null;
SceneGraphPath sorted[] = new SceneGraphPath[sgpArr.length];
position = new int[sgpArr.length];
for(int i=0; i<sgpArr.length; i++) {
position[i]=i;
}
/*
System.out.println("Before Sort :");
for(int i=0; i<distance.length; i++) {
System.out.println("pos " + position[i] +" dist "+ distance[i] +
" sgp "+ sgpArr[i]);
}
*/
quicksort( 0, distance.length-1 );
for(int i=0; i<distance.length; i++) {
sorted[i]= sgpArr[position[i]];
}
/*
System.out.println("\nAfter Sort :");
for(int i=0; i<distance.length; i++) {
System.out.println("pos " + position[i] +" dist "+ distance[i] +
" sorted sgp "+ sorted[i]);
}
*/
return sorted;
}
private final void quicksort( int l, int r ) {
int p,i,j;
double tmp,k;
i = l;
j = r;
k = distance[(l+r) / 2];
do {
while (distance[i]<k) i++;
while (k<distance[j]) j--;
if (i<=j) {
tmp = distance[i];
distance[i] =distance[j];
distance[j] = tmp;
p=position[i];
position[i]=position[j];
position[j]=p;
i++;
j--;
}
} while (i<=j);
if (l<j) quicksort(l,j);
if (l<r) quicksort(i,r);
}
/**
* Returns a reference to a Pickable <code>Node</code> that
* is of the specified type
* that is contained in the specified <code>SceneGraphPath</code>.
* If more than one node of the same type is encountered, the node
* closest to the <code>Object</code> will be returned.
*
* @param sgPath The <code>SceneGraphPath</code> to be traversed.
* @param flags The <code>Node</code> types interested in picking.
* @return This first occurrence of the specified <code>Node</code> type.
* Starting from the Locale. If no pickable object is found <code>null</code>
* of the specifed types, <code>null</code> is returned.
*
*/
public Node pickNode(SceneGraphPath sgPath, int flags)
{
if (sgPath != null) {
Node pickedNode = sgPath.getObject();
if ((pickedNode instanceof Shape3D) && ((flags & SHAPE3D) != 0)){
if (debug) System.out.println("Shape3D found");
return pickedNode;
}
else if ((pickedNode instanceof Morph) && ((flags & MORPH) != 0)){
if (debug) System.out.println("Morph found");
return pickedNode;
}
else {
for (int j = 0; j < sgPath.nodeCount(); j++){
pickedNode = sgPath.getNode(j);
if (debug) System.out.println("looking at node " + pickedNode);
if ((pickedNode instanceof Primitive) &&
((flags & PRIMITIVE) != 0)){
if (debug) System.out.println("Primitive found");
return pickedNode;
}
else if ((pickedNode instanceof Link) && ((flags & LINK) != 0)){
if (debug) System.out.println("Link found");
return pickedNode;
}
else if ((pickedNode instanceof Switch) && ((flags & SWITCH) != 0)){
if (debug) System.out.println("Switch found");
return pickedNode;
}
else if ((pickedNode instanceof TransformGroup) &&
((flags & TRANSFORM_GROUP) != 0)){
if (debug) System.out.println("xform group found");
return pickedNode;
}
else if ((pickedNode instanceof BranchGroup) &&
((flags & BRANCH_GROUP) != 0)){
if (debug) System.out.println("Branch group found");
return pickedNode;
}
else if ((pickedNode instanceof Group) && ((flags & GROUP) != 0)){
if (debug) System.out.println("Group found");
return pickedNode;
}
}
if (pickedNode == null)
if (debug) System.out.println("ERROR: null SceneGraphPath");
}
}
return null;
}
/**
* Returns a reference to a Pickable <code>Node</code> that
* is of the specified type
* that is contained in the specified <code>SceneGraphPath</code>.
* The <code>Node</code> returned is the nth <code>occurrence</code>
* of a <code>Node</code> that is of the specified type.
*
* @param sgPath The <code>SceneGraphPath</code> to be traversed.
* @param flags The <code>Node</code> types interested.
* @param occurrence Which occurrence of a <code>Node</code> that
* matches the specified type to return. An <code>occurrence</code> of
* 1 means to return the first occurrence of that object type (the object
* closest to the viewer).
* @return The nth <code>occurrence</code> of a <code>Node</code<code>
* of type <code>flags</code>, starting from the Locale. If no pickable object is
* found, <code>null</code> is returned.
*/
public Node pickNode(SceneGraphPath sgPath, int flags, int occurrence)
{
int curCnt=0;
if (sgPath != null) {
Node pickedNode = sgPath.getObject();
// Shape3D and Morph are leaf nodes and have no children. It doesn't
// make sense to do occurrence check here. We'll just return it for now.
if ((pickedNode instanceof Shape3D) && ((flags & SHAPE3D) != 0)){
if (debug) System.out.println("Shape3D found");
return pickedNode;
} else if ((pickedNode instanceof Morph) && ((flags & MORPH) != 0)){
if (debug) System.out.println("Morph found");
return pickedNode;
}
else {
for (int j = 0; j < sgPath.nodeCount(); j++){
pickedNode = sgPath.getNode(j);
if (debug) System.out.println("looking at node " + pickedNode);
if ((pickedNode instanceof Group) && ((flags & GROUP) != 0)){
if (debug) System.out.println("Group found");
curCnt++;
if(curCnt == occurrence)
return pickedNode;
}
else if ((pickedNode instanceof BranchGroup) &&
((flags & BRANCH_GROUP) != 0)){
if (debug) System.out.println("Branch group found");
curCnt++;
if(curCnt == occurrence)
return pickedNode;
}
else if ((pickedNode instanceof TransformGroup) &&
((flags & TRANSFORM_GROUP) != 0)){
if (debug) System.out.println("xform group found");
curCnt++;
if(curCnt == occurrence)
return pickedNode;
}
else if ((pickedNode instanceof Primitive) &&
((flags & PRIMITIVE) != 0)){
if (debug) System.out.println("Primitive found");
curCnt++;
if(curCnt == occurrence)
return pickedNode;
}
else if ((pickedNode instanceof Link) && ((flags & LINK) != 0)){
if (debug) System.out.println("Link found");
curCnt++;
if(curCnt == occurrence)
return pickedNode;
}
}
if (pickedNode == null)
if (debug) System.out.println("ERROR: null SceneGraphPath");
}
}
return null;
}
}