Search code examples
c++directx.obj

How to correctly read information from OBJ file and use it to render a model with DirectX 11


I'm trying to render a model in OBJ file format with DirectX. I'm using simple cube model, but I get some really strange results(picture below)

I've tried to render a cube by filling vertecies array manually and it worked pretty good. After studing OBJ file format I've thougth that I should do the similar thing, but I don't understand why this is not working.


Sending two functions and structs descriptions:

  1. initGeometry
    In this function I set up my geometry, shaders and read information from file. I think that I do something wrong at the end of this function, because I didn't change shaders part and initializations part, only added new way to fill up a vertex array
  2. render
    This function didn't changed at all from my first version, where I filled up a vertecies array mannualy.
  3. Structs
    Just some structs descriptions.
HRESULT RenderDevice::initGeometry() {

    ///////READING INFO FROM FILE//////////
    ifstream *inp = new ifstream("test.obj");
    ofstream *out = new ofstream("result.txt");
    char str[256];
    while (!inp->eof()) {
        inp->getline(str, 256);
        meshInfo.coord.push_back(new std::string(str));
    }

    HRESULT hr = S_OK;
    ID3DBlob *pVSBlob = NULL;
    hr = compileShaderFromFile(L"texture.fx", "VS", "vs_4_0", &pVSBlob);
    if (FAILED(hr)) {
        MessageBox(NULL, L"Can't compile Vertex Shader", L"Error", MB_OK);
        return hr;
    }
    hr = g_pd3dDevice->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &g_pVertexShader);
    if (FAILED(hr)) {
        pVSBlob->Release();
        return hr;
    }

    D3D11_INPUT_ELEMENT_DESC layout[] = {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
        {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
    };
    UINT numElemenets = ARRAYSIZE(layout);

    hr = g_pd3dDevice->CreateInputLayout(layout, numElemenets, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout);
    pVSBlob->Release();
    if (FAILED(hr)) {
        return hr;
    }
    g_pImmediateContext->IASetInputLayout(g_pVertexLayout);
    ID3DBlob *pPSBlob = NULL;
    hr = compileShaderFromFile(L"texture.fx", "PS", "ps_4_0", &pPSBlob);
    if (FAILED(hr)) {
        MessageBox(NULL, L"Can't compile Pixel Shader", L"Error", MB_OK);
        return hr;
    }

    hr = g_pd3dDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShader);
    pPSBlob->Release();
    if (FAILED(hr)) {
        return hr;
    }
    pPSBlob = NULL;
    hr = compileShaderFromFile(L"texture.fx", "PSSolid", "ps_4_0", &pPSBlob);
    if (FAILED(hr)) {
        MessageBox(NULL, L"Can't compile Solid Pixel Shader", L"Error", MB_OK);
        return hr;
    }
    hr = g_pd3dDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &g_pPixelShaderSolid);
    pPSBlob->Release();
    if (FAILED(hr)) {
        return hr;
    }

    /////////SPLITING INFO INTO DIFFERENT VECTORS/////////////////
    for (int i = 0; i < meshInfo.coord.size(); i++) {
        if (meshInfo.coord[i]->c_str()[0] == 'v' && meshInfo.coord[i]->c_str()[1] != 'n') {
            float tmpx, tmpy, tmpz;
            sscanf_s(meshInfo.coord[i]->c_str(), "v %f %f %f", &tmpx, &tmpy, &tmpz);
            meshInfo.positions.push_back(XMFLOAT3(tmpx, tmpy, tmpz));
        } else if (meshInfo.coord[i]->c_str()[0] == 'v' && meshInfo.coord[i]->c_str()[1] == 'n') {
            float tmpx, tmpy, tmpz;
            sscanf_s(meshInfo.coord[i]->c_str(), "vn %f %f %f", &tmpx, &tmpy, &tmpz);
            meshInfo.normals.push_back(XMFLOAT3(tmpx, tmpy, tmpz));
        } else if (meshInfo.coord[i]->c_str()[0] == 'f') {
            int iX, iY, iZ, nX, nY, nZ;
            sscanf_s(meshInfo.coord[i]->c_str(), "f %d//%d %d//%d %d//%d", &iX, &nX, &iY, &nY, &iZ, &nZ);
            meshInfo.indexiesPoints.push_back(iX);
            meshInfo.indexiesPoints.push_back(iY);
            meshInfo.indexiesPoints.push_back(iZ);

            meshInfo.indexiesNormals.push_back(nX);
            meshInfo.indexiesNormals.push_back(nY);
            meshInfo.indexiesNormals.push_back(nZ);
        }
    }

    meshInfo.indexiesAmount = meshInfo.indexiesPoints.size();

    meshInfo.vertexAmount = meshInfo.positions.size();
    meshInfo.normalsAmount = meshInfo.normals.size();
    Vertex *vertices = new Vertex[meshInfo.indexiesAmount];

    //////////////FILLING VERTECIES ARRAY///////////////
    for (int i = 0; i < meshInfo.indexiesAmount; i++) {
        vertices[i].normal.x = meshInfo.normals[meshInfo.indexiesNormals[i] - 1].x;
        vertices[i].normal.y = meshInfo.normals[meshInfo.indexiesNormals[i] - 1].y;
        vertices[i].normal.z = meshInfo.normals[meshInfo.indexiesNormals[i] - 1].z;

        vertices[i].pos.x = meshInfo.positions[meshInfo.indexiesPoints[i] - 1].x;
        vertices[i].pos.y = meshInfo.positions[meshInfo.indexiesPoints[i] - 1].y;
        vertices[i].pos.z = meshInfo.positions[meshInfo.indexiesPoints[i] - 1].z;
    } 

    D3D11_BUFFER_DESC bd;
    ZeroMemory(&bd, sizeof(bd));
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(Vertex) * meshInfo.indexiesAmount;
    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;

    D3D11_SUBRESOURCE_DATA initData;
    ZeroMemory(&initData, sizeof(initData));
    initData.pSysMem = vertices;
    hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &g_pVertexBuffer);
    if (FAILED(hr)) {
        return hr;
    }


    ///////////////////FILLING INIXIES ARRAY///////////////////
    WORD *indixies = new WORD[meshInfo.indexiesAmount];
    for (int i = 0; i < meshInfo.indexiesAmount; i++) {
        indixies[i] = meshInfo.indexiesPoints[i];
    }

    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(int) * meshInfo.indexiesAmount;
    bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
    bd.CPUAccessFlags = 0;
    initData.pSysMem = indixies;
    hr = g_pd3dDevice->CreateBuffer(&bd, &initData, &g_pIndexBuffer);
    if (FAILED(hr)) {
        return hr;
    }

    UINT stride = sizeof(Vertex);
    UINT offset = 0;
    g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);

    g_pImmediateContext->IASetIndexBuffer(g_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);

    g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(ConstantBuffer);
    bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    bd.CPUAccessFlags = 0;
    hr = g_pd3dDevice->CreateBuffer(&bd, NULL, &g_pCBMatrixes);
    if (FAILED(hr)) {
        return hr;
    }

    return S_OK;
}
void RenderDevice::render() {
    float clearColor[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
    g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, clearColor);

    g_pImmediateContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
    updateLigth();

    for (int i = 0; i < 6; i++) {
        updateMatrix(MX_SETWORLD, i * (XM_PI * 2) / 6);
        g_pImmediateContext->VSSetShader(g_pVertexShader, NULL, 0);
        g_pImmediateContext->VSSetConstantBuffers(0, 1, &g_pCBMatrixes);
        g_pImmediateContext->VSSetConstantBuffers(1, 1, &g_pCBLigth);
        g_pImmediateContext->PSSetConstantBuffers(0, 1, &g_pCBMatrixes);
        g_pImmediateContext->PSSetConstantBuffers(1, 1, &g_pCBLigth);
        g_pImmediateContext->PSSetShader(g_pPixelShader, NULL, 0);
        g_pImmediateContext->DrawIndexed(meshInfo.indexiesAmount, 0, 0);
    }


    g_pImmediateContext->PSSetShader(g_pPixelShaderSolid, NULL, 0);
    for (int m = 0; m < 2; m++) {
        updateMatrix(m, 0);
        g_pImmediateContext->DrawIndexed(meshInfo.indexiesAmount, 0, 0);
    }

    g_pSwapChain->Present(0, 0);
}
typedef struct Vertex {
    XMFLOAT3 pos;
    XMFLOAT3 normal;
}Vertex;

typedef struct MeshInfo {
    int vertexAmount;
    int normalsAmount;
    vector<XMFLOAT3> positions;
    vector<XMFLOAT3> normals;
    vector<string*> coord;
    vector<int> indexiesPoints;
    vector<int> indexiesNormals;
    int indexiesAmount;
}MeshInfo;


typedef struct ConstantBuffer {
    XMMATRIX mWorld;
    XMMATRIX mView;
    XMMATRIX mProjection;
    XMFLOAT4 vLigthDir[2];
    XMFLOAT4 vLigthColor[2];
    XMFLOAT4 vOutputColor;
}ConstantBuffer;

I expect to see 6 cubes in my program's window, but I get this thing


Solution

  • I've actually found a solution to my problem. The main problem was to make a correct order for vertecies to draw. But in .OBJ files only UNIQUE vertices, normals and texture coordinates are written. Also they are separated from each other (for example it can be 8 UNIQUE vertex coordinates and 6 UNIQUE normals coordinates). The main thing is happening in part, where are faces are described. Each face is pointing to a specific index of position, normal and texture coordinate, meaning that we can use same normals, positions and texture coordinates different times(I know it's obviously). And I realised that in my realisation I don't need indexies array at all, because Vertecies array, that I've filled up was already a model! And I ended up using method Draw instead of DrawIndexed in ID3D11DeviceContext. This is the result that I get with more complex shapes like Susanna from blender