Author Topic: TypeMasks, what are they?  (Read 2447 times)

What are typemasks and are they server or client-sided? I know that doing something like (%player.getType() & $TypeMasks::PlayerObjectType) returns whatever $TypeMasks::PlayerObjectType is, but that's about all I know.

As a most basic definition, a typemask is a category into which a physical object fits. They are similar to classnames in this way but they are much more flexible.

To get more in depth, typemasks are binary numbers. Looking at them in binary makes them significantly simpler to understand, in my opinion. For example, $Typemasks::PlayerObjectType is 16384 in decimal. In binary, this number is 100000000000000. $Typemasks::VehicleObjectType is 65536, 10000000000000000 in binary.

You begin to have some insight upon examining the two brick typemasks that are defined by default, $Typemasks::FxBrickAlwaysObjectType and $Typemasks::FxBrickObjectType. These are 67108864 and 33554432, respectively. In binary, one of them has one more zero than the other. However, getting the type off of a raycasting brick returns 100663296 - neither of these! Why is that? Let's look at the three in binary:

100000000000000000000000000 $Typemasks::FxBrickAlwaysObjectType
010000000000000000000000000 $Typemasks::FxBrickObjectType
110000000000000000000000000 Typemask of a raycasting brick

What this means is that a raycasting brick is both of these typemasks. If you disable raycasting on the brick and check its type, it will 67108864 (FxBrickAlwaysObjectType).

This is one flexibility of typemasks that classnames do not have - an object can belong to multiple typemasks. Checking if an object belongs to any of several typemasks is also easier than doing the same with classnames. The typemasks are "stacked" (I have no idea what any of the binary operations are called, I'm sure they have formal names) with a single | character. You can create the typemask a raycasting brick returns with this:

%mask = $Typemasks::FxBrickAlwaysObjectType | $Typemasks::FxBrickObjectType;

And as you know two binary numbers are "compared" with a single & character. If any bit of the number preceding the & is 1 and matches a 1 in the succeeding number, the operation will return true, else it is false.

I'm sorry if this was a substantially more thorough examination than you were looking for, I just wanted to be thorough and I find the way typemasks work to be very interesting.


Now we just need collision masks. :)

The operators are called bitwise operators, as they operate on the individual bits.

| is a bitwise or (1 if either bit is 1, 0 otherwise)
& is a bitwise and (1 if both bits are 1, otherwise 0)
^ is a bitwise xor (1 if one bit is 1 and the other is 0, 0 if both bits are the same)

Then there are a few others...

~ is a bitwise not. It only uses one number, and it turns 0 into 1, and 1 into 0. You use it like a minus sign for making negative numbers, used like ~42

>> is a bitwise right shift. It's basically the same as dividing by two a certain number of times. It's used like 47650 >> 3 (this is the same as dividing by 2 to the power of 3)

<< is a bitwise left shift, the opposite of the right shift.


Wikipedia has an article here.

I'm sorry if this was a substantially more thorough examination than you were looking for, I just wanted to be thorough and I find the way typemasks work to be very interesting.
Thanks, that's exactly what I wanted!

Thanks! So ^ pretty much serves the same purpose as != ?

Oh yeah, these are server-sided, correct?

Thanks! So ^ pretty much serves the same purpose as != ?
For booleans, yes. It will work for other numbers as well if you used in an if check, but it will still not be a value.
Oh yeah, these are server-sided, correct?
No. These are numerical values, so they can be defined anywhere. However, the $TypeMasks::* values are also available on the client-side, and you can use getType on any object in the ServerConnection. I think you sort of understand what's going on, but I'll explain it a bit more. Everyone is talking about bitwise operators which is correct, but I'll go into a little bit more about bitmasks (which is what these are) and how and why bitwise operations make bitmasks work.

Already mentioned: Bitwise operators (&, |, ^, etc - not to be confused with && and ||, the conditional versions) allow you to manipulate single bit values inside a numerical value. But the reason this works all comes down to the shifting operators being combined with the other bitwise operators


Importantly: a bit can have 2 values, a 0 or 1. This is the binary system. Let's start with the simplest numerical value: 1. But to explain this, I'm going to say it is:

Code: [Select]
n = 00000000001

Where n is the binary value we're going to work with. It's the same value as decimal 1 and binary 1 and doesn't need the extra zeroes, I just need the zeroes to explain how this works.

What the bitwise shift operators do is shift that 1 bit that is on (on being has a value of 1, instead of 0 which is off) left or right. The << operator is left, the >> operator is right. For example, n << 2  is shifting our value of n two units to the left:

Code: [Select]
n =          00000000001 (= 1)
                           <<
n << 2 =  00000000100 (= 4)

As you can see, its shifted that one bit two places to the left. The value of n << 2 is now 4 in the decimal system, and 00000000100 in the binary system. Shifting right is not really important for bitmasks, so we'll ignore that.

Now, the bitwise and operator check compares two numerical values (in binary) and any bits that are the same are turned on whereas any bits that are not the same are turned off. ie, if any bits in the same binary place are both on, keep the final value on otherwise turn it off for that single place. Here's an example of what I mean:

Code: [Select]
let x =     00000100101 (= 37)
let y =     00000101001 (= 41)
               _____v____v
x & y =    00000100001 (= 33)
(the key: v means it was turned on, the underscore means it was turned off)

As you can (hopefully) see, it has gone through each place and turned on any bits that are the same between the two binary numbers and turned off the rest.

The bitwise or operator is similar. It goes through each bit, and if both values are off, it turns the bit off. If either value is on, then it turns the bit on. Pretty simple, here's an example:

Code: [Select]
let x =     00000100101 (= 37)
let y =     00000101001 (= 41)
               _____v_vv_v
x | y =     00000101101 (= 45)
(the key: v means the bit was turned on, _ means it was turned off)

For each place of bits in x and y, if either bit was turned on then that bit was turned on otherwise it was turned off.

The other operators are also useful, but you can look into those. Now finally, I can explain what those explanations were building up to. The way bitmasks work:

First, each option you would like to accept needs a constant value, which is actually a shifted value. For example, all of the $TypeMasks::* values are bitwise-shifted values (to the left) of 1. From what we've gone over, you should know that each consecutive time you shift a value to the left, the bit turned on is being carried over to the next bit place towards the left. So when you are defining the constant values that the user uses for the options, they are consecutive shifts of the binary value 1. These are not the actual values, but here's an example of how typemasks would be defined:

Code: [Select]
$TypeMasks::Brick = 1; //(= 00001)
$TypeMasks::Terrain = 1 << 2; //(= 00010)
$TypeMasks::Player = 1 << 3; //(= 00100)
$TypeMasks::Interior = 1 << 4; //(= 01000)
$TypeMasks::StaticShape = 1 << 5; //(= 10000)

You'll notice that each constant has a bit in a different place, because each one is consecutively shifted. This is the magic of how bitmasks work. Using the bitwise or operator, we can combine two constants together without the need of an extra argument, while still retaining both the constants in the value. We do this using the bitwise or operation.

You will remember that the bitwise or operator goes through each bit of the two values, and if neither of the values  are turned on at that bit place, the new value of the bit in the place is off, otherwise it is on. This can be used with the bitwise-shifted operations to combine the two constants into one value. Example, if were to combine the brick typemask with the player typemask:

Code: [Select]
$TypeMasks::Brick =                      00001 (= 1)
$TypeMasks::Player =                     00100 (= 8)
                                         __v_v
$TypeMasks::Brick | $TypeMasks::Player = 00101 (= 5)

The two constants are now combined into one binary value. But you're thinking, the hell can I do with this value? How do I check for each seperate value? That's where the bitwise and operation comes into play. Because the and operator checks each bit and makes sure they're both turned on, it can be used to single out only one constant value's bits inside a combined constant value. Probably hard to explain, so here's an example:

Code: [Select]
$TypeMasks::Brick | $TypeMasks::Player = 00101 (= 5)

let x = 00101 (from above)

$TypeMasks::Brick && 00101 =             00001 (= 1)

As you can see, it has got rid of any stuff left over from combining it with $TypeMasks::Player. If a certain constant value is inside something you've combined with another constant value and you use bitwise and operator with that certain constant, the value will be 0 if it is not inside it, or a numeric value other than 0 if it is inside it. This can be combined with an if check, so that you can test if a certain constant has been switched on in a value. I know I'm probably not making much sense, so I'll just bring it back to the basics, and what you asked originally:

Code: [Select]
if(%player.getType() & $TypeMasks::PlayerObjectType)
echo("Yes, the value of %player.getType() has been bitwise or'd with $TypeMasks::PlayerObjectType. It could also be exactly equal to the the typemask as well.");
else
echo("No, it was not combined with that typemask, so the bitwise and has come out as zero, hence the conditional failed.");

There's many reasons to use typemasks. Firstly, it can be used to reduce the number of arguments that you need to pass. For example:

Code: [Select]
function laugh(%loudly,%snort,%dieafterwards,%stamponthecat,%wishtherewasmoreargumentsonthisfunction,%throwthetableover)
{
if(%loudly)
{
//do this
}
if(%snort)
{
//do this too
}
if(%dieafterwards)
{
//do it omg
}
if(%stamponthecat)
{
//no way
}
if(%wishtherewasmoreargumentsonthisfunction)
{
//keep trying
}
if(%throwthetableover)
{
//(ノಠ益ಠ)ノ彡┻━┻
}
}

laugh(true,true,false,true,false,true);
If you have code like this, usually with a lot of arguments, it can get painfull to remember their order and importance. If you use bitmasks instead:

Code: [Select]
$LOUDLY = 1;
$SNORT = 1 << 2;
$DIEAFTERWARDS = 1 << 3;
$STAMPONTHECAT = 1 << 4;
$WISHTHEREWASMOREARGUMENTSONTHISFUNCTION = 1 << 5;
$THROWTHETABLEOVER = 1 << 6;

function laugh(%bitmask)
{
if(%bitmask & $LOUDLY)
{
//do this
}
if(%bitmask & $SNORT)
{
//do this too
}
if(%bitmask & $DIEAFTERWARDS)
{
//do it omg
}
if(%bitmask & $STAMPONTHECAT)
{
//no way
}
if(%bitmask & $WISHTHEREWASMOREARGUMENTSONTHISFUNCTION)
{
//keep trying
}
if(%bitmask & $THROWTHETABLEOVER)
{
//(ノಠ益ಠ)ノ彡┻━┻
}
}

laugh($LOUDLY | $SNORT | $STAMPONTHECAT | $THROWTHETABLEOVER);

Then you only need one argument, and it becomes much more readable. The reason Torque uses it for types is so that they can have combinations of types. Say you had a brick that was also a static shape, the typemask would be $TypeMasks::Brick | $TypeMasks::StaticShape and when you bitwise and the combined typemask with each seperate typemask, it will be true for both.

It's also used for raycasts, as you combine each typemask together and pass it to the function (eg. $TypeMasks::fxBrickObject | $TypeMasks::PlayerObjectType, saying I want it collide with a player or brick) and then the raycast check itself does a bitwise and on each object it find's type with the value we passed to it, and if the and returns true then it has collided with a type that we passed.

tl;dr - I suck at explaining stuff, so try this: http://en.wikipedia.org/wiki/Mask_(computing)
That's where I learnt bitmasks, there was another good one but I lost it. Sorry for any mistakes and the stuff diagrams.
« Last Edit: December 07, 2011, 03:57:14 AM by Destiny/Zack0Wack0 »

Thanks, didn't know I could make my own bitmasks, that might be useful.