Skip to content Skip to sidebar Skip to footer

How To Read, Parse The Sbm File Format From Superbible Opengl

Calling on experts, gurus, and anybody to help read and parse a file in python. On page 751 of 6th ed. or page 800 of 7th ed. of Superbible OpenGL there is Appendix B. The SBM File

Solution 1:

Since you are already using NumPy, i'll give yuo an answer which uses NumPy to read the binary file. NumPy has some had functionality to reinterpret buffer data by different data type and that is exactly what is needed to interpret a binary file.

A binary file can be read toe a byte array by numpy.fromfile.e.g.:

data = numpy.fromfile(filename, dtype=np.byte)

Some bytes of the array can be interpreted as unsigned integer (unit32) by numpy.frombuffer. e.g.:

classSB6M_HEADER:
    def__init__(self, data):
        int_data = np.frombuffer(np.array(data[:16], dtype=np.byte), dtype=np.uint32)
        self.magic, self.size, self.num_chunks, self.flags = int_data 
        print(self.magic, self.size, self.num_chunks, self.flags)

Porting the source code from sb6mfile.h respectively sb7object.cpp to python and reading and "*.sbm" file:

defSB6M_FOURCC(a,b,c,d):
    return ( (ord(a) << 0) | (ord(b) << 8) | (ord(c) << 16) | (ord(d) << 24) )

SB6M_MAGIC = SB6M_FOURCC('S','B','6','M')

SB6M_CHUNK_TYPE_INDEX_DATA      = SB6M_FOURCC('I','N','D','X')
SB6M_CHUNK_TYPE_VERTEX_DATA     = SB6M_FOURCC('V','R','T','X')
SB6M_CHUNK_TYPE_VERTEX_ATTRIBS  = SB6M_FOURCC('A','T','R','B')
SB6M_CHUNK_TYPE_SUB_OBJECT_LIST = SB6M_FOURCC('O','L','S','T')
SB6M_CHUNK_TYPE_COMMENT         = SB6M_FOURCC('C','M','N','T')
SB6M_CHUNK_TYPE_DATA            = SB6M_FOURCC('D','A','T','A')

classSB6M_HEADER:
    def__init__(self, data):
        int_data = np.frombuffer(np.array(data[:16], dtype=np.byte), dtype=np.uint32)
        self.magic, self.size, self.num_chunks, self.flags = int_data 
        print(self.magic, self.size, self.num_chunks, self.flags)

classSB6M_CHUNK_HEADER:
    def__init__(self, data, offset):
        int_data = np.frombuffer(np.array(data[offset:offset+8], dtype=np.byte), dtype=np.uint32)
        self.type, self.size = int_data

classSB6M_CHUNK_INDEX_DATA(SB6M_CHUNK_HEADER):
     def__init__(self, data, offset):
        super().__init__(data, offset)
        int_data = np.frombuffer(np.array(data[offset+8:offset+20], dtype=np.byte), dtype=np.uint32)
        self.index_type, self.index_count, self.index_data_offset = int_data

classSB6M_CHUNK_VERTEX_DATA(SB6M_CHUNK_HEADER):
     def__init__(self, data, offset):
        super().__init__(data, offset)
        int_data = np.frombuffer(np.array(data[offset+8:offset+20], dtype=np.byte), dtype=np.uint32)
        self.data_size, self.data_offset, self.total_vertices = int_data

classSB6M_CHUNK_VERTEX_DATA(SB6M_CHUNK_HEADER):
     def__init__(self, data, offset):
        super().__init__(data, offset)
        int_data = np.frombuffer(np.array(data[offset+8:offset+20], dtype=np.byte), dtype=np.uint32)
        self.data_size, self.data_offset, self.total_vertices = int_data

SB6M_VERTEX_ATTRIB_FLAG_NORMALIZED = 0x00000001
SB6M_VERTEX_ATTRIB_FLAG_INTEGER    = 0x00000002classSB6M_VERTEX_ATTRIB_DECL:
    def__init__(self, data, offset):
        self.name = ''.join([chr(n) for n in data[offset:offset+64] if n > 30])
        int_data = np.frombuffer(np.array(data[offset+64:offset+84], dtype=np.byte), dtype=np.uint32)
        self.size, self.type, self.stride, self.flags, self.data_offset = int_data

classSB6M_VERTEX_ATTRIB_CHUNK(SB6M_CHUNK_HEADER):
    def__init__(self, data, offset):
        super().__init__(data, offset)
        int_data = np.frombuffer(np.array(data[offset+8:offset+12], dtype=np.byte), dtype=np.uint32)
        self.attrib_count = int_data[0]
        self.attrib_data = []
        for i inrange(self.attrib_count):
            self.attrib_data.append(SB6M_VERTEX_ATTRIB_DECL(data, offset+12+i*84))

classSB6M_DATA_CHUNK(SB6M_CHUNK_HEADER):
    def__init__(self, data, offset):
        super().__init__(data, offset)
        int_data = np.frombuffer(np.array(data[offset+8:offset+20], dtype=np.byte), dtype=np.uint32)
        self.encoding, self.data_offset, self.data_length = int_data

classSB6M_SUB_OBJECT_DECL:
    def__init__(self, data, offset):
        int_data = np.frombuffer(np.array(data[offset:offset+8], dtype=np.byte), dtype=np.uint32)
        self.first, self.count = int_data

classSB6M_CHUNK_SUB_OBJECT_LIST(SB6M_CHUNK_HEADER):
    def__init__(self, data, offset):
        super().__init__(data, offset)
        int_data = np.frombuffer(np.array(data[offset+8:offset+12], dtype=np.byte), dtype=np.uint32)
        self.count = int_data[0]
        self.sub_object = []
        for i inrange(self.count):
            self.sub_object.append(SB6M_SUB_OBJECT_DECL(data, offset+12+i*8))
defload(filename):

    vertex_attrib_chunk = None
    vertex_data_chunk = None
    index_data_chunk = None
    sub_object_chunk = None
    data_chunk = Nonetry:
        data = numpy.fromfile(filename, dtype=np.byte)
        filesize = data.size

        header = SB6M_HEADER(data)
        offset = header.size

        for i inrange(header.num_chunks):

            chunk = SB6M_CHUNK_HEADER(data, offset)
            if chunk.type == SB6M_CHUNK_TYPE_VERTEX_ATTRIBS:
                vertex_attrib_chunk = SB6M_VERTEX_ATTRIB_CHUNK(data, offset) 
            elif chunk.type == SB6M_CHUNK_TYPE_VERTEX_DATA:
                vertex_data_chunk = SB6M_CHUNK_VERTEX_DATA(data, offset)
            elif chunk.type == SB6M_CHUNK_TYPE_INDEX_DATA:
                index_data_chunk = SB6M_CHUNK_INDEX_DATA(data, offset) 
            elif chunk.type == SB6M_CHUNK_TYPE_SUB_OBJECT_LIST:
                sub_object_chunk = SB6M_CHUNK_SUB_OBJECT_LIST(data, offset)
            elif chunk.type == SB6M_CHUNK_TYPE_DATA:
                data_chunk = SB6M_DATA_CHUNK(data, offset) 
            else:
                raise

            offset += chunk.size

    except:
        print("error reading file {}".format(filename))

Finally the floating point vertex data can be read:

if vertex_data_chunk and vertex_attrib_chunk:
    start = vertex_data_chunk.data_offset
    end = start + vertex_data_chunk.data_size
    vertex_data = np.frombuffer(np.array(data[start:end], dtype=np.byte), dtype=np.float)

    data_buffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, data_buffer)
    glBufferData(GL_ARRAY_BUFFER, vertex_data, GL_STATIC_DRAW)

    vertexcount = vertex_data_chunk.total_vertices
    vao = glGenVertexArrays(1)
    glBindVertexArray(self.vao)

    for attrib_i, attrib inenumerate(vertex_attrib_chunk.attrib_data):
        if attrib.name=='position'or attrib.name=='map1':  
            glVertexAttribPointer(attrib_i,
                attrib.size, attrib.type,
                GL_TRUE if (attrib.flags & SB6M_VERTEX_ATTRIB_FLAG_NORMALIZED) != 0else GL_FALSE,
                attrib.stride, ctypes.c_void_p(int(attrib.data_offset)))
            glEnableVertexAttribArray(attrib_i)

Finally draw the mesh:

glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES, 0, vertexcount)

Solution 2:

The next step has to be similar to what happens in the C code:

SB6M_HEADER * header = (SB6M_HEADER *)ptr;
ptr += header->size;

You need to advance the pointer by a known size.

You have that attribute in your header class. Do you set it correctly?

But I would suggest a different approach: do not use raw Pyhon for this at all.

Instead create a wrapper using the original C code found in sb6mfile.h

and a part of the function to read the file found in sb7object.cpp

Then access this code using Python's CFFI. You pass the C header with the SBM types to the files and can use them in Python.

Once this works you can get the data back and put it in a numpy array. Usually this is a bit of work but once it works it is really robust.

Here are some links to get you started:

buffer_size = np_arr.size*np_arr.dtype.itemsize
c_buffer = ffi.buffer(cffi_arr,buffer_size)
np_arr2 = np.frombuffer(c_buffer, dtype=np_arr.dtype)

(https://ammous88.wordpress.com/2014/12/30/numpy-array-with-cffi-c-function/)

Post a Comment for "How To Read, Parse The Sbm File Format From Superbible Opengl"