OGRE中Query Mask

Query Masks

Assuming we have two kinds of objects which are Robots and Ninjas.

Notice that no matter what mode we are in we can select either object. Our RaySceneQuery will return either Robots or Ninjas, whichever is in front. It doesn't have to be this way though. All MovableObjects allow you to set a mask value for them, and SceneQueries allow you to filter your results based on this mask. All masks are done using the binary AND operation, so if you are unfamiliar with this, you should brush up on it before continuing.

The first thing we are going to do is create the mask values. Go to the very beginning of the IntermediateTutorial3 class and add this after the public statement:

enum QueryFlags
        NINJA_MASK = 1<<0,
        ROBOT_MASK = 1<<1

This creates an enum with two values, which in binary are 0001 and 0010. Now, every time we create a Robot entity, we call its "setMask" function to set the query flags to be ROBOT_MASK. Every time we create a Ninja entity we call its "setMask" function and use NINJA_MASK instead. Now, when we are in Ninja mode, we will make the RaySceneQuery only consider objects with the NINJA_MASK flag, and when we are in Robot mode we will make it only consider ROBOT_MASK.

Find this section of mousePressed:

if (bRobotMode)
        sprintf(name, "Robot%d", mCount++);
        ent = mSceneMgr->createEntity(name, "robot.mesh");
} // if
        sprintf(name, "Ninja%d", mCount++);
        ent = mSceneMgr->createEntity(name, "ninja.mesh");
} // else

We will add two lines to set the mask on both of them:

if (bRobotMode)
        sprintf(name, "Robot%d", mCount++);
        ent = mSceneMgr->createEntity(name, "robot.mesh");
} // if
        sprintf(name, "Ninja%d", mCount++);
        ent = mSceneMgr->createEntity(name, "ninja.mesh");
} // else

We still need to make it so that when we are in one mode, we can only click and drag objects of the corresponding type. We need to set the query flags so that only the correct object type can be selected. We accomplish this by setting the query mask in the RaySceneQuery to be the ROBOT_MASK in Robot mode, and set it to NINJA_MASK in Ninja mode. Find this code in the mousePressed() function:


Add this line of code after it:

mRayScnQuery->setQueryMask(bRobotMode ? ROBOT_MASK : NINJA_MASK);

Compile and run the tutorial. We now select only the objects we are looking for. All rays that pass through other objects go through them and hit the correct object. We are now finished working on this code. The next section will not be modifying it.

Query Type Masks

There's one more thing to consider when using scene queries. Suppose you added a billboardset or a particle system to your scene above, and you want to move it around. You will find that the query never returns the billboardset that you click on. This is because the SceneQuery has another mask, the QueryTypeMask, that limits you to selecting only the type specified as the flag. By default when you do a query, it returns only objects of entity type.

In your code, if you want your query to return BillboardSets or ParticleSystems, you'll have to do this first before executing your query:


Now the query will only return BillboardSets or ParticleSystems as results.

There are 6 types of QueryTypeMask defined in the SceneManager class as static members:

WORLD_GEOMETRY_TYPE_MASK //Returns world geometry.
ENTITY_TYPE_MASK         //Returns entities.
FX_TYPE_MASK             //Returns billboardsets / particle systems.
STATICGEOMETRY_TYPE_MASK //Returns static geometry.
LIGHT_TYPE_MASK          //Returns lights.
USER_TYPE_MASK_LIMIT     //User type mask limit.

The default QueryTypeMask when the property is not set manually is ENTITY_TYPE_MASK.

More on Masks

Our mask example is very simple, so I would like to go through a few more complex examples.

Setting a MovableObject's Mask

Every time we want to create a new mask, the binary representation must contain only one 1 in it. That is, these are valid masks:


And so on. We can very easily create these values by taking 1 and bitshifting them by a position value. That is:

00000001 = 1<<0
00000010 = 1<<1
00000100 = 1<<2
00001000 = 1<<3
00010000 = 1<<4
00100000 = 1<<5
01000000 = 1<<6
10000000 = 1<<7

All the way up to 1<<31. This gives us 32 distinct masks we can use for MovableObjects.

Querying for Multiple Masks

We can query for multiple masks by using the bitwise OR operator. Let's say we have three different groups of objects in a game:

enum QueryFlags

Now, if we wanted to query for only friendly characters we could do:


If we want the query to return both enemy characters and stationary objects, we would use:


If you use a lot of these types of queries, you might want to define this in the enum:


And then simply use OBJECTS_ENEMIES to query.

Querying for Everything but a Mask

You can also query for anything other than a mask using the bit inversion operator, like so:


Which will return everything other than friendly characters. You can also do this for multiple masks:


Which would return everything other than friendly characters and stationary objects.

Selecting all Objects or No Objects

You can do some very interesting stuff with masks. The thing to remember is, if you set the query mask QM for a SceneQuery, it will match all MovableObjects that have the mask OM if (QM & OM) contains at least one 1. Thus, setting the query mask for a SceneQuery to 0 will make it return no MovableObjects. Setting the query mask to ~0 (0xFFFFF...) will make it return all MovableObjects that do not have a 0 query mask.

Using a query mask of 0 can be highly useful in some situations. For example, the TerrainSceneManager does not use QueryMasks when it returns a worldFragment. By doing this:


You will get ONLY the worldFragment in your RaySceneQueries for that SceneManager. This can be very useful if you have a lot of objects on screen and you do not want to waste time looping through all of them if you only need to look for the Terrain intersection.


posted on 2013-05-30 14:47  zuoc  阅读(424)  评论(0编辑  收藏  举报