New method proposal: ISceneNode::getScreenSpace()

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: New method proposal: ISceneNode::getScreenSpace()

Post by Mel »

I think it is kinda nice to know the screen quad that uses a certain node, perhaps it would be possible (with all due respect to the change on the video driver of stuff...) to render certain objects using a clipping rectangle that englobed them and that enabled effects only on that region of the screen

Unreal uses this for the LOD selection? firts news i had O.o I knew the Torque Game Engine used the size of an object on screen to pick the LOD, but i never knew the exact details.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
CuteAlien
Admin
Posts: 9733
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: New method proposal: ISceneNode::getScreenSpace()

Post by CuteAlien »

Ah - that way for LOD. Kinda didn't get it before (and was really talking about transparency sorting).
Thought couldn't allow to clip it when using it for LOD that way I guess.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
REDDemon
Developer
Posts: 1044
Joined: Tue Aug 31, 2010 8:06 pm
Location: Genova (Italy)

Re: New method proposal: ISceneNode::getScreenSpace()

Post by REDDemon »

In doom III each object was rendered multiple times (1 for each light affecting it) but setting scissor only to the enclosing rectangle for discarding light-less pixels of a mesh, would be cool to have something that gives the enclosing screen rectangle of lights.
Junior Irrlicht Developer.
Real value in social networks is not about "increasing" number of followers, but about getting in touch with Amazing people.
- by Me
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: New method proposal: ISceneNode::getScreenSpace()

Post by Mel »

Okay, take a look at this. This code is stand alone.

main.cpp

Code: Select all

#include <irrlicht.h>
#include "CScreenSpaceQuadsNode.h"
 
using namespace irr;
 
#define MAX_NODES 7
 
int main(int argc, char* argv[])
{
    irr::SIrrlichtCreationParameters prm;
    prm.DriverType = video::EDT_OPENGL;
    prm.WindowSize = core::dimension2du(1280, 720);
 
    IrrlichtDevice* device = createDeviceEx(prm);
 
    //No device? UPS!
    if (!device)
        return 1;
 
    video::IVideoDriver* driver = device->getVideoDriver();
    scene::ISceneManager* manager = device->getSceneManager();
 
    manager->addCameraSceneNodeFPS();
 
    //Create a sphere mesh
    scene::IMesh* sphereMesh = manager->addSphereMesh("Sphere", 10.0f);
 
    //Add the quad renderer...
    scene::CScreenQuadsSceneNode* quads = new scene::CScreenQuadsSceneNode(manager->getRootSceneNode(), manager, -32);
 
    //Add some nodes...
    for (u32 j = 1; j < 4;++j)
        for (u32 i = 0; i < MAX_NODES; ++i)
        {
            scene::IMeshSceneNode* node = manager->addMeshSceneNode(sphereMesh);
            node->setPosition(core::vector3df(j*300 * sin(2 * core::PI*i / MAX_NODES), 0, j*300 * cos(2 * core::PI*i / MAX_NODES)));
            node->setScale(core::vector3df(j,j,j));
            node->getMaterial(0).DiffuseColor = video::SColor(0xFFFFFFFF);
            node->getMaterial(0).NormalizeNormals = true;
            quads->registerNode(node);
        }
 
    //Add a light
    scene::ILightSceneNode* light = manager->addLightSceneNode(manager->getRootSceneNode(), core::vector3df(500, 500, 500), video::SColorf(1, 1, 1, 1), 1000.0f);
 
    quads->drop();
    quads = 0;
 
    while (device->run())
    {
        driver->beginScene();
        driver->draw2DRectangle(core::recti(0, 0, 1280, 720), video::SColor(0xFF00007F), video::SColor(0xFF00007F), video::SColor(0xFF007F7F), video::SColor(0xFF007F7F));
        manager->drawAll();
        driver->endScene();
    }
    device->drop();
 
    return 0;
}
"CScreenSpaceQuadsNode.h"

Code: Select all

 
#ifndef _CSCREENQUADSSCENENODE_H_
#define _CSCREENQUADSSCENENODE_H_
 
#include <irrlicht.h>
#include <map>
 
//This is a scene node that will draw quads around registered scene nodes covering their screen space, also, you can retrieve the quads given a scene node via a irr::map
 
namespace irr
{
    namespace scene
    {
        typedef std::map<ISceneNode*, core::rectf> nodeRectMap;
 
        class CScreenQuadsSceneNode : public ISceneNode
        {
            //Nodes whose screenspace we're going to calculate
            nodeRectMap nodes;
 
            //Dummy box
            core::aabbox3df box;
 
            //Video driver
            video::IVideoDriver* driver;
 
            //Active camera
            scene::ICameraSceneNode* camera;
 
            //Current resolution
            core::dimension2du resolution;
 
        public:
            //CTOR
            CScreenQuadsSceneNode(ISceneNode* parent, ISceneManager* manager, s32 id);
 
            //DTOR
            ~CScreenQuadsSceneNode();
 
            //Register scene nodes
            void registerNode(ISceneNode* node);
 
            //Clears the node map
            void clearNodes();
 
            //Render method
            void render();
 
            //Get Bounding box. Nothing too fancy here
            const core::aabbox3d<f32>& getBoundingBox() const;
 
            //Get the quad belonging to a given scene node
            core::rectf getScreenSpace(ISceneNode* node);
 
            //Animates this node. Updates the screen quads positions but there is a frame delay... Is okay, produces a cool "tracking" like effect :)
            void OnAnimate(u32 ms);
 
            //Registers this node for rendering
            void OnRegisterSceneNode();
 
        private:
            //Renders the rectangles
            void renderQuad(core::rectf& rect);
 
            //Calculates all the visible quads
            void calculateScreenQuads();
 
            //Caluculates a quad
            core::rectf calculateQuad(ISceneNode* node, ICameraSceneNode* camera);
        };
    }
}
 
#endif
 
"CScreenSpaceQuadsNode.cpp"

Code: Select all

 
#include "CScreenSpaceQuadsNode.h"
 
namespace irr
{
    namespace scene
    {
        //CTOR
        CScreenQuadsSceneNode::CScreenQuadsSceneNode(ISceneNode* parent, ISceneManager* manager, s32 id)
            :ISceneNode(parent,manager,id)
        {
            driver = getSceneManager()->getVideoDriver();
            resolution = driver->getScreenSize();
            printf("Node Tracker Ready for operation at %d x %d\n",resolution.Width,resolution.Height);
            setAutomaticCulling(EAC_OFF);
        }
 
        //DTOR
        CScreenQuadsSceneNode::~CScreenQuadsSceneNode()
        {
            nodes.clear();
        }
 
        //Register scene nodes
        void CScreenQuadsSceneNode::registerNode(ISceneNode* node)
        {
            //Not that it wouldn't be possible to do it, but it is kinda pointless to do this to a potential screen sized render XD
            if (node == this)
                return;
            
            if (node == nullptr)
                return;
 
            nodes[node] = core::rectf();
        }
 
        //Clears the node map
        void CScreenQuadsSceneNode::clearNodes()
        {
            nodes.clear();
        }
 
        //Get the quad belonging to a given scene node. If not found, returns an empty rectangle.
        core::rectf CScreenQuadsSceneNode::getScreenSpace(ISceneNode* node)
        {
            if (nodes.find(node) != nodes.end())
                return nodes[node];
            else
                return core::rectf();
        }
 
        //Render method
        void CScreenQuadsSceneNode::render()
        {
            //nothing to do here :)
            if (camera == nullptr)
                return;
 
            //No quads? no work
            if (nodes.size() == 0)
                return;
 
            nodeRectMap::iterator it;
 
            for (it = nodes.begin(); it != nodes.end(); ++it)
            {
                renderQuad((*it).second);
            }
        }
 
        //Get Bounding box. Nothing too fancy here
        const core::aabbox3d<f32>& CScreenQuadsSceneNode::getBoundingBox() const
        {
            return box;
        }
 
        //Animates this node. Updates the screen quads positions but there is a frame delay... Is okay, produces a cool "tracking" like effect :)
        void CScreenQuadsSceneNode::OnAnimate(u32 ms)
        {
            //Is there any active camera? if not, it is very easy to know where to continue after this...
            camera = getSceneManager()->getActiveCamera();
 
            if (camera == nullptr)
            {
                ISceneNode::OnAnimate(ms);
                return;
            }
 
            //Update all the rectangles
 
            //Using the standard map which implements iterative access
            nodeRectMap::iterator it;
 
            for (it = nodes.begin(); it != nodes.end(); ++it)
                (*it).second = calculateQuad((*it).first, camera);
 
            ISceneNode::OnAnimate(ms);
        }
 
        //Renders a quad
        void CScreenQuadsSceneNode::renderQuad(core::rectf& rect)
        {
            core::recti r = core::recti(rect.UpperLeftCorner.X, rect.UpperLeftCorner.Y, rect.LowerRightCorner.X, rect.LowerRightCorner.Y);
 
            u8 red, green;
            red = pow(abs((rect.getArea() / resolution.getArea())),1.0f/6.0f) * 255; //gets redder the bigger the quad is on screen ;) hint for LOD? :P
            green = 255 - red;
 
            driver->draw2DRectangleOutline(r, video::SColor(255, red, green, 0));
        }
 
        //Calculates the bounding quad
        core::rectf CScreenQuadsSceneNode::calculateQuad(ISceneNode* node, ICameraSceneNode* camera)
        {
            const scene::SViewFrustum* fr = camera->getViewFrustum();
            core::aabbox3df box, viewBox; //Necesary to have a box in view space and then in projection space
            core::matrix4 view, proj;//Necesary to obtain the box in view space and then in projection space
            f32 near, far;
            f32 vec[4]; //Irr won't do the homogeinization step for us... in any form! O.o
 
            //Get the node's aabb.
            box = node->getTransformedBoundingBox();
            //If the node is invisible, it has no screen space
            if (!node->isVisible())
                return core::rectf();
            //if the node is completely outside the view frustum, it has no screen space
            bool outside = false;
            for (u32 i = 0; i < 6 && !outside; ++i)
                outside = box.classifyPlaneRelation(fr->planes[i]) == core::ISREL3D_FRONT;
            if (outside)
                return core::rectf();
 
            //At this point the box is either completely inside the view volume, or clipped somewhere, in any case, it is worth the attempt.
 
            core::array<core::vector3df> points;//Box corners
            points.reallocate(8);
            node->getTransformedBoundingBoxEdges(points);
 
            //Get the AABB in view space.
            view = camera->getViewMatrix();
 
            //Transform into view space
            view.transformVect(vec, points[0]);
            viewBox.reset(vec[0] / vec[3], vec[1] / vec[3], vec[2] / vec[3]);
 
            for (u32 i = 1; i < 8; ++i)
            {
                view.transformVect(vec, points[i]);
                viewBox.addInternalPoint(vec[0] / vec[3], vec[1] / vec[3], vec[2] / vec[3]);
            }
 
            //Viewbox is aligned to the Z near and far planes
            near = camera->getNearValue();
            far = camera->getFarValue();
 
            //Clamp to minimum view Z
            if (viewBox.MinEdge.Z < near)
                viewBox.MinEdge.Z = near;
 
            //Clamp to maximum view Z
            if (viewBox.MaxEdge.Z > far)
                viewBox.MaxEdge.Z = far;
 
            //Copy the points again so we can transform them into projection space
            viewBox.getEdges(points.pointer());
            
            proj = camera->getProjectionMatrix();
 
            //Transform into projection space
            proj.transformVect(vec, points[0]);
            box.reset(vec[0] / vec[3], vec[1] / vec[3], vec[2] / vec[3]);
 
            for (u32 i = 1; i < 8; ++i)
            {
                proj.transformVect(vec, points[i]);
                box.addInternalPoint(vec[0] / vec[3], vec[1] / vec[3], vec[2] / vec[3]);
            }
 
            //The box is in projection space, clamp it to -1,1
            if (box.MinEdge.X < -1)
                box.MinEdge.X = -1;
 
            if (box.MaxEdge.X > 1)
                box.MaxEdge.X = 1;
            
            //Clamp the Y coordinate...
            if (box.MinEdge.Y < -1)
                box.MinEdge.Y = -1;
 
            if (box.MaxEdge.Y > 1)
                box.MaxEdge.Y = 1;
 
 
            //Transform into 0-range and swap Y to be in screen coordinates
            box.MinEdge.X = box.MinEdge.X*0.5f + 0.5f;
            box.MaxEdge.X = box.MaxEdge.X*0.5f + 0.5f;
 
            box.MinEdge.Y = -box.MinEdge.Y*0.5f + 0.5f;
            box.MaxEdge.Y = -box.MaxEdge.Y*0.5f + 0.5f;
 
            //Return this box in screen coordinates
            return core::rectf(box.MinEdge.X*resolution.Width, box.MinEdge.Y*resolution.Height, box.MaxEdge.X*resolution.Width, box.MaxEdge.Y*resolution.Height);
        }
 
        //Registers this node for rendering
        void CScreenQuadsSceneNode::OnRegisterSceneNode()
        {
            //Register in the last pass of the render
            getSceneManager()->registerNodeForRendering(this, ESNRP_TRANSPARENT_EFFECT);
            ISceneNode::OnRegisterSceneNode();
        }
    }
}
This should provide the following effect:

Image

Which is a bit conservative in the side that it is always larger than the actual bounding box, but it is because it is the bounding box of the viewspace bounding volume. I hope you can test it for yourselves! and if you find it useful, go ahead and use it :)

Just in case you want to use it for lights... Irr won't calculate the lights bounding volume, but it can calculate the screen space of ANY SCENE NODE that has a bounding box, which is every of them, as the scene nodes have in their interface a "getBoundingBox" method :)

I've intentionally made the tint of the quad become redder the bigger it is on the screen ;) (uses the area to calculate it) So using the quad area / screen area proportion you may have a cabal idea of how large is a node compared to the screen. And it works both in GL and DirectX alike.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Re: New method proposal: ISceneNode::getScreenSpace()

Post by Virion »

It seems like epic is still continue to use this method in UE4, as described in this page (the last sentence) http://docs.speedtree.com/doku.php?id=w ... els_in_ue4

Also, I managed to found the screenshot which shows the "screen size" settings in UE4 to confirm this:

Image Image
CuteAlien
Admin
Posts: 9733
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: New method proposal: ISceneNode::getScreenSpace()

Post by CuteAlien »

@Mel: Thanks! edit: Have to test (thought first there was a bug, but probably not...).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Re: New method proposal: ISceneNode::getScreenSpace()

Post by Mel »

I haven't tested it thoroughly, and for instance if you remove a node, the program will crash, and I would like to have a tighter bound on the objects, to but the functionality is there, and bears with anything that can be englobed by a bounding box, which is... everything :)
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
Post Reply