Archive for the ‘DNA’ tag
BlendReader Class
A prototype quality BlendReader class has been integrated into LxEngine. The interface is small and simple. Part of the core vision for LxEngine is excellent usability for the development team during development. This means removing steps from the development process that technically can be automated. Direct support for the Blender file format is an exciting addition in this regard. No more export step: simply save the updated file and run the application.
I call the code ‘prototype quality’ at this stage because I know it won’t work correctly on .blend files from 32-bit systems or from big endian systems. Both these issues will be trivial to solve, but have not yet been addressed. There’s also likely a bit of room for optimization, but for the most part, I doubt that matters for anything but massive scenes (in which case, it’ll likely be better not to be loading a .blend file directly, but rather some leaner format).
If nothing else, the .blend file format is fairly interesting in itself.
Usage
The BlendReader interface is trivial to use:
- Create the BlendReader object
- Call reader.open(std::string filename)
- This call opens the file, reads the blend file’s “DNA” structure and build indices so objects can be read out of the file easily
- Call reader.getBlocksByType(std::string type)
- This returns a list of the info about the blocks in the file of the given type. For example, “Mesh” or “Scene”.
- Call reader.readObject(address)
- This takes the address of a block and reads it in as a typed object. It’s not a C++ type, but rather a wrapper on the block that lets the user grab the fields by name without any error-prone pointer arithmetic.
- Call obj.field<type>(name, index)
- Reads a particular named field out of the object and casts it to the given type. Of course the caller needs to get the type right – but this is inevitable as at some point the opaque chunk of binary data needs to be cast into native C++ types
Example Code
Here’s the chunk of prototype code that loads the blend files into an LxEngine document (using the LxEngine Mesh structure):
float normalizeShort (short s) { return float (s) / float(std::numeric_limits<short>::max()); } Mesh* load_blend (std::string filename) { BlendReader reader; if ( reader.open(filename) ) { Mesh* pMesh = new Mesh; auto meshBlocks = reader.getBlocksByType("Mesh"); if (meshBlocks.size() != 1) { lx_warn("More than one mesh found in .blend file. Processing only the " "first one that is found."); } auto spObj = reader.readObject(meshBlocks[0]->address); auto numVerts = spObj->field<int>("totvert"); auto numFaces = spObj->field<int>("totface"); pMesh->mVertices.reserve(numVerts); pMesh->mFaces.reserve(numFaces); pMesh->mFlags.mVertexNormals = true; auto spVerts = reader.readObject(spObj->field<unsigned __int64>("mvert")); for (int i = 0; i < numVerts; ++i) { Mesh::Vertex v; v.position = spVerts->field<point3>("co", 0); // Normals are encoded as shorts v.normal.x = normalizeShort( spVerts->field<short>("no", 0) ); v.normal.y = normalizeShort( spVerts->field<short>("no", 1) ); v.normal.z = normalizeShort( spVerts->field<short>("no", 2) ); pMesh->mVertices.push_back(v); spVerts->next(); } spVerts.reset(); auto spFaces = reader.readObject(spObj->field<unsigned __int64>("mface")); for (int i = 0; i < numFaces; ++i) { Mesh::Quad q; q.index[0] = spFaces->field<int>("v1"); q.index[1] = spFaces->field<int>("v2"); q.index[2] = spFaces->field<int>("v3"); q.index[3] = spFaces->field<int>("v4"); pMesh->mFaces.push_back(q); spFaces->next(); } spFaces.reset(); return pMesh; } else { lx_error("Could not open file '%s'", filename.c_str()); return nullptr; } }
Full Source Code
The latest code (assuming future submissions haven’t moved it) is available here on github.
And some related links on the .blend file format:
