Picking and 2D Bounds

Started by AGP, October 23, 2011, 07:22:08 AM

Previous topic - Next topic

AGP

I'm doing picking with the mouse on objects. I'm creating the textured plane like this:

     private Object3D createPlane(String textureName) {
Texture texture = TextureManager.getInstance().getTexture(textureName);
Object3D plane = Primitives.getPlane(1, texture.getWidth()/6);
plane.setTransparency(100);
plane.setTexture(textureName);
plane.setSelectable(Object3D.MOUSE_SELECTABLE);
plane.build();
return plane;
     }


Then I'm trying to get 2D bounds as follows:

     public Rectangle getBounds2D(Camera theCamera, FrameBuffer buffer) {
if (sourceMesh == null) {
     VertexController controller = new VertexController(this.unitPlane.getMesh());
     sourceMesh = controller.getSourceMesh();
}
SimpleVector p1 = new SimpleVector(sourceMesh[0]), p2 = new SimpleVector(sourceMesh[1]);
SimpleVector p3 = new SimpleVector(sourceMesh[2]), p4 = new SimpleVector(sourceMesh[3]);
p1.matMul(unitPlane.getWorldTransformation());p2.matMul(unitPlane.getWorldTransformation());
p3.matMul(unitPlane.getWorldTransformation());p4.matMul(unitPlane.getWorldTransformation());
p1 = Interact2D.project3D2D(theCamera, buffer, p1);
p2 = Interact2D.project3D2D(theCamera, buffer, p2);
p3 = Interact2D.project3D2D(theCamera, buffer, p3);
p4 = Interact2D.project3D2D(theCamera, buffer, p4);
float x1 = p1.x, x2, y1 = p1.y, y2;
if (x1 != p2.x)
     x2 = p2.x;
else x2 = p3.x;
if (y1 != p2.y)
     y2 = p2.y;
else y2 = p3.y;
System.out.println("X: "+x1 +" Y: "+y1 +" WIDTH : "+((int)Math.abs(x1-x2)) +" HEIGHT: "+(int)Math.abs(y1-y2));
return new Rectangle((int)x1, (int)y1, (int)Math.abs(x1-x2), (int)Math.abs(y1-y2));
     }


The trouble is that only the same two planes are selectable (the others aren't) and x1 is often negative (and it's always the right values for the same two).

I should add that I've rotated all the objects (but not the camera) by -90 degrees around the X axis so that I'm looking down on them from above (the world is on the X/Y plane).

EgonOlsen

It would be easier to use the PolygonManager to get the vertcies transformed into world space. To calculate the bounds in 2D you have to do the same thing that you did to calculate  them in 3D in the other thread. I'm not sure what the two ifs you are using now are supposed to do...

AGP

#2
No, I know, but in this particular instance I'm not going to rotate anything anymore. So shouldn't it work? And is it weird that it consistently works for two planes, and consistently doesn't for the other four?

The ifs on getBounds2D test for whether the Rectangle has width and height that are non-zero.

Also, how are the polygon IDs arranged? 0-PolygonManager.getMaxPolygonID() ?

EgonOlsen

Hard to tell, as i've no idea what unitPlane for example is...however, i would simply rewrite this to use the PolygonManager instead (yes, IDs go from 0 to max (exclusive IIRC)) and do a proper min/max-calculation to get a correct bounding box for all cases.

AGP

OK, I wrote this:

     public Rectangle newBounds2D(Camera theCamera, FrameBuffer buffer) {
PolygonManager polyManager = unitPlane.getPolygonManager();
SimpleVector[] vertices = new SimpleVector[polyManager.getMaxPolygonID()*3];
int currentPoly = 0;
float minX = Float.MAX_VALUE, minY = Float.MAX_VALUE, minZ = Float.MAX_VALUE;
float maxX = Float.MIN_VALUE, maxY = Float.MIN_VALUE, maxZ = Float.MIN_VALUE;
for (int i = 0; i < vertices.length; i+=3) {
     vertices[i] = polyManager.getTransformedVertex(currentPoly, 0);
     vertices[i+1] = polyManager.getTransformedVertex(currentPoly, 1);
     vertices[i+2] = polyManager.getTransformedVertex(currentPoly++, 2);
     if (minX > vertices[i].x)
minX = vertices[i].x;
     if (minX > vertices[i+1].x)
minX = vertices[i+1].x;
     if (minX > vertices[i+2].x)
minX = vertices[i+2].x;
     if (maxX < vertices[i].x)
maxX = vertices[i].x;
     if (maxX < vertices[i+1].x)
maxX = vertices[i+1].x;
     if (maxX < vertices[i+2].x)
maxX = vertices[i+2].x;
     if (minY > vertices[i].y)
minY = vertices[i].y;
     if (minY > vertices[i+1].y)
minY = vertices[i+1].y;
     if (minY > vertices[i+2].y)
minY = vertices[i+2].y;
     if (maxY < vertices[i].y)
maxY = vertices[i].y;
     if (maxY < vertices[i+1].y)
maxY = vertices[i+1].y;
     if (maxY < vertices[i+2].y)
maxY = vertices[i+2].y;
     if (minZ > vertices[i].z)
minZ = vertices[i].z;
     if (minZ > vertices[i+1].z)
minZ = vertices[i+1].z;
     if (minZ > vertices[i+2].z)
minZ = vertices[i+2].z;
     if (maxZ < vertices[i].z)
maxZ = vertices[i].z;
     if (maxZ < vertices[i+1].z)
maxZ = vertices[i+1].z;
     if (maxZ < vertices[i+2].z)
maxZ = vertices[i+2].z;
}
return getBounds2D(theCamera, buffer, minX, minY, maxX, maxY);
     }
     public Rectangle getBounds2D(Camera theCamera, FrameBuffer buffer, float minX, float minY, float maxX, float maxY) {
SimpleVector minP = Interact2D.project3D2D(theCamera, buffer, new SimpleVector(minX, minY, 0));
SimpleVector maxP = Interact2D.project3D2D(theCamera, buffer, new SimpleVector(maxX, maxY, 0));
return new Rectangle((int)minP.x, (int)minP.y, (int)Math.abs(maxP.x-minP.x), (int)Math.abs(maxP.y-minP.y));
     }


Exact same problem, though.

EgonOlsen

What exactly IS the actual problem with this? I didn't really get it so far...

AGP

Try it out. I've sent it to you (chopped as much of it as possible but there's still some fat).

EgonOlsen

For those reading this thread: The wiki/documentation on Interact2D.pickPolygon() got the order in the result array reversed (at least for some parts of the documentation), which caused confusion. The wiki is already fixed, the documentation will be fixed in the next version.

AGP

PART 2: maxX and maxY seem to both be at the origin, making the bounding box of something built on Cartesian quadrant 2 (relative to the origin) much bigger than it should be.

(By the way, the KeyMapper thing still isn't solved. I'm starting to think it's a Java 7 thing.)


     public Rectangle newBounds2D(Camera theCamera, FrameBuffer buffer) {
PolygonManager polyManager = unitPlane.getPolygonManager();
SimpleVector[] vertices = new SimpleVector[polyManager.getMaxPolygonID()*3];
int currentPoly = 0;
float minX = Float.MAX_VALUE, minY = Float.MAX_VALUE, minZ = Float.MAX_VALUE;
float maxX = Float.MIN_VALUE, maxY = Float.MIN_VALUE, maxZ = Float.MIN_VALUE;
for (int i = 0; i < vertices.length; i+=3) {
     vertices[i] = polyManager.getTransformedVertex(currentPoly, 0);
     vertices[i+1] = polyManager.getTransformedVertex(currentPoly, 1);
     vertices[i+2] = polyManager.getTransformedVertex(currentPoly++, 2);
     if (minX > vertices[i].x)
minX = vertices[i].x;
     if (minX > vertices[i+1].x)
minX = vertices[i+1].x;
     if (minX > vertices[i+2].x)
minX = vertices[i+2].x;
     if (maxX < vertices[i].x)
maxX = vertices[i].x;
     if (maxX < vertices[i+1].x)
maxX = vertices[i+1].x;
     if (maxX < vertices[i+2].x)
maxX = vertices[i+2].x;
     if (minY > vertices[i].y)
minY = vertices[i].y;
     if (minY > vertices[i+1].y)
minY = vertices[i+1].y;
     if (minY > vertices[i+2].y)
minY = vertices[i+2].y;
     if (maxY < vertices[i].y)
maxY = vertices[i].y;
     if (maxY < vertices[i+1].y)
maxY = vertices[i+1].y;
     if (maxY < vertices[i+2].y)
maxY = vertices[i+2].y;
     if (minZ > vertices[i].z)
minZ = vertices[i].z;
     if (minZ > vertices[i+1].z)
minZ = vertices[i+1].z;
     if (minZ > vertices[i+2].z)
minZ = vertices[i+2].z;
     if (maxZ < vertices[i].z)
maxZ = vertices[i].z;
     if (maxZ < vertices[i+1].z)
maxZ = vertices[i+1].z;
     if (maxZ < vertices[i+2].z)
maxZ = vertices[i+2].z;
}
return getBounds2D(theCamera, buffer, minX, minY, maxX, maxY);
     }
     public Rectangle getBounds2D(Camera theCamera, FrameBuffer buffer, float minX, float minY, float maxX, float maxY) {
SimpleVector minP = Interact2D.project3D2D(theCamera, buffer, new SimpleVector(minX, minY, -20f));//z=0f
SimpleVector maxP = Interact2D.project3D2D(theCamera, buffer, new SimpleVector(maxX, maxY, -20f));//z=0f
return new Rectangle((int)minP.x, (int)minP.y, (int)Math.abs(maxP.x-minP.x), (int)Math.abs(maxP.y-minP.y));
     }

EgonOlsen

Maybe because you are actually calculating the box in 3D world space and then project that into 2D. I think the right solution would be to project it into 2D and then calculate the box based on that.

About the KeyMapper...i'm not sure about this. Don't the keyPressed()/keyReleased() methods get called?

AGP

Only keyReleased gets called with key code 0. It's weird.

AGP

As per your suggestion, I wrote this:

     private Point[] get2DVertices(SimpleVector[] vertices3d, Camera theCamera, FrameBuffer buffer) {
Point[] vertices = new Point[vertices3d.length];
for (int i = 0; i < vertices.length; i++) {
     SimpleVector point = Interact2D.project3D2D(theCamera, buffer, vertices3d[i]);
     vertices[i] = new Point((int)(point.x+.5f), (int)(point.y+.5f));
}
return vertices;
     }
     public Rectangle get2DBounds(Camera theCamera, FrameBuffer buffer) {
System.out.println("get2DBounds");
PolygonManager polyManager = unitPlane.getPolygonManager();
SimpleVector[] vertices3d = new SimpleVector[polyManager.getMaxPolygonID()*3];
int currentPoly = 0;
int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, minZ = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE, maxZ = Integer.MIN_VALUE;
for (int i = 0; i < vertices3d.length; i+=3) {
     vertices3d[i] = polyManager.getTransformedVertex(currentPoly, 0);
     vertices3d[i+1] = polyManager.getTransformedVertex(currentPoly, 1);
     vertices3d[i+2] = polyManager.getTransformedVertex(currentPoly++, 2);
}
Point[] vertices = this.get2DVertices(vertices3d, theCamera, buffer);
for (int i = 0; i < vertices.length; i+=3) {
     if (minX > vertices[i].x)
minX = vertices[i].x;
     if (minX > vertices[i+1].x)
minX = vertices[i+1].x;
     if (minX > vertices[i+2].x)
minX = vertices[i+2].x;
     if (maxX < vertices[i].x)
maxX = vertices[i].x;
     if (maxX < vertices[i+1].x)
maxX = vertices[i+1].x;
     if (maxX < vertices[i+2].x)
maxX = vertices[i+2].x;
     if (minY > vertices[i].y)
minY = vertices[i].y;
     if (minY > vertices[i+1].y)
minY = vertices[i+1].y;
     if (minY > vertices[i+2].y)
minY = vertices[i+2].y;
     if (maxY < vertices[i].y)
maxY = vertices[i].y;
     if (maxY < vertices[i+1].y)
maxY = vertices[i+1].y;
     if (maxY < vertices[i+2].y)
maxY = vertices[i+2].y;
}
return new Rectangle(minX, minY, (int)Math.abs(maxX-minX), (int)Math.abs(maxY-minY));
     }


Now my question is: how expensive is the project3d2d method? Because as it is I'm using it on all the vertices as opposed to just what I had perceived as necessary...

EgonOlsen

#12
Shouldn't be too expensive (as long as the models aren't too complex). To speed it up a little, you can enable lazy transformation at the beginning of the method and disable it afterwards.