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.
New method proposal: ISceneNode::getScreenSpace()
Re: New method proposal: ISceneNode::getScreenSpace()
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
Re: New method proposal: ISceneNode::getScreenSpace()
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.
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
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Re: New method proposal: ISceneNode::getScreenSpace()
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
Real value in social networks is not about "increasing" number of followers, but about getting in touch with Amazing people.
- by Me
Re: New method proposal: ISceneNode::getScreenSpace()
Okay, take a look at this. This code is stand alone.
main.cpp
"CScreenSpaceQuadsNode.h"
"CScreenSpaceQuadsNode.cpp"
This should provide the following effect:
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.
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;
}
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
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();
}
}
}
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
Re: New method proposal: ISceneNode::getScreenSpace()
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:
Also, I managed to found the screenshot which shows the "screen size" settings in UE4 to confirm this:
My company: http://www.kloena.com
My blog: http://www.zhieng.com
My co-working space: http://www.deskspace.info
My blog: http://www.zhieng.com
My co-working space: http://www.deskspace.info
Re: New method proposal: ISceneNode::getScreenSpace()
@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
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Re: New method proposal: ISceneNode::getScreenSpace()
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