LordStark
LordStark

Reputation: 9

How do I read Blender's default cube?

I'm learning DirectX 11 and I've got very confused after looking at Blender's default cube, I've exported it in the Wavefront OBJ format, and this is the file I've got:

# Blender v2.92.0 OBJ File: ''
# www.blender.org
mtllib untitled.mtl
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.625000 0.500000
vt 0.875000 0.500000
vt 0.875000 0.750000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.125000 0.500000
vt 0.375000 0.500000
vt 0.125000 0.750000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
usemtl Material
s off
f 1/1/1 5/2/1 7/3/1 3/4/1
f 4/5/2 3/4/2 7/6/2 8/7/2
f 8/8/3 7/9/3 5/10/3 6/11/3
f 6/12/4 2/13/4 4/5/4 8/14/4
f 2/13/5 1/1/5 3/4/5 4/5/5
f 6/11/6 5/10/6 1/1/6 2/13/6

I know the secret for it to render is use the provided indices, but I'm not sure how I should implement the buffer for this data, if the vertex, uv and normals were in the same amount I could organize them in a array of structures, but if they are in a different number I don't know how could I do this and create a buffer.

Upvotes: 0

Views: 188

Answers (1)

Chuck Walbourn
Chuck Walbourn

Reputation: 41057

The Wavefront OBJ format is very old-school, but it's been around a long time so it's well understood and fairly easy to parse. That said, 3D renderer packages like Blender don't have to store their data in the same formats that you typically use for runtime rendering, so expect to do some work to change the Wavefront OBJ method of individual indices for each kind of data into the Vertex format for single-stream rendering.

Another challenge is that Wavefront OBJ stores faces as 'n-gons', and you need to convert it to triangles to render with Direct3D 11. You can also run into relative indices (i.e. negative numbers) for the different arrays. And then there's dealing with the mtl file.

An example of doing all the text file parsing and conversion in C++ can be found on GitHub: WavefrontReader.h which is documented here.

WaveFrontReader<uint16_t> wfReader;
Microsoft::WRL::ComPtr<ID3D11Buffer> vertexBuffer;
Microsoft::WRL::ComPtr<ID3D11Buffer> indexBuffer;

HRESULT hr = wfReader.Load(L"cube.obj");
if (FAILED(hr))
    // Error

D3D11_BUFFER_DESC bufferDesc = {};
bufferDesc.ByteWidth = static_cast<UINT>(wfReader.vertices.size() * sizeof(WaveFrontReader::Vertex));
bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bufferDesc.Usage = D3D11_USAGE_DEFAULT;

D3D11_SUBRESOURCE_DATA initData = { wfReader.vertices.data(), 0, 0 };

hr = device->CreateBuffer(&bufferDesc, &initData, &vertexBuffer);
if (FAILED(hr))
    // Error

bufferDesc.ByteWidth = static_cast<UINT>(wfReader.indices.size() * sizeof(uint16_t));
bufferDesc.bindFlags = D3D11_BIND_INDEX_BUFFER;
initData.pSysMem = wfReader.indices.data();
hr = device->CreateBuffer(&bufferDesc, &initData, &indexBuffer);
if (FAILED(hr))
    // Error
UINT vertexStride = sizeof(WaveFrontReader::Vertex);
uint vertexOffset = 0;
deviceContext->IASetVertexBuffers(0, 1, vertexBuffer.GetAddressOf(), &vertexStride, &vertexOffset);

deviceContext->IASetIndexBuffer(indexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);

A common strategy--at least games--is to do all that parsing offline and write out a nice, GPU-friendly runtime file format that you can just read into memory and render with. For DirectX, there a number of 'sample' formats like VBO, SDKMESH, or Visual Studio's CMO. Using these tools typically makes reading the Vertex & Index buffer at runtime trivial. See DirectXMesh's meshconvert, the DirectX SDK Samples Content Exporter, and the Visual Studio Content pipeline. Code for loading and rendering all these formats can be found in the DirectX Tool Kit.

Of course, for a simple common shape like a cube, you can just create it in memory directly rather than having to go through all the mess of parsing a model in the first place. That's what GeometricPrimitive in the DirectX Tool Kit does.

Note: Even if you went with multi-stream rendering you'd still have to duplicate the data because all streams have to use the same index from the IB for each vertex. Since you are already doing some data rearranging on the CPU, you might as well create a single-stream packed Vertex which is more efficient than have 2 or 3 streams.

Upvotes: 1

Related Questions