Get Screen Scale Factor

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Get Screen Scale Factor

Post by Klunk »

Is there a function in irrlicht that returns the screen scale factor for a given point in world space ? I've been trolling through the api not found anything.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Re: Get Screen Scale Factor

Post by hybrid »

Screen scale for point in world space? A point is a point, no matter where and how it is projected. If you have a dimension somewhere, you can transform the vertices manually and get the resulting dimension. There's no automatic method for this, AFAIK. Don't know if it's too helpful, anyway. Other way round is probably more often used.
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: Get Screen Scale Factor

Post by Klunk »

its a function from the 3dsmax sdk that I would like to replicate in irrlicht, its used in the following manner in max

Code: Select all

Matrix3 tm = inode->GetObjectTM(t);     
        
        float size;
        int screenSize;
 
        pblock2->GetValue(pointobj_size, t, size, FOREVER);
        pblock2->GetValue(pointobj_screensize, t, screenSize, FOREVER);
 
        float zoom = 1.0f;
        if (screenSize)
                zoom = vpt->GetScreenScaleFactor(tm.GetTrans())*0.005f;
        if (zoom==0.0f) 
                 zoom = 1.0f;
 
        size *= zoom;
it gives you the option for helper objects (dummy, point, etc) to be drawn with a constant screen size.

float GetScreenScaleFactor ( const Point3 worldPoint ) Returns the screen scale factor for a point given in world coordinates. This factor gives the width in world-space units at the point's distance of the viewport (from the sdk doc).

Am i right in saying that its the width across the camera fustrum at the world point would give me the same result ?
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: Get Screen Scale Factor

Post by Klunk »

the answer to my question was yes. So for future reference...

add the following function to your custom scene node

Code: Select all

        float GetScreenScaleFactor(vector3df worldpnt,ICameraSceneNode* camera)
        {
                plane3df        near_fplane = camera->getViewFrustum()->planes[SViewFrustum::VF_NEAR_PLANE];
                vector3df       cam_z = near_fplane.Normal;
                vector3df       width_vtr = camera->getUpVector().crossProduct(cam_z);
                plane3df left_fplane = camera->getViewFrustum()->planes[SViewFrustum::VF_LEFT_PLANE];
                plane3df right_fplane = camera->getViewFrustum()->planes[SViewFrustum::VF_RIGHT_PLANE];
                
                vector3df p1,p2;
                bool i1 = left_fplane.getIntersectionWithLine(worldpnt,-width_vtr,p1);
                bool i2 = right_fplane.getIntersectionWithLine(worldpnt,width_vtr,p2);
 
                return p1.getDistanceFrom(p2);
        }
the following code in your node render function will keep it a fixed size on the screen

Code: Select all

float temp = size;
float zoom = GetScreenScaleFactor(getPosition(),SceneManager->getActiveCamera()) * 0.002f; // 0.002 is a constant scalar
if(zoom == 0.0f)
        zoom = 1.0f;
temp *= zoom;
 
// example wireframe screen gizmo draw
for(u32 i = 0; i < edges.size(); i++)
     driver->draw3DLine(verts[edges[i].X] * temp,verts[edges[i].Y] * temp,colour);
obviously if theres an easier way to get the width of the view at a specific point please chip in.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Re: Get Screen Scale Factor

Post by hybrid »

Ok, so you want the dimensions of a full screen plane at a certain depth. I got a completely different idea from the first description. But for this purpose your code seems ok. I guess you will also need the height at that depth, and get the maximum of both values (or both values in case of non-uniform scaling) to really fill the whole screen. If not, you can still switch to the height, as this would not need the cross product then.
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: Get Screen Scale Factor

Post by Klunk »

i'm not sure it matters which is used width or height as long as it's constant as the value can be tuned with the scalar term. slightly neater method for calculating the scalefactor

Code: Select all

float GetScreenScaleFactor(const vector3df& worldpnt,ICameraSceneNode* camera)
{
        vector3df viewpnt = worldpnt;
        camera->getViewMatrix().transformVect(viewpnt);
        return  2 * viewpnt.Z / camera->getProjectionMatrix()[0];
}
 
my only concern would be what happens with an orthographic camera
shadowslair
Posts: 758
Joined: Mon Mar 31, 2008 3:32 pm
Location: Bulgaria

Re: Get Screen Scale Factor

Post by shadowslair »

Klunk wrote:my only concern would be what happens with an orthographic camera
Nothing. You check if your camera is ortho and if so, you simply return 1.0f. With ortho projection all nodes will always keep their original scales, right?
"Although we walk on the ground and step in the mud... our dreams and endeavors reach the immense skies..."
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: Get Screen Scale Factor

Post by Klunk »

interestingly or not, it provides a similar method of getting screen coordinates from a 3d position.

Code: Select all

        void GetScreenCoords(const vector3df& worldpnt,ICameraSceneNode* camera, position2di& screenCoords)
        {
// get the viewport 
 
                const recti& viewPort = SceneManager->getVideoDriver()->getViewPort();
 
// transform world point into view space
 
                vector3df viewpnt = worldpnt;
                camera->getViewMatrix().transformVect(viewpnt);
 
// get half the view port size at the point
 
                float hwidth = viewpnt.Z / camera->getProjectionMatrix()[0];
                float hheight = viewpnt.Z / camera->getProjectionMatrix()[5];
 
// get the equivalent screen coords
 
                screenCoords.X = round32((hwidth + viewpnt.X) * viewPort.getWidth()/ hwidth * 0.5f);
                screenCoords.Y = round32((hheight - viewpnt.Y) * viewPort.getHeight()/ hheight * 0.5f);
        } 
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: Get Screen Scale Factor

Post by Klunk »

could this thread be moved to code snippets please.
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Re: Get Screen Scale Factor

Post by hybrid »

I think the latter two are pretty close to getScreenCoordinatesFrom3DPosition, aren't they?
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: Get Screen Scale Factor

Post by Klunk »

similar, though not by design :)

you can remove one of the divisions at the expense of a check for div by zero, though i doubt you would use it enough for it to be a performance gain.

Code: Select all

void GetScreenCoords(const vector3df& worldpnt,ICameraSceneNode* camera, position2di& screenCoords)
        {
// get the viewport size
 
                const recti& viewPort = SceneManager->getVideoDriver()->getViewPort();
 
// transform world point into view space
 
                vector3df viewpnt = worldpnt;
                camera->getViewMatrix().transformVect(viewpnt);
 
                if(viewpnt.Z == 0.0f) viewpnt.Z = FLT_EPSILON;
 
// get half the view port size at the point
 
                float hwidth = camera->getProjectionMatrix()[0]/viewpnt.Z;
                float hheight = camera->getProjectionMatrix()[5]/viewpnt.Z;
 
// get the equivalent screen coords
 
                screenCoords.X = round32((1.0f + hwidth * viewpnt.X) * viewPort.getWidth() * 0.5f);
                screenCoords.Y = round32((1.0f - hheight * viewpnt.Y) * viewPort.getHeight() * 0.5f);
        }
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: Get Screen Scale Factor

Post by Klunk »

as a quick addendum, you can use the following to draw something more complicated in a fixed screensize

Code: Select all

float zoom = 1.0f;
if(fixedsize)
        zoom = GetScreenScaleFactor(getPosition(),SceneManager->getActiveCamera()) * screenscale;
if(zoom == 0.0f)
        zoom = 1.0f;
 
matrix4 scalar;
scalar.setScale(zoom);
driver->setTransform(ETS_WORLD,AbsoluteTransformation * scalar);
driver->setMaterial(mbuf->setMaterial());
driver->drawMeshBuffer(mbuf);
Klunk
Posts: 264
Joined: Mon Jan 10, 2011 5:21 pm

Re: Get Screen Scale Factor

Post by Klunk »

updated to handle orthogonal camera

Code: Select all

float GetScreenScaleFactor(const vector3df& worldpnt,ICameraSceneNode* camera)
{
    if(camera->isOrthogonal()) 
        return 2.0f/camera->getProjectionMatrix()[0];
    vector3df viewpnt = worldpnt;
    camera->getViewMatrix().transformVect(viewpnt);
    return  2.0f * viewpnt.Z / camera->getProjectionMatrix()[0];
}
Post Reply