| Tips
                                        And Tricks: Binary ManipulationAuthor:
                                        Jack Hoxley
 Written: 4th
                                        May 2001
 Contact: [EMail]
 
 Contents
                                        of This lesson:1. Introduction
 2. Bit fields
 3. Flag combining
 
 1.
                                        Introduction This
                                        tutorial isn't greatly important in the
                                        grand scheme of things, but I wanted to
                                        cover a couple of useful topics that may
                                        well help you out at some point. In
                                        general VB shields us from the low level
                                        binary manipulation (binary is the
                                        010100111010001 stuff!), but there are
                                        times that we may want access to them,
                                        for example, when directly accessing
                                        memory in DirectX we are often required
                                        to directly manipulate the raw data -
                                        where 3 values are stored in 1 (and we
                                        need to get them out)... 
 2.
                                        Bit Fields You
                                        should be familiar with the 'Boolean'
                                        datatype - often fundamental to the
                                        operation of many programs, particularly
                                        games. Well, there's one slight problem
                                        with them - they're a bit slow, and they
                                        take up a lot of space. Each 'Boolean'
                                        datatype is a 16 bit (2 byte) signed
                                        number - normally, using an integer of
                                        the same size you have 65536 possible
                                        values, a Boolean of the same space will
                                        only store true/false. Partly because of
                                        this, conversion must take place - you
                                        assign 1645 to a Boolean value and it'll
                                        come back as -1 (true), so there must be
                                        something in there that's converting
                                        them... which will also slow things
                                        down. Bit
                                        fields are a clever way of reducing the
                                        size that these Booleans take up. One
                                        possible (and often used) method of
                                        collision detection is to store a
                                        true/false value for every
                                        tile/pixel/area in a game world -
                                        100x100 grid = 10000 values, if stored
                                        as a Boolean you're already using up
                                        20,000 bytes of memory (19.5kb),
                                        whereas, if you were clever, you could
                                        fit those 10,000 values into only 1250
                                        bytes (1.2kb) - which is considerably
                                        smaller than the original - expand the
                                        maths a little further, take a 640x480
                                        image, each pixel has a passable
                                        true/false collision flag - that's
                                        307,200 values - stored as booleans
                                        it'll be 600kb, stored in my clever
                                        little method it'll cost you 37.5kb... So what
                                        is my clever little method then? well,
                                        we're going to be storing each boolean
                                        flag as a single bit (binary digit), a
                                        binary digit is 1 or 0 (on or off),
                                        effectively the same as a boolean - true
                                        or false, on or off. So it makes sense
                                        to use the smallest possible unit. You
                                        may now be thinking that there isn't a
                                        bit datatype (or anything similiar),
                                        thats because there isn't. We're
                                        going to use the "Byte"
                                        datatype, it is the smallest possible
                                        value, and is unsigned (which means it's
                                        never a negative number); as it's a byte
                                        it's made up of 8 bits, therefore we can
                                        store 8 on/of or true/false values in
                                        each byte - if we then make an array of
                                        these bytes we can store many thousands
                                        very easily - the smaller values that I
                                        demonstrated above were taken by
                                        dividing the total number of values by
                                        8, 8 of them go into a byte, so the
                                        final number is the total number of
                                        bytes... The
                                        following piece of code is all that is
                                        required to make a simple bit field,
                                        calling SetBool( ) will change the
                                        current value at that point, and GetBool(
                                        ) will return the current value at that
                                        point - all you really need. 
                                          
                                          
                                            
                                              
                                                | 
                                                    
                                                      
                                                        | 
                                                            
                                                              
                                                                | 
                                                                    
                                                                      
                                                                        | Private BitField(0 To 99) As Byte '100 bytes = 800 boolean values
Private Function GetBool(N As Long) As Boolean
    If (BitField(N \ 8) And 2 ^ (N - ((N \ 8) * 8))) = 2 ^ (N - ((N \ 8) * 8)) Then GetBool = True
End Function
Private Sub SetBool(N As Long, Val As Boolean)
    If Val = True Then
        BitField(N \ 8) = BitField(N \ 8) Or 2 ^ (N - ((N \ 8) * 8))
    Else
        BitField(N \ 8) = BitField(N \ 8) Xor 2 ^ (N - ((N \ 8) * 8))
    End If
End Sub |  |  |  |  Looks kind
                                        of scary doesn't it! Lets have a look at
                                        all of this. A byte datatype has a
                                        minimum value of 0, and a maximum value
                                        of 255, described by 8 bits (when all of
                                        them are 0, the result is 0, when all of
                                        them are 1, the result is 255). Each bit
                                        represents a 2^n number, where n is the
                                        bit (0 to 7); to calculate the binary
                                        number 01101101 we just add up all of
                                        the 2^n values for the bits which are
                                        '1', the bit number goes from right to
                                        left, ie, 76543210, so in the previous
                                        number, we have 2^0 + 2^2 + 2^3 + 2^5 +
                                        2^6 = 1 + 4 + 8 + 32 + 64 = 109. Now,
                                        the next part are the logical bitwise
                                        operators (And, Or, Xor) - these all
                                        perform a comparison between two bits
                                        (or when used with numbers, all of the
                                        bits individually), take the following
                                        truth tables:
 AND: 
                                          
                                            | Input
                                              1 | Input
                                              2 | Output |  
                                            | 0 | 0 | 0 |  
                                            | 0 | 1 | 0 |  
                                            | 1 | 0 | 0 |  
                                            | 1 | 1 | 1 |  OR: 
                                          
                                            | Input
                                              1 | Input
                                              2 | Output |  
                                            | 0 | 0 | 0 |  
                                            | 0 | 1 | 1 |  
                                            | 1 | 0 | 1 |  
                                            | 1 | 1 | 1 |  XOR: 
                                          
                                            | Input
                                              1 | Input
                                              2 | Output |  
                                            | 0 | 0 | 0 |  
                                            | 0 | 1 | 1 |  
                                            | 1 | 0 | 1 |  
                                            | 1 | 1 | 0 |  We
                                        could now do a quick sum using these,
                                        which is the basis of our code. Notice
                                        that when we are using GetBool it uses
                                        the logical AND operator, well this
                                        basically says that, if the source and
                                        the destination have the same bits, then
                                        the output will have the same as the
                                        destination. Or, for a better example: 0111101100100000
 -------- AND
 00100000
 What
                                        a surprise, the result is the same as
                                        the second input! so, in VB code we
                                        could say:If (X And Y) = Y then The_Bit_Is_True
 which, if you look up to the top of the
                                        code again, is exactly what I've done.
                                        All the brackets and maths symbols are
                                        just so that we can find which byte to
                                        read from, and which bit within that
                                        byte we are checking.
 The
                                        Same goes for the Or'ing and the XOR'ing....
                                        The OR is used to set a bit, which, by
                                        looking at the truth table above should
                                        be fairly obvious. XOR'ing is used to
                                        turn a bit off, which you can see from
                                        the truth table... One
                                        final thing to do, should you want to
                                        use this method for a 2D array you're
                                        going to need a way of converting an X,Y
                                        coordinate into a single N value. This
                                        can be quite easily done using X+(Y*Width),
                                        where the array would be a 100x100 array
                                        (of 0-99 scale) the formula works out as
                                        99+(99*100) = 9999, the last element in
                                        a (100x100)-1 sized array... Just
                                        remember to make sure that the bit field
                                        array is large enough... 
 3.
                                        Flag Combining Even
                                        if your new to the DirectX world you'll
                                        very quickly see that it uses a lot of
                                        functions where you can specify lots of
                                        options in one parameter, for example,
                                        in Direct3D8, to create a Flexible
                                        Vertex Format you use notation like: 
                                          
                                          
                                            
                                              
                                                | 
                                                    
                                                      
                                                        | 
                                                            
                                                              
                                                                | 
                                                                    
                                                                      
                                                                        | Const
                                                                          FVF =
                                                                          (D3DFVF_XYZ
                                                                          or
                                                                          D3DFVF_DIFFUSE
                                                                          or
                                                                          D3DFVF_TEX1) Const
                                                                          FVF2 =
                                                                          (D3DFVF_XYZ
                                                                          or
                                                                          D3DFVF_NORMAL
                                                                          or
                                                                          D3DFVF_TEX2
 |  |  |  |  Both are
                                        stored in a 32bit integer (Long), yet
                                        there are many many different
                                        combinations that can be used. Whilst it
                                        isn't greatly important to understand
                                        how this works, it may be useful if you
                                        come to create your own game engine, or
                                        AI state engine etc... Where you want to
                                        have a number of possibilities, but
                                        don't want to hardcode them all in as
                                        separate functions.
 Take
                                        the following example for a generic
                                        graphics engine initialisation: 
                                          
                                          
                                            
                                              
                                                | 
                                                    
                                                      
                                                        | 
                                                            
                                                              
                                                                | 
                                                                    
                                                                      
                                                                        | InitGraphics(bUseFullscreen,
                                                                          iModelDetail,
                                                                          iTextureDetail, 0, 0, 0, 0, 0,
                                                                          bSpecialFX) |  |  |  |  Not too
                                        pretty really is it... lots of 0's
                                        indicating that we don't want to specify
                                        that parameter etc...
 
 
                                          
                                          
                                            
                                              
                                                | 
                                                    
                                                      
                                                        | 
                                                            
                                                              
                                                                | 
                                                                    
                                                                      
                                                                        | InitGraphics GROPT_STARTFULLSCREEN Or _
                  GROPT_USEHIDETAILMODELS Or _
                  GROPT_USEHIGHTDETAILTEXTURES Or _
                  GROPT_USESPECIALEFFECTS |  |  |  |  It may
                                        look like more work, but using VB's
                                        intellisense dropdown parameter lists,
                                        it's as easy as selecting them from a
                                        list (really!). And the best part is, we
                                        don't have to specify any parameters
                                        that we don't want to. This method may
                                        not suit your engine perfectly, but it
                                        can be extended in various different
                                        ways...
 To
                                        implement the second type of function
                                        calling, we need this code: 
                                          
                                          
                                            
                                              
                                                | 
                                                    
                                                      
                                                        | 
                                                            
                                                              
                                                                | 
                                                                    
                                                                      
                                                                        | Private Enum Options
    GROPT_STARTFULLSCREEN = 2 ^ 0
    GROPT_STARTWINDOWED = 2 ^ 1
    GROPT_USEHWTNL = 2 ^ 2
    GROPT_USEPUREDEV = 2 ^ 3
    GROPT_USELOWDETAILMODELS = 2 ^ 4
    GROPT_USEMEDDETAILMODELS = 2 ^ 5
    GROPT_USEHIDETAILMODELS = 2 ^ 6
    GROPT_USELOWDETAILTEXTURES = 2 ^ 7
    GROPT_USEHIGHTDETAILTEXTURES = 2 ^ 8
    GROPT_USESPECIALEFFECTS = 2 ^ 9
    GROPT_USEDYNAMICLIGHTS = 2 ^ 10
End Enum
Private Function InitGraphics(ParamList As Options) As Boolean
    If ParamList And GROPT_STARTFULLSCREEN = GROPT_STARTFULLSCREEN Then
        'initialise fullscreen mode
    ElseIf ParamList And GROPT_STARTWINDOWED = GROPT_STARTWINDOWED Then
        'initialise in windowed mode
    End If
End Function |  |  |  |  The
                                        enumeration type is used to reference a
                                        bit number to a name, our function can
                                        then split them apart to work out which
                                        values are set; the above example only
                                        does it for the first two enumerations -
                                        but the process is the same for all 10
                                        of them. Note that you can only have up
                                        to 31 enumerations of this type (2^0 to
                                        2^30) due to the maximum value they hold
                                        (32bit long integer).
 Look
                                        back at the previous section about bit
                                        fields, remember that X or Y is used to
                                        set a bit and (X and Y)=Y is used to
                                        test a bit for on/off status; these are
                                        used again here. 
 Hopefully
                                        you've learned something useful here,
                                        nothing particularly ground breaking -
                                        but it's the sort of thing that can be
                                        the perfect solution to that little
                                        problem that you've been working on
                                        recently...
                                       |