set a type to a SceneNode?

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
danielmccarthy
Posts: 51
Joined: Fri May 30, 2014 12:55 am

set a type to a SceneNode?

Post by danielmccarthy »

Hi,
I have a problem. Lets say for example I have a tree node and a player node. How will you be able to tell the difference when using a ray to check if the mouse has hovered over them?. I thought of an idea and that was having the lower 16 bits of the nodeID as the object type and the upper 16 bits as the objectID. I think this is a bit hacky though

Any advice friends?

Thanks,
Dan
danielmccarthy
Posts: 51
Joined: Fri May 30, 2014 12:55 am

Re: set a type to a SceneNode?

Post by danielmccarthy »

bump
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Re: set a type to a SceneNode?

Post by Seven »

what you describe is a perfectly normal method for doing this.

(warning : ramble in progress below)

personally..........

I use an object class to house a scenenode.
in that object class, I have an ID and a TYPE variable.

then I copied the function called getPickedNodeBB and made a new one called

void CSO_Editor::getPickedNodeBB(OBJECT_TYPE objecttype, ISceneNode* root,
core::line3df& ray, s32 bits, bool noDebugObjects,
f32& outbestdistance, ISceneNode*& outbestnode)


and just added a call to bool CSO_Editor::isSelectable(CSObject* obj, OBJECT_TYPE objecttype) in it.


I use it like this..........

Code: Select all

 
    //! Returns the nearest scene node which collides with a 3d ray and
    //! which id matches a bitmask.
    ISceneNode* CSO_Editor::getSceneNodeFromRayBB(OBJECT_TYPE objecttype,
        const core::line3d<f32>& ray,
        s32 idBitMask, bool noDebugObjects, scene::ISceneNode* root)
    {
        ISceneNode* best = 0;
        f32 dist = FLT_MAX;
 
        core::line3d<f32> truncatableRay(ray);
 
        getPickedNodeBB(objecttype, (root == 0) ? getSmgr()->getRootSceneNode() : root, truncatableRay,
            idBitMask, noDebugObjects, dist, best);
 
        return best;
    }
 
the only part that needed changed was this part

Code: Select all

 
                CSObject* obj = getLevel()->getObjectFactory()->getObjectPointer(current->getID());
                if ((noDebugObjects ? !current->isDebugObject() : true) &&
                    (isSelectable(obj, objecttype))) 
 

Code: Select all

 
    bool CSO_Editor::isSelectable(CSObject* obj, OBJECT_TYPE objecttype)
    {
        if (!obj) return false;
        if (obj->getDebugObject()) return false;
        if (obj->getObjectType() == objecttype) return true;
        else return false;
    }
 
    //! recursive method for going through all scene nodes
    void CSO_Editor::getPickedNodeBB(OBJECT_TYPE objecttype, ISceneNode* root,
        core::line3df& ray, s32 bits, bool noDebugObjects,
        f32& outbestdistance, ISceneNode*& outbestnode)
    {
        const ISceneNodeList& children = root->getChildren();
        const core::vector3df rayVector = ray.getVector().normalize();
 
        ISceneNodeList::ConstIterator it = children.begin();
        for (; it != children.end(); ++it)
        {
            ISceneNode* current = *it;
 
            if (current->isVisible())
            {
                //              printf("ID = %d/n", current->getID());
                CSObject* obj = getLevel()->getObjectFactory()->getObjectPointer(current->getID());
                if ((noDebugObjects ? !current->isDebugObject() : true) &&
                    (isSelectable(obj, objecttype))) //(bits != 0 && (current->getID() & bits))))
                    //                  (bits == 0 || (isSelectable(obj, objecttype)))) //(bits != 0 && (current->getID() & bits))))
                {
                    // get world to object space transform
                    core::matrix4 worldToObject;
                    if (!current->getAbsoluteTransformation().getInverse(worldToObject))
                        continue;
 
                    // transform vector from world space to object space
                    core::line3df objectRay(ray);
                    worldToObject.transformVect(objectRay.start);
                    worldToObject.transformVect(objectRay.end);
 
                    const core::aabbox3df & objectBox = current->getBoundingBox();
 
                    // Do the initial intersection test in object space, since the
                    // object space box test is more accurate.
                    if (objectBox.isPointInside(objectRay.start))
                    {
                        // use fast bbox intersection to find distance to hitpoint
                        // algorithm from Kay et al., code from gamedev.net
                        const core::vector3df dir = (objectRay.end - objectRay.start).normalize();
                        const core::vector3df minDist = (objectBox.MinEdge - objectRay.start) / dir;
                        const core::vector3df maxDist = (objectBox.MaxEdge - objectRay.start) / dir;
                        const core::vector3df realMin(core::min_(minDist.X, maxDist.X), core::min_(minDist.Y, maxDist.Y), core::min_(minDist.Z, maxDist.Z));
                        const core::vector3df realMax(core::max_(minDist.X, maxDist.X), core::max_(minDist.Y, maxDist.Y), core::max_(minDist.Z, maxDist.Z));
 
                        const f32 minmax = core::min_(realMax.X, realMax.Y, realMax.Z);
                        // nearest distance to intersection
                        const f32 maxmin = core::max_(realMin.X, realMin.Y, realMin.Z);
 
                        const f32 toIntersectionSq = (maxmin>0 ? maxmin*maxmin : minmax*minmax);
                        if (toIntersectionSq < outbestdistance)
                        {
                            outbestdistance = toIntersectionSq;
                            outbestnode = current;
 
                            // And we can truncate the ray to stop us hitting further nodes.
                            ray.end = ray.start + (rayVector * sqrtf(toIntersectionSq));
                        }
                    }
                    else
                        if (objectBox.intersectsWithLine(objectRay))
                        {
                            // Now transform into world space, since we need to use world space
                            // scales and distances.
                            core::aabbox3df worldBox(objectBox);
                            current->getAbsoluteTransformation().transformBox(worldBox);
 
                            core::vector3df edges[8];
                            worldBox.getEdges(edges);
 
                            /* We need to check against each of 6 faces, composed of these corners:
                            /3--------/7
                            /  |      / |
                            /   |     /  |
                            1---------5  |
                            |   2- - -| -6
                            |  /      |  /
                            |/        | /
                            0---------4/
 
                            Note that we define them as opposite pairs of faces.
                            */
                            static const s32 faceEdges[6][3] =
                            {
                                { 0, 1, 5 }, // Front
                                { 6, 7, 3 }, // Back
                                { 2, 3, 1 }, // Left
                                { 4, 5, 7 }, // Right
                                { 1, 3, 7 }, // Top
                                { 2, 0, 4 }  // Bottom
                            };
 
                            core::vector3df intersection;
                            core::plane3df facePlane;
                            f32 bestDistToBoxBorder = FLT_MAX;
                            f32 bestToIntersectionSq = FLT_MAX;
 
                            for (s32 face = 0; face < 6; ++face)
                            {
                                facePlane.setPlane(edges[faceEdges[face][0]],
                                    edges[faceEdges[face][1]],
                                    edges[faceEdges[face][2]]);
 
                                // Only consider lines that might be entering through this face, since we
                                // already know that the start point is outside the box.
                                if (facePlane.classifyPointRelation(ray.start) != core::ISREL3D_FRONT)
                                    continue;
 
                                // Don't bother using a limited ray, since we already know that it should be long
                                // enough to intersect with the box.
                                if (facePlane.getIntersectionWithLine(ray.start, rayVector, intersection))
                                {
                                    const f32 toIntersectionSq = ray.start.getDistanceFromSQ(intersection);
                                    if (toIntersectionSq < outbestdistance)
                                    {
                                        // We have to check that the intersection with this plane is actually
                                        // on the box, so need to go back to object space again.
                                        worldToObject.transformVect(intersection);
 
                                        // find the closest point on the box borders. Have to do this as exact checks will fail due to floating point problems.
                                        f32 distToBorder = core::max_(core::min_(core::abs_(objectBox.MinEdge.X - intersection.X), core::abs_(objectBox.MaxEdge.X - intersection.X)),
                                            core::min_(core::abs_(objectBox.MinEdge.Y - intersection.Y), core::abs_(objectBox.MaxEdge.Y - intersection.Y)),
                                            core::min_(core::abs_(objectBox.MinEdge.Z - intersection.Z), core::abs_(objectBox.MaxEdge.Z - intersection.Z)));
                                        if (distToBorder < bestDistToBoxBorder)
                                        {
                                            bestDistToBoxBorder = distToBorder;
                                            bestToIntersectionSq = toIntersectionSq;
                                        }
                                    }
                                }
 
                                // If the ray could be entering through the first face of a pair, then it can't
                                // also be entering through the opposite face, and so we can skip that face.
                                if (!(face & 0x01))
                                    ++face;
                            }
 
                            if (bestDistToBoxBorder < FLT_MAX)
                            {
                                outbestdistance = bestToIntersectionSq;
                                outbestnode = current;
 
                                // If we got a hit, we can now truncate the ray to stop us hitting further nodes.
                                ray.end = ray.start + (rayVector * sqrtf(outbestdistance));
                            }
                        }
                }
 
                // Only check the children if this node is visible.
                getPickedNodeBB(objecttype, current, ray, bits, noDebugObjects, outbestdistance, outbestnode);
            }
        }
    }
 
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: set a type to a SceneNode?

Post by CuteAlien »

The scenenode id is actually used by the Irrlicht collision functions (I'd prefer if we had an own there, but that did grow historically).
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
Post Reply