Projecting 3D position and size to 2D         Projecting 3D objects and position to a 2D plane
Print

One way to have HUD Elements like targeting indicators or playernames positioned on 3D objects is projecting the 3D position to 2D and positioning the HUD Elements there. The following code can be used to do that.

Projecting position

using namespace Ogre;
/// returns true if in front of the cam, and fills x,y with clamped screencoords in [-1;1]
// cam->getProjectionMatrix() is cached inside ogre
bool    ProjectPos    (Camera* cam,const Ogre::Vector3& pos,Ogre::Real& x,Ogre::Real& y) {
    Vector3 eyeSpacePos = cam->getViewMatrix(true) * pos;
    // z < 0 means in front of cam
    if (eyeSpacePos.z < 0) {
        // calculate projected pos
        Vector3 screenSpacePos = cam->getProjectionMatrix() * eyeSpacePos;
        x = screenSpacePos.x;
        y = screenSpacePos.y;
        return true;
    } else {
        x = (-eyeSpacePos.x > 0) ? -1 : 1;
        y = (-eyeSpacePos.y > 0) ? -1 : 1;
        return false;
    }
}

 

Projecting position and radius

using namespace Ogre;
/// returns true if in front of the cam, and fills x,y with clamped screencoords in [-1;1]
/// and fills cx,cy with projected size on screen in [0;1]
// cam->getProjectionMatrix() is cached inside ogre
bool    ProjectSizeAndPos    (Camera* cam,const Ogre::Vector3& pos,const Ogre::Real rad,Ogre::Real& x,Ogre::Real& y,Ogre::Real& cx,Ogre::Real& cy) {
    Camera* cam = mCamera;
    Vector3 eyeSpacePos = cam->getViewMatrix(true) * pos;
    // z < 0 means in front of cam
    if (eyeSpacePos.z < 0) {
        // calculate projected pos
        Vector3 screenSpacePos = cam->getProjectionMatrix() * eyeSpacePos;
        x = screenSpacePos.x;
        y = screenSpacePos.y;
        // calculate projected size
        Vector3 spheresize(rad, rad, eyeSpacePos.z);
        spheresize = cam->getProjectionMatrix() * spheresize;
        cx = spheresize.x;
        cy = spheresize.y;
        return true;
    } else {
        cx = 0;
        cy = 0;
        x = (-eyeSpacePos.x > 0) ? -1 : 1;
        y = (-eyeSpacePos.y > 0) ? -1 : 1;
        return false;
    }
}

 

Notes

  • a return value of true just means in front of the cam, not visible on screen (e.g. it might be too far on left)
  • to check if the object is visible, check if the projected x,y are in [-1;1]
  • to check if the object is partially visible, check if the projected x is in [-1-cx/2;1+cx/2] (y analogue with cy)

Tracking a hierarchical position

 

When tracking the position of a scenenode pMyChildNode that is the child of another scenenode pMyParentNode,
probably SceneNode::getWorldPosition() should be used to get an absolute position.

When only the position of pMyParentNode is updated,
the pMyChildNode->getWorldPosition() will normally only be update after rendering a frame,
this leads to the projected position lagging behind one frame.
This can be prevented by either :

  • calling pMyChildNode->needUpdate() right before calculating the projected position
  • calling pMyParentNode->_update(/*bool updateChildren=*/true,/*bool parentHasChanged=*/false) when moving the parent
  • also pMyChildNode->setListener() can be used to install a listener that is called at a time where getWorldPosition() will return the correct position, so that could be a good place to recalculate the projected position, but it also depends on the camera position, it would also have to be updated every time the camera moves, so pCamera->setListener() will also be needed. The problem is that this way, the position will be recalculated twice if the cam and the parent change, if that happens every frame, it might be better to recalculate right before rendering a frame and using _update() or needUpdate() instead of the listeners.

Alias: Projecting_3D_position_and_size_to_2D

 


Contributors to this page: JustBoo , OgreWikiBot and jacmoe133512 points  .
Page last modified on Wednesday 30 of June, 2010 21:03:38 UTC by JustBoo.


The content on this page is licensed under the terms of the Creative Commons Attribution-ShareAlike License.
As an exception, any source code contributed within the content is released into the Public Domain.