Page 1 of 1

getScreenCoordinatesFrom3DPosition trouble

Posted: Thu Jul 05, 2007 1:11 pm
by Dave
Hi everyone,

Don't know if it's a bug or just me with some wrong code, so I made the thread here.

I use the function 'getScreenCoordinatesFrom3DPosition' to calculate the 2D screen coordinates of each corner of a bounding box. Here's an eplanation of what I'm doing in PSEUDO-code:

Code: Select all

V_3DVECTOR corner[8]; // Corners of the bounding box, trust me that they're in their.
irr::core::position2d<irr::s32> screen[8]; // 2D screen coordinates of the bounding box.
irr::core::aabbox3d<irr::f32> box = node->getTransformedBoundingBox(); // Used for corner calculation.

// Now I fill the 'screen' array variable (for explanation just the first element).
screen[0] = collision->getScreenCoordinatesFrom3DPosition(corner[0]);
Now I'll show the results of both corner and screen vector:
Corner:
[0] {X=0.30330077 Y=19.511070 Z=5.3033009 }
[1] {X=0.30330077 Y=4.5110703 Z=5.3033009 }
[2] {X=0.30330077 Y=19.511070 Z=-15.909903 }
[3] {X=0.30330077 Y=4.5110703 Z=-15.909903 }
[4] {X=0.30330071 Y=19.511070 Z=5.3033009 }
[5] {X=0.30330071 Y=4.5110703 Z=5.3033009 }
[6] {X=0.30330071 Y=19.511070 Z=-15.909903 }
[7] {X=0.30330071 Y=4.5110703 Z=-15.909903 }
Screen:
[0] {X=402 Y=124 }
[1] {X=403 Y=368 }
[2] {X=402 Y=236 }
[3] {X=403 Y=253 }
[4] {X=403 Y=253 }
[5] {X=402 Y=236 }
[6] {X=403 Y=368 }
[7] {X=402 Y=124 }
As you can see, the X element of each corner is the same; that's not the problem. However, the same goes for the screen coordinates.
That's kinda weird, how can all the corners of a cube (with each side of equal length) have the same X-coordinate? This happens when I start rotating the cube, in my case along the Y-axis. Sometimes the X screen coordinates are the same, and sometimes the Y.
Does anyone know what I'm doing wrong?

Posted: Thu Jul 05, 2007 6:23 pm
by vitek
The box returned by getTransformedBoundingBox() is world axis aligned. That means that the X, Y and Z axes of the box are in line with the world coordinate system. Given that, the planes of the box will always be aligned with these axes. That is why you see two distinct sets of values for each axis. There is one set of values for the left [top, front] side of the world aligned box, and one for the right [bottom, back].

The box returned by getTransformedBoundingBox() is not accurate. I've posted sample code to illustrate this problem several times. If you display the transformed box of a rotating cube you'll see the problem. You can use the object bounding box, transformation matrix and transformBoxEx() for a more accurate bounding box. If you want to get the edges of a box in 2d coordinates, the following will give you the most accurate results...

Code: Select all

  // get the object space bounding box
  const core::aabbox3df box = node->getBoundingBox();

  // get the corners of the box in object coordinate system
  core::vector3df edges[8];
  box.getEdges(edges);

  // convert each corner to world coordinates
  u32 e;
  for (e = 0; e < 8; ++e)
  {
    node->getAbsoluteTransformation().transformVect(edges[e]);
  }

  // now the edges are in world coordinates and they represent
  // the corners of the object space bounding box. this gives us
  // the smallest box that encloses the scene node. any other
  // method for calcluating the world space bounding box from
  // the object space bounding box will be less accurate.
If you wanted to display a 2d rectangle that encloses the edges, the following code would do that...

Code: Select all

  // get the corners of the box in screen coordinates
  core::rect<s32> rect;
  rect.UpperLeftCorner =
  rect.LowerRightCorner = smgr->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(edges[0]);

  for (e = 1; e < 8; ++e)
  {
    core::position2di edge =
        smgr->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition(edges[e]);

    rect.UpperLeftCorner.X = core::min_(rect.UpperLeftCorner.X, edge.X);
    rect.UpperLeftCorner.Y = core::min_(rect.UpperLeftCorner.Y, edge.Y);

    rect.LowerRightCorner.X = core::max_(rect.LowerRightCorner.X, edge.X);
    rect.LowerRightCorner.Y = core::max_(rect.LowerRightCorner.Y, edge.Y);
  }

  driver->draw2DRectangle(video::SColor(64, 255, 0, 0), rect);
Travis

Posted: Fri Jul 06, 2007 7:51 am
by Dave
Great! I guess is didn't look good enough on the forums for your already posted solution. Nevertheless, it works flawlessly! My solution that didn't even work correctly when rotating object, contained 3x more source lines for nothing :roll:
Thanks again vitek.