Archives pour la catégorie Introduction à DirectX 10

Introduction à DirectX 10 – Écrire du texte à l’écran (système de polices) – partie 7

directx9c

Hello

Utilisation de BMFont :

BMFont

 

Intro :

Pour afficher du texte dans une application DirectX il faut procéder comme suit.

Prérequis :

– Savoir initialiser DirectX 10

– Savoir comment afficher une image 2D à l’écran

Septième partie :

Afficher du texte à l’écran.

Explications :

Il vous faudra plusieurs images bitmap pour supporter les différentes résolutions de l’écran de l’utilisateur.

« Their source rect will form our texture coordinates, and their destination rect will form our vertexcoordinates. »

 

Voici le fichier FontTextManager.h :

#ifndef FONT_TEXT_MANAGER_H
#define FONT_TEXT_MANAGER_H

#include <d3dx10math.h>
#include <vector>
#include <map>
#include <string>
#include <fstream>

#include "Singleton.h"
#include "Defines.h"
#include "Types.h"

namespace FontSystem
{
    struct FontTextFileCharacter
    {
        FontTextFileCharacter() :
        iXPos(0),
        iYPos(0),
        iWidth(0),
        iHeight(0),
        iXOffset(0),
        iYOffset(0),
        iXAdvance(0),
        iPage(0)
        {
        }

        uint16 iXPos, iYPos;
        uint16 iWidth, iHeight;
        float iXOffset, iYOffset;
        float iXAdvance;
        uint16 iPage;
    };

    struct PTPVertex
    {
        D3DXVECTOR3 position;
        D3DXVECTOR2 texture;
        int page;
    };

    struct Font
    {
        std::string sFontTextName;
        uint16 iLineHeight;
        uint16 iBase;
        uint16 iWidth; // Taille en largeur de l'image de la police
        uint16 iHeight; // Taille en hauteur de l'image de la police
        uint16 iPagesCount;
        int16 iSize;

        // Un tableau de caractères constituants la police de font
        FontTextFileCharacter* aCharacters;
        // Un tableau contenant les fichiers shader ressources  (les pages !) en l'occurence les textures de la police de font
        ID3D10ShaderResourceView* texturesArray;
    };

    class Text
    {
    public:
        Text(const std::wstring m_sText, const D3DXCOLOR& color, float fPosX, float fPosY);
        virtual ~Text();
    
        void SetFont(Font* pFont);
        Font* GetFont();

        float GetPosX();
        float GetPosY();

        std::wstring GetText();
    
        void SetVertexBuffer(ID3D10Buffer* pBuffer);
        void SetIndexBuffer(ID3D10Buffer* pBuffer);

        ID3D10Buffer* GetVertexBuffer();
        ID3D10Buffer* GetIndexBuffer();

        D3DXCOLOR GetColor();

        size_t GetVertexCount();
        size_t GetIndexCount();

        void SetVertexCount(size_t iCount);
        void SetIndexCount(size_t iCount);

        void SetTransparency(float fAlpha);

    private:
        std::wstring m_sText;

        float m_fPosX;
        float m_fPosY;

        D3DXCOLOR m_color;

        Font* m_pFont;

        // Un vertex / index buffer par page
        ID3D10Buffer* m_pVertexBuffer;
        ID3D10Buffer* m_pIndexBuffer;

        size_t m_iVertexCount;
        size_t m_iIndexCount;
    };
}

using namespace FontSystem;

class FontTextManager : public Singleton<FontTextManager>
{
public : // todo : mettre des commentaires partout ; utiliser AlphaTransparencyPass
    FontTextManager();
    virtual ~FontTextManager();
    
    bool Initialize();

    void Render(Text* pText);

    bool LoadFontTextFile(const std::string& sFileName, const std::string& sFontTextName);
    bool IsFontTextLoaded(const std::string& sFontTextName);

    Text* CreateText(const std::wstring& sText, const std::string& sFontTextName, const float fPosX, const float fPosY,
        const D3DXCOLOR& color);

    float GetGlyphAspectRatio(const wchar_t* c, const std::string& sFontTextName);
    RECT GetGlyphTexCoords(const wchar_t* c, const std::string& sFontTextName);
    uint16 GetCharacterPage(const wchar_t* c, const std::string& sFontTextName);

private:
    void CreateVertexInputLayout();
    void SetupMatrix();
    bool BuildTextBuffers(Text* text);
    bool InitializeShader();

    ID3D10Texture2D* CreateTexture(const std::string& sBitmapFileName);
    ID3D10Texture2D* CreateTextureArray(std::vector<ID3D10Texture2D*>& textures);
    ID3D10ShaderResourceView* GetTextureRVArray(ID3D10Texture2D* pTexArray);

private:
    D3D10_INPUT_ELEMENT_DESC* m_pLayoutPT; // todo : enlever
    ID3D10InputLayout* m_pVertexLayout;

    std::map<std::string, Font*> m_fonts;

    ID3D10Effect* m_pEffect;
    ID3D10EffectTechnique* m_pTechnique;

    ID3D10EffectShaderResourceVariable* m_pBitmapVariable;

    ID3D10EffectMatrixVariable* m_pWorldVariable;
    ID3D10EffectMatrixVariable* m_pViewVariable;
    ID3D10EffectMatrixVariable* m_pProjVariable;
    ID3D10EffectVectorVariable* m_pColorVariable;

    D3DXMATRIX m_worldMatrix;
    D3DXMATRIX m_viewMatrix;
    D3DXMATRIX m_projMatrix;
};

#endif

 

Voici le fichier FontTextManager.cpp :

#include "FontTextManager.h"
#include "D3D10Renderer.h"
#include "AlphaPass.h"

Text::Text(const std::wstring sText, const D3DXCOLOR& color, float fPosX, float fPosY) :
m_sText(sText),
m_color(color),
m_fPosX(fPosX),
m_fPosY(fPosY),
m_pFont(nullptr),
m_pIndexBuffer(nullptr),
m_pVertexBuffer(nullptr),
m_iIndexCount(0),
m_iVertexCount(0)
{
}

Text::~Text()
{
    SAFE_RELEASE(m_pVertexBuffer);
    SAFE_RELEASE(m_pIndexBuffer);
}

void Text::SetFont(Font* pFont)
{
    m_pFont = pFont;
}

Font* Text::GetFont()
{
    return m_pFont;
}

std::wstring Text::GetText()
{
    return m_sText;
}

void Text::SetVertexBuffer(ID3D10Buffer* pBuffer)
{
    m_pVertexBuffer = pBuffer;
}

void Text::SetIndexBuffer(ID3D10Buffer* pBuffer)
{
    m_pIndexBuffer = pBuffer;
}

ID3D10Buffer* Text::GetVertexBuffer()
{
    return m_pVertexBuffer;
}

ID3D10Buffer* Text::GetIndexBuffer()
{
    return m_pIndexBuffer;
}

size_t Text::GetVertexCount()
{
    return m_iVertexCount;
}

size_t Text::GetIndexCount()
{
    return m_iIndexCount;
}

void Text::SetVertexCount(size_t iCount)
{
    m_iVertexCount = iCount;
}

void Text::SetIndexCount(size_t iCount)
{
    m_iIndexCount = iCount;
}

D3DXCOLOR Text::GetColor()
{
    return m_color;
}

float Text::GetPosX()
{
    return m_fPosX;
}

float Text::GetPosY()
{
    return m_fPosY;
}

void Text::SetTransparency(float fAlpha)
{
    m_color.a = fAlpha;
}

// ---------------------------------------------//

template<> FontTextManager* Singleton<FontTextManager>::ms_instance = nullptr;

FontTextManager::FontTextManager() :
m_pLayoutPT(nullptr),
m_pBitmapVariable(nullptr),
m_pEffect(nullptr),
m_pTechnique(nullptr),
m_pVertexLayout(nullptr),
m_pWorldVariable(nullptr),
m_pViewVariable(nullptr),
m_pProjVariable(nullptr),
m_pColorVariable(nullptr)
{
}

FontTextManager::~FontTextManager()
{
    SAFE_DELETE(m_pLayoutPT);

    SAFE_RELEASE(m_pVertexLayout);
    SAFE_RELEASE(m_pEffect);
}

bool FontTextManager::Initialize()
{
    CreateVertexInputLayout();

    if (!InitializeShader())
    {
        return false;
    }

    SetupMatrix();

    return true;
}

bool FontTextManager::InitializeShader()
{
    HRESULT hr = S_OK;

    ID3D10Blob* pBlob = nullptr;
 
    hr = D3DX10CreateEffectFromFile(FONT_TEXT_FX_FILE_NAME, nullptr, nullptr, "fx_4_0",
        D3D10_SHADER_ENABLE_STRICTNESS | D3D10_SHADER_DEBUG, 0,
        D3D10_RENDERER->GetDevice(), nullptr, nullptr, &m_pEffect, &pBlob, nullptr);

    if (FAILED(hr))
    {
        if (pBlob)
        {
            // Affiche l'erreur de compilation du shader
            MessageBoxA(nullptr, (PCSTR)pBlob->GetBufferPointer(), "Erreur dans le fichier shader", MB_ICONHAND | MB_OK);
        }
        else
        {
            ShowMessageBoxDXError(hr);
        }
 
        return false;
    }
  

    m_pTechnique = m_pEffect->GetTechniqueByName("FontTechnique");

    m_pWorldVariable = m_pEffect->GetVariableByName("World")->AsMatrix();
    m_pViewVariable = m_pEffect->GetVariableByName("View")->AsMatrix();
    m_pProjVariable = m_pEffect->GetVariableByName("Projection")->AsMatrix();
    m_pBitmapVariable = m_pEffect->GetVariableByName("gTextures")->AsShaderResource();
    m_pColorVariable = m_pEffect->GetVariableByName("BlendColor")->AsVector();

    D3D10_PASS_DESC PassDesc;
    m_pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);
    hr = D3D10_RENDERER->GetDevice()->CreateInputLayout(m_pLayoutPT, 3, PassDesc.pIAInputSignature,
                                         PassDesc.IAInputSignatureSize, &m_pVertexLayout);

    VR_RETURN(hr);

    return true;
}

void FontTextManager::SetupMatrix()
{
    D3DXMatrixIdentity(&m_worldMatrix);
 
    D3DXVECTOR3 Eye(0.0f, 0.0f, -1.0f);
    D3DXVECTOR3 At(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 Up(0.0f, 1.0f, 0.0f);
 
    D3DXMatrixLookAtLH(&m_viewMatrix, &Eye, &At, &Up);

    D3DXMatrixOrthoLH(&m_projMatrix, (float) D3D10_RENDERER->GetViewportWidth(), (float) D3D10_RENDERER->GetViewportHeight(),
        1.0f, 20.0f);
}

void FontTextManager::CreateVertexInputLayout()
{    
    m_pLayoutPT = new D3D10_INPUT_ELEMENT_DESC[3];

    D3D10_INPUT_ELEMENT_DESC layoutPT[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "PAGE", 0, DXGI_FORMAT_R16_UINT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };

    memcpy(m_pLayoutPT, layoutPT, sizeof(D3D10_INPUT_ELEMENT_DESC) * 3);
}

bool FontTextManager::IsFontTextLoaded(const std::string& sFontTextName)
{
    return m_fonts.count(sFontTextName) != 0;
}
/*
    http://www.angelcode.com/products/bmfont/doc/render_text.html
*/
bool FontTextManager::BuildTextBuffers(Text* pText)
{
    std::string sFontTextName = pText->GetFont()->sFontTextName;

    std::wstring sText = pText->GetText();
    float fPosX = pText->GetPosX();
    float fPosY = -pText->GetPosY();

    // Décalage en fonction de la taille de l'écran
    fPosX -= (float)((D3D10_RENDERER->GetViewportWidth() / 2 ));
    fPosY += (float)((D3D10_RENDERER->GetViewportHeight() / 2 ));

    // On vérifie si l'on a bien la police chargée en mémoire
    if (m_fonts.count(sFontTextName) == 0)
    {
        return false;
    }

    // On obtient la police spécifiée
    Font* pFont = m_fonts[sFontTextName];

    // Tableau de tous les caractères de la police de font
    FontTextFileCharacter* aCharacters = pFont->aCharacters;

    D3D10_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
    D3D10_SUBRESOURCE_DATA vertexData, indexData;

    HRESULT hr;

    std::vector<PTPVertex> vertices;

    for (uint32 i = 0; i < sText.length(); i++)
    {
        // Chaque lettre
        uint32 iLetter = (uint32)sText[i];

        FontTextFileCharacter character = pFont->aCharacters[iLetter];

        if (iLetter == SPACE_ASCII_CODE) // Espace !
        {
            fPosX += character.iXAdvance;

            continue;
        }

        if (iLetter == '\n')
        {
            fPosX = 0;
        }

        float x = 0;
        float y = 0;

        float tu = 0;
        float tv = 0;

        uint32 iCharX = character.iXPos; // Abscisse du caractère dans la texture
        uint32 iCharY = character.iYPos; // Ordonnée du caractère dans la texture
        uint32 iWidth = character.iWidth; // Largeur du caractère dans la texture
        uint32 iHeight = character.iHeight; // Hauteur du caractère dans la texture
        float iOffsetX = character.iXOffset;
        float iOffsetY = character.iYOffset;
        float iXAdvance = character.iXAdvance;
        uint16 iPage = character.iPage;

         //----------------- 1er triangle de la face du caractère -----------------//

        // ---- Bottom left ---- //
        iOffsetY = - iOffsetY - iHeight;
        //iOffsetY = (- iOffsetY - pFont->iLineHeight
        //iOffsetY -= pFont->iLineHeight;
        //iOffsetY -= pFont->iLineHeight;

        x = fPosX + iOffsetX;
        y = fPosY + iOffsetY;

        tu = (float) (iCharX) / pFont->iWidth;
        tv = (float) (iCharY + iHeight) / pFont->iHeight;

        PTPVertex vertex;

        vertex.position = D3DXVECTOR3(x, y, 0.0f);
        vertex.texture = D3DXVECTOR2(tu, tv);
        vertex.page = iPage;

        vertices.push_back(vertex);

        // ---- Top left ---- //

        x = fPosX + iOffsetX;
        y = fPosY + iOffsetY + iHeight;

        tu = (float) iCharX / pFont->iWidth;
        tv = (float) iCharY / pFont->iHeight;

        vertex.position = D3DXVECTOR3(x, y, 0.0f);  
        vertex.texture = D3DXVECTOR2(tu, tv);
        vertex.page = iPage;

        vertices.push_back(vertex);

        // ---- Top right ---- //

        x = fPosX + iOffsetX + iWidth;
        y = fPosY + iOffsetY + iHeight;

        tu = (float) (iCharX + iWidth) / pFont->iWidth;
        tv = (float) iCharY / pFont->iHeight;

        vertex.position = D3DXVECTOR3(x, y, 0.0f);  
        vertex.texture = D3DXVECTOR2(tu, tv);
        vertex.page = iPage;

        vertices.push_back(vertex);

        //----------------- 2ème triangle de la face du caractère -----------------//

        // ---- Top right ---- //

        x = fPosX + iOffsetX + iWidth;
        y = fPosY + iOffsetY + iHeight;

        tu = (float) (iCharX + iWidth) / pFont->iWidth;
        tv = (float) iCharY / pFont->iHeight;

        vertex.position = D3DXVECTOR3(x, y, 0.0f);  
        vertex.texture = D3DXVECTOR2(tu, tv);
        vertex.page = iPage;

        vertices.push_back(vertex);

        // ---- Bottom right ---- //

        x = fPosX + iOffsetX + iWidth;
        y = fPosY + iOffsetY;

        tu = (float) (iCharX + iWidth) / pFont->iWidth;
        tv = (float) (iCharY + iHeight) / pFont->iHeight;

        vertex.position = D3DXVECTOR3(x, y, 0.0f);  
        vertex.texture = D3DXVECTOR2(tu, tv);
        vertex.page = iPage;

        vertices.push_back(vertex);

        // ---- Bottom left ---- //

        x = fPosX + iOffsetX;
        y = fPosY + iOffsetY;

        tu = (float) (iCharX) / pFont->iWidth;
        tv = (float) (iCharY + iHeight) / pFont->iHeight;

        vertex.position = D3DXVECTOR3(x, y, 0.0f);  
        vertex.texture = D3DXVECTOR2(tu, tv);
        vertex.page = iPage;

        vertices.push_back(vertex);

        // On décale de quelques pixels vers la gauche après chaque lettre
        fPosX += iXAdvance;
    }

    pText->SetVertexCount(vertices.size());
    pText->SetIndexCount( pText->GetVertexCount() );

    std::vector<uint16> indices;
    for (uint16 i = 0; i < pText->GetIndexCount(); i++)
    {
        indices.push_back(i);
    }

    vertexBufferDesc.Usage = D3D10_USAGE_DYNAMIC;
    vertexBufferDesc.ByteWidth = sizeof(PTPVertex) * pText->GetVertexCount();
    vertexBufferDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
    vertexBufferDesc.MiscFlags = 0;

    vertexData.pSysMem = vertices.data();

    ID3D10Buffer* pVB = nullptr;

    hr = D3D10_RENDERER->GetDevice()->CreateBuffer(&vertexBufferDesc, &vertexData, &pVB);
    VR_RETURN(hr);

    pText->SetVertexBuffer(pVB);
        
    indexBufferDesc.Usage = D3D10_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(uint16) * pText->GetIndexCount();
    indexBufferDesc.BindFlags = D3D10_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;

    indexData.pSysMem = indices.data();

    ID3D10Buffer* pIB = nullptr;
    hr = D3D10_RENDERER->GetDevice()->CreateBuffer(&indexBufferDesc, &indexData, &pIB);
    VR_RETURN(hr);

    pText->SetIndexBuffer(pIB);

    return true;
}

void FontTextManager::Render(Text* pText)
{
    if (!pText)
    {
        return;
    } 

    uint32 iStride = sizeof(PTPVertex);
    uint32 iOffset = 0;

    D3D10_RENDERER->EnableZBuffer(false);

    m_pWorldVariable->SetMatrix((float*)&m_worldMatrix);
    m_pViewVariable->SetMatrix((float*)&m_viewMatrix);
    m_pProjVariable->SetMatrix((float*)&m_projMatrix);
    m_pColorVariable->SetFloatVector((float*)pText->GetColor());

    ID3D10Buffer* pVertexBuffer = pText->GetVertexBuffer();
    ID3D10Buffer* pIndexBuffer = pText->GetIndexBuffer();

    size_t iVertexCount = pText->GetVertexCount();
    size_t iIndexCount = pText->GetIndexCount();

    D3D10_RENDERER->GetDevice()->IASetVertexBuffers(0, 1, &pVertexBuffer, &iStride, &iOffset);
    D3D10_RENDERER->GetDevice()->IASetIndexBuffer(pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
    D3D10_RENDERER->GetDevice()->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    ID3D10ShaderResourceView* pRV = pText->GetFont()->texturesArray;

    if (pRV)
    {
        m_pBitmapVariable->SetResource(pRV);
    }

    D3D10_TECHNIQUE_DESC techniqueDesc;
    D3D10_RENDERER->GetDevice()->IASetInputLayout(m_pVertexLayout);

    m_pTechnique->GetDesc(&techniqueDesc);

    AlphaPass TransparencyPass;

    for (uint32 i = 0; i < techniqueDesc.Passes; i++)
    {
        m_pTechnique->GetPassByIndex(i)->Apply(0);

        D3D10_RENDERER->GetDevice()->Draw(iVertexCount, 0);
    }

    D3D10_RENDERER->EnableZBuffer(true);
}

Text* FontTextManager::CreateText(const std::wstring& sText, const std::string& sFontTextName,
                                  const float fPosX, const float fPosY, const D3DXCOLOR& color)
{
    Text* pText = new Text(sText, color, fPosX, fPosY);

    if (!IsFontTextLoaded(sFontTextName)) // todo : add dummy text
    {
        PRINT_ERROR("Police de caractere non reconnu");

        return nullptr;
    }

    pText->SetFont(m_fonts[sFontTextName]);

    if (!BuildTextBuffers(pText))
    {
        PRINT_ERROR("Echec lors du la construction des buffers d'un texte speficie");

        return nullptr;
    }

    return pText;
}

/* Voir la documentation sur
    http://www.angelcode.com/products/bmfont/doc/file_format.html
*/
bool FontTextManager::LoadFontTextFile(const std::string& sFileName, const std::string& sFontTextName)
{
    std::vector<ID3D10Texture2D*> textures;

    std::string sLine;
    std::string sRead, sKey, sValue;

    std::ifstream file(sFileName);

    if (!file.is_open())
    {
        PRINT_ERROR("Impossible de charger un fichier font");

        return false;
    }

    std::string sBitmapFileName = "";

    FontTextFileCharacter* aCharacters = new FontTextFileCharacter[256];

    Font* pFont = new Font;
    pFont->sFontTextName = sFontTextName;
    pFont->aCharacters = aCharacters;

    while (!file.eof())
    {
        std::stringstream sLineStream;
        std::getline( file, sLine );
        sLineStream << sLine;

        // Lit le type de la ligne
        sLineStream >> sRead;

        if (sRead == "info")
        {
            while (!sLineStream.eof())
            {
                std::stringstream Converter;
                sLineStream >> sRead;

                uint32 i = sRead.find('=');
                sKey = sRead.substr(0, i);
                sValue = sRead.substr(i + 1);

                Converter << sValue;

                if (sKey == "size")
                {
                    Converter >> pFont->iSize;
                }
            }
        }
        else if (sRead == "common")
        {
            while (!sLineStream.eof())
            {
                std::stringstream Converter;
                sLineStream >> sRead;

                uint32 i = sRead.find('=');
                sKey = sRead.substr(0, i);
                sValue = sRead.substr(i + 1);

                Converter << sValue;

                if (sKey == "lineHeight")
                {
                    Converter >> pFont->iLineHeight;
                }
                else if (sKey == "base")
                {
                    Converter >> pFont->iBase;
                }
                else if (sKey == "scaleW")
                {
                    Converter >> pFont->iWidth;
                }
                else if (sKey == "scaleH")
                {
                    Converter >> pFont->iHeight;
                }
                else if (sKey == "pages")
                {
                    Converter >> pFont->iPagesCount;
                }
            }
        }
        else if (sRead == "page")
        {
            while (!sLineStream.eof())
            {
                std::stringstream Converter;
                sLineStream >> sRead;

                uint32 i = sRead.find('=');
                sKey = sRead.substr(0, i);
                sValue = sRead.substr(i + 1);

                Converter << sValue;
    
                if (sKey == "file")
                {
                    Converter >> sBitmapFileName;

                    // On supprime les guillemets entre le nom du fichier
                    sBitmapFileName.pop_back();    
                    sBitmapFileName.erase(0, 1);

                    fastprint("Chargement de " << sBitmapFileName << " pour " << pFont->sFontTextName);
                
                    ID3D10Texture2D* pTexture = CreateTexture(sBitmapFileName);

                    if (pTexture)
                    {
                        textures.push_back(pTexture);
                    }
                }

            }
        }
        else if (sRead == "char")
        {
            uint16 iCharID = 0;

            while (!sLineStream.eof())
            {
                std::stringstream Converter;
                sLineStream >> sRead;

                uint32 i = sRead.find('=');
                sKey = sRead.substr(0, i);
                sValue = sRead.substr(i + 1);
                Converter << sValue;

                if (sKey == "id")
                {
                    Converter >> iCharID;
                }
                else if (sKey == "x")
                {
                    Converter >> pFont->aCharacters[iCharID].iXPos;
                }
                else if (sKey == "y" )
                {
                    Converter >> pFont->aCharacters[iCharID].iYPos;
                }
                else if (sKey == "width")
                {
                    Converter >> pFont->aCharacters[iCharID].iWidth;
                }
                else if (sKey == "height")
                {
                    Converter >> pFont->aCharacters[iCharID].iHeight;
                }
                else if (sKey == "xoffset")
                {
                    Converter >> pFont->aCharacters[iCharID].iXOffset;
                }
                else if (sKey == "yoffset")
                {
                    Converter >> pFont->aCharacters[iCharID].iYOffset;
                }
                else if (sKey == "xadvance")
                {
                    Converter >> pFont->aCharacters[iCharID].iXAdvance;
                }
                else if (sKey == "page")
                {
                    Converter >> pFont->aCharacters[iCharID].iPage;
                }
            }
        }
    }
    
    m_fonts[sFontTextName] = pFont;

    debugprint(pFont->sFontTextName.c_str());
    debugprint(pFont->iBase);
    debugprint(pFont->iHeight);
    debugprint(pFont->iLineHeight);
    debugprint(pFont->iPagesCount);
    debugprint(pFont->iWidth);

    if (textures.size() == 0)
    {
        PRINT_ERROR("Aucune textures chargees");
    }

    fastprint("\n---------------------------\n");

    ID3D10Texture2D* pTextureArray = CreateTextureArray(textures);

    for (uint32 i = 0; i < textures.size(); i++)
    {
        ID3D10Texture2D* pCurrentTexture = textures[i];

        SAFE_RELEASE(pCurrentTexture);
    }

    ID3D10ShaderResourceView* pRVArray = GetTextureRVArray(pTextureArray);

    SAFE_RELEASE(pTextureArray);

    pFont->texturesArray = pRVArray;

    return true;
}

float FontTextManager::GetGlyphAspectRatio(const wchar_t* c, const std::string& sFontTextName)
{
    // On obtient la police spécifiée
    Font* pFont = m_fonts[sFontTextName];

    if (pFont)
    {
        return 0;
    }

    // Tableau de tous les caractères de la police de font
    FontTextFileCharacter* aCharacters = pFont->aCharacters;

    FontTextFileCharacter character = aCharacters[(uint32)c];

    uint32 iWidth = character.iWidth;
    uint32 iHeight = character.iHeight;

    return (float) (iWidth / iHeight);
}

RECT FontTextManager::GetGlyphTexCoords(const wchar_t* c, const std::string& sFontTextName)
{
    RECT rect;
    ZeroMemory(&rect, sizeof(RECT));

    // On obtient la police spécifiée
    Font* pFont = m_fonts[sFontTextName];

    if (!pFont)
    {
        return rect;
    }

    // Tableau de tous les caractères de la police de font
    FontTextFileCharacter* aCharacters = pFont->aCharacters;

    FontTextFileCharacter character = aCharacters[(uint32)c];

    uint32 iCharX = character.iXPos;
    uint32 iCharY = character.iYPos;
    uint32 iWidth = character.iWidth;
    uint32 iHeight = character.iHeight;

    rect.left = iCharX;
    rect.top = iCharY;
    rect.right = iWidth;
    rect.bottom = iHeight;

    return rect;
}

uint16 FontTextManager::GetCharacterPage(const wchar_t* c, const std::string& sFontTextName)
{
    // On obtient la police spécifiée
    Font* pFont = m_fonts[sFontTextName];

    if (!pFont)
    {
        return 0;
    }

    // Tableau de tous les caractères de la police de font
    FontTextFileCharacter* aCharacters = pFont->aCharacters;

    FontTextFileCharacter character = aCharacters[(uint32)c];

    return character.iPage;
}

ID3D10Texture2D* FontTextManager::CreateTexture(const std::string& sBitmapFileName)
{
    ID3D10Texture2D* pLoadedTexture = nullptr;

    D3DX10_IMAGE_LOAD_INFO loadinfo;
    ZeroMemory(&loadinfo, sizeof(D3DX10_IMAGE_LOAD_INFO));

    loadinfo.Width = D3DX10_FROM_FILE;
    loadinfo.Height = D3DX10_FROM_FILE;
    loadinfo.Depth = D3DX10_FROM_FILE;

    loadinfo.FirstMipLevel = 0;
    loadinfo.MipLevels = D3DX10_FROM_FILE;
    loadinfo.Usage = D3D10_USAGE_STAGING;

    loadinfo.BindFlags = 0;
    loadinfo.CpuAccessFlags = D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE;
    loadinfo.MiscFlags = 0;

    loadinfo.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    loadinfo.Filter = D3DX10_FILTER_NONE;
    loadinfo.MipFilter = D3DX10_FILTER_NONE;
    loadinfo.pSrcInfo = 0;

    D3DX10CreateTextureFromFileA(D3D10_RENDERER->GetDevice(),
        sBitmapFileName.c_str(), &loadinfo, 0, (ID3D10Resource**)&pLoadedTexture, 0);

    return pLoadedTexture;
}

ID3D10Texture2D* FontTextManager::CreateTextureArray(std::vector<ID3D10Texture2D*>& textures)
{
    if (textures.size() == 0)
    {
        return nullptr;
    }

    ID3D10Texture2D* pTexturesArray = nullptr;

    D3D10_TEXTURE2D_DESC textElementDesc;
    ZeroMemory(&textElementDesc, sizeof(D3D10_TEXTURE2D_DESC));

    textures[0]->GetDesc(&textElementDesc);

    D3D10_TEXTURE2D_DESC textArrayDesc;

    textArrayDesc.Width = textElementDesc.Width;
    textArrayDesc.Height = textElementDesc.Height;
    textArrayDesc.MipLevels = textElementDesc.MipLevels;
    textArrayDesc.ArraySize = textures.size();
    textArrayDesc.Format = textElementDesc.Format;
    textArrayDesc.SampleDesc.Count = 1;
    textArrayDesc.SampleDesc.Quality = 0;
    textArrayDesc.Usage = D3D10_USAGE_DEFAULT;
    textArrayDesc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
    textArrayDesc.CPUAccessFlags = 0;
    textArrayDesc.MiscFlags = 0;

    D3D10_RENDERER->GetDevice()->CreateTexture2D(&textArrayDesc, 0, &pTexturesArray);

    // On copie toutes les textures dans le tableau !
    for (uint16 i = 0; i < textures.size(); i++)
    {
        for (uint16 j = 0; j < textElementDesc.MipLevels; j++)
        {
            D3D10_MAPPED_TEXTURE2D mappedTex2D;

            ID3D10Texture2D* pCurrentTexture = textures[i];

            pCurrentTexture->Map(j, D3D10_MAP_READ, 0, &mappedTex2D);

            uint32 c = D3D10CalcSubresource(j, i, textElementDesc.MipLevels);

            D3D10_RENDERER->GetDevice()->UpdateSubresource(pTexturesArray, c,
                0, mappedTex2D.pData, mappedTex2D.RowPitch, 0);

            pCurrentTexture->Unmap(j);
        }
    }

    return pTexturesArray;
}

ID3D10ShaderResourceView* FontTextManager::GetTextureRVArray(ID3D10Texture2D* pTexArray)
{
    if (pTexArray == nullptr)
    {
        return nullptr;
    }

    ID3D10ShaderResourceView* pArrayRV = nullptr;

    D3D10_SHADER_RESOURCE_VIEW_DESC viewDesc;
    ZeroMemory(&viewDesc, sizeof(D3D10_SHADER_RESOURCE_VIEW_DESC));

    D3D10_TEXTURE2D_DESC textArrayDesc;
    pTexArray->GetDesc(&textArrayDesc);

    viewDesc.Format = textArrayDesc.Format;
    viewDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2DARRAY;
    viewDesc.Texture1DArray.MostDetailedMip = 0;
    viewDesc.Texture1DArray.MipLevels = textArrayDesc.MipLevels;
    viewDesc.Texture1DArray.FirstArraySlice = 0;
    viewDesc.Texture1DArray.ArraySize = textArrayDesc.ArraySize;

    D3D10_RENDERER->GetDevice()->CreateShaderResourceView(pTexArray, &viewDesc, &pArrayRV);

    return pArrayRV;
}

 

Résumé :

Nous avons présenter une méthode générique pour afficher du texte à l’écran ! Pas besoin d’utiliser D3DXFont !

Références :

– http://www.rastertek.com/dx10tut12.html

– http://kvazars.com/littera/

– http://www.angelcode.com/products/bmfont/

– http://www.angelcode.com/products/bmfont/doc/file_format.html

– http://www.gamedev.net/topic/330742-quick-tutorial-variable-width-bitmap-fonts/

Introduction à DirectX 10 – Redimensionner la fenêtre de rendu – partie 6

directx9c

Intro :

Dans une application DirectX, l’utilisateur peut vouloir redimensionner la fenêtre à sa guise ou passer le jeu en mettre en mode plein écran.

Il faut implémenter et coder cette fonctionnalité.

Prérequis :

– Avoir suivi la cinquième partie de ce tutoriel.

Sixième partie :

Redimensionnement et plein écran de la fenêtre de rendu.

Explications :

Avant tout, juste pour énoncer un petit point  mis à part :

L’appel à D3D11DeviceContext::ClearState réinitialise par défaut toutes les configurations du contexte d’affichage (remet à zéro les shaders, les inputs layout, les viewports, etc..)

 


 

Avec les interfaces DXGI* on peut obtenir les informations sur la carte graphique :


bool D3D10Renderer::GetGPUInfo()
{
    HRESULT hr = S_OK;

    uint32 iNumModes;

    IDXGIFactory* pDXGIFactory = nullptr;
    IDXGIAdapter* pDXGIAdapter = nullptr;
    IDXGIOutput* pDXGIAdapterOutput = nullptr;

    DXGI_MODE_DESC* pDisplayModeList = nullptr;

    DXGI_ADAPTER_DESC adapterDesc;

    // Créée une interface métier "factory" DirectX
    hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&pDXGIFactory);

    HR_FAILED_RETURN_FALSE(hr);

    // Utilise la "factory"
    hr = pDXGIFactory->EnumAdapters(0, &pDXGIAdapter);

    HR_FAILED_RETURN_FALSE(hr);

    // Enumère l'adpatateur principal de l'écran
    hr = pDXGIAdapter->EnumOutputs(0, &pDXGIAdapterOutput);

    HR_FAILED_RETURN_FALSE(hr);

    // Obtient le nombre de modes d'affichage qui corresppond à DXGI_FORMAT_R8G8B8A8_UNORM  
    hr = pDXGIAdapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &iNumModes, nullptr);

    HR_FAILED_RETURN_FALSE(hr);

    /* Créé une liste qui détient tous les modes d'affichage possibles pour cette carte graphique et
       le format d'affichage */
    pDisplayModeList = new DXGI_MODE_DESC[iNumModes];

    if (!pDisplayModeList)
    {
        return false;
    }

    // Remplie la précédente structure
    hr = pDXGIAdapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &iNumModes, pDisplayModeList);
    
    HR_FAILED_RETURN_FALSE(hr);

    /* On parcourt tous les modes d'affichages possibles
       et on trouve celui qui correspond à la taille spécifiée
       en paramètre ; puis on enregistre le numérateur et le dénominateur
       du taux de rafraichissement de l'écran
    */
    for (uint32 i = 0; i < iNumModes; i++)
    {
        if (pDisplayModeList[i].Width == (uint32)m_iWidth)
        {
            if (pDisplayModeList[i].Height == (uint32)m_iHeight)
            {
                m_iNumerator = pDisplayModeList[i].RefreshRate.Numerator;
                m_iDenominator = pDisplayModeList[i].RefreshRate.Denominator;
            }
        }

        DXGI_MODE_DESC desc = pDisplayModeList[i];

        // On vérifie que ce mode n'a pas déjà été ajouté
        bool bAlreadyExist = false;
        for (uint32 i = 0; i < m_modes.size(); i++)
        {
            if (m_modes[i].Width == desc.Width &&
                m_modes[i].Height == desc.Height)
            {
                bAlreadyExist = true;
                break;
            }
        }

        if (!bAlreadyExist)
        {
            m_modes.push_back(desc);
        }
    }

    hr = pDXGIAdapter->GetDesc(&adapterDesc);

    HR_FAILED_RETURN_FALSE(hr);

    // On enregistre la taille de la mémoire vidéo en mégabytes
    m_iVideoCardMemory = (adapterDesc.DedicatedVideoMemory / 1024 / 1024);

    size_t iStringLength = 0;
    // Convertie le nom la carte vidéo dans une chaîne de caractères
    int iError = wcstombs_s(&iStringLength, m_sVideoCardDescription, 128, adapterDesc.Description, 128);

    if (iError != 0)
    {
        return false;
    }

    // On supprime les objets dont a plus besoin !
    SAFE_DELETE_ARRAY(pDisplayModeList);

    SAFE_RELEASE(pDXGIAdapterOutput);
    SAFE_RELEASE(pDXGIAdapter);
    SAFE_RELEASE(pDXGIFactory);

    return true;
}

 

Voici le programme de cet article une fois compilé et lancé :

fullscreen

 

La fonction qui redimensionne les buffers :


bool D3D10Renderer::ResizeSwapChain(const size_t iWidth, const size_t iHeight)
{
    /* On décharge les précédents objets de rendu */
    SAFE_RELEASE(m_pRenderTargetView);
    SAFE_RELEASE(m_pDepthStencilView);

    HRESULT hr = S_OK;
    hr = m_pSwapChain->ResizeBuffers(2, iWidth, iHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0);

    HR_FAILED_RETURN_FALSE(hr);

    m_iWidth = iWidth;
    m_iHeight = iHeight;

    /* Puis on les recréer */
    CreateRenderTarget();
    CreateDepthStencilView();

    //m_pCube->SetLens()
    SetViewport();

    return true;
}

 

La fonction qui gère les entrées clavier :

void D3D10Renderer::OnKeyPressed(const KeyEvent& arg)
{
    if (arg.keyCode == VK_ESCAPE)
    {
        SYSTEM->Quit();
    }
    else if (arg.keyCode == VK_F1)
    {
        m_bFullscreen = !m_bFullscreen;

        if (m_bFullscreen)
        {
            ResizeSwapChain(m_iWidth, m_iHeight);
        }

        m_pSwapChain->SetFullscreenState(m_bFullscreen, nullptr);
    }
    else if (arg.keyCode == VK_F2)
    {
        static uint32 iMode = 0;

        // Largeur de la fenêtre
        uint32 iWidth = m_modes[iMode].Width;
        // Hauteur de la fenêtre
        uint32 iHeight = m_modes[iMode].Height;

        ResizeSwapChain(iWidth, iHeight);

        RECT rc;

        GetWindowRect(m_hWnd, &rc);

        SetWindowPos(m_hWnd, 0, rc.left, rc.top, iWidth, iHeight, SWP_SHOWWINDOW);

        iMode++;

        if (iMode >= m_modes.size())
        {
            iMode = 0;
        }
    }
}

 

Résumé :

Depuis DirectX 10 il est plus facile et aisé de changer de résolution et de passer en mode plein écran.

Voici les fichiers source – Partie 6

Références :

– https://msdn.microsoft.com/en-us/library/windows/desktop/ff476389%28v=vs.85%29.aspx

– https://msdn.microsoft.com/en-us/library/windows/desktop/bb174577%28v=vs.85%29.aspx

Introduction à DirectX 10 – Activer le super échantillonnage – partie 5

directx9c

Intro :

Dans une application graphique, les objets du rendu peuvent subir ce qu’on appelle un « crénelage » ou un « aliasing » en anglais.

Il apparaît comme un escalier autour de la ligne affichée.

La ligne du bas représente la correction de l’effet d’escalier de la ligne du haut.

crénélage

Prérequis :

– Savoir initialiser DirectX 10.1

Explications :

Cinquième partie :

Par défaut, DirectX 10.1 gère un type d’anticrénelage nommé Multisample anti-aliasing (MSAA)

1) L’anti-aliasing activé :

AA_on

1) L’anti-aliasing désactivé :

AA_off

 

Comment activer le multi-sampling dans le code ?

Lors de l’appel à D3D10CreateDeviceAndSwapChain il faut spécifier dans la structure DXGI_SWAP_CHAIN_DESC :

DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&amp;sd, sizeof(sd));
sd.BufferCount = 1;
sd.BufferDesc.Width = m_iWidth;
sd.BufferDesc.Height = m_iHeight;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 0;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = m_hWnd;

//----------------------------------//

/* On indique le nombre d'échantillons de l'anti-aliasing */
sd.SampleDesc.Count = m_iFSAASampleCount;
/* On indique le niveau de qualité de l'anti-aliasing */
sd.SampleDesc.Quality = 0;

//----------------------------------//

// Fenêtré ou non
sd.Windowed = !m_bFullscreen;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

 

warning_small

Il reste deux étapes à ne pas oublier d’effectuer :


1) Effectuer la même configuration que précédemment, lors la création de la texture du deph-stencil
:

Il faut spécifier la même configuration que précédemment, sinon cela ne fonctionnera pas !

D3D10_TEXTURE2D_DESC descDepth;
ZeroMemory(&amp;descDepth, sizeof(descDepth));
descDepth.Width = m_iWidth;
descDepth.Height = m_iHeight;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D32_FLOAT;

//----------------------------------//

/* On indique le nombre d'échantillons de l'anti-aliasing */
descDepth.SampleDesc.Count = m_iFSAASampleCount;
/* On indique le niveau de qualité de l'anti-aliasing */
descDepth.SampleDesc.Quality = 0;

//----------------------------------//

descDepth.Usage = D3D10_USAGE_DEFAULT;
descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;

m_pd3dDevice-&gt;CreateTexture2D(&amp;descDepth, nullptr, &amp;m_pDepthStencil);


2) Effectuer une certaine configuration lors la création de la DepthStencilView
:

D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
descDSV.Format = descDepth.Format;

if (m_iFSAALevel &gt; 1)
{
     descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2DMS;
}
else
{
     descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
}

descDSV.Texture2D.MipSlice = 0;

m_pd3dDevice-&gt;CreateDepthStencilView(m_pDepthStencil, &amp;descDSV, &amp;m_pDepthStencilView);

 


 

Voici une fonction qui permet d’afficher les capacités possibles de votre carte graphique en matière d’anti-aliasing.

Il faut obtenir le nombre d’échantillons m_iFSAASampleCount par ce bais :

void PrintFSAACapabilities()
{
    HRESULT hr = S_OK;
    UINT maxQualityLevel = 1;

    for (UINT iSampleCount = 1; iSampleCount &lt;= D3D10_MAX_MULTISAMPLE_SAMPLE_COUNT; iSampleCount++)
    {
        hr = m_pd3dDevice-&gt;CheckMultisampleQualityLevels(
            DXGI_FORMAT_R8G8B8A8_UNORM, iSampleCount, &amp;maxQualityLevel);

        if (hr != S_OK)
        {
            std::cout &lt;&lt; &quot;CheckMultisampleQualityLevels a échouee.&quot; &lt;&lt; std::endl;
        }
                                 
        if (maxQualityLevel &gt; 0)
        {
            std::cout &lt;&lt; &quot;MSAA &quot; &lt;&lt; iSampleCount &lt;&lt; (&quot;X supportee par la carte vidéo avec &quot;) &lt;&lt;
                maxQualityLevel &lt;&lt; &quot; niveau(x) de qualite.&quot; &lt;&lt; std::endl;
        }
    }
}

 

Résumé :

Nous avons présenté comment procéder pour afficher dans DirectX 10 les différents niveaux d’anticrénélages possibles par la carte vidéo et comment les activer.

Références :

– Windows DirectX Graphics Documentation (SDK June 2010)

Introduction à DirectX 10 – Rendu 2D – partie 5

directx9c

Intro :

Dans cette cinquième partie, nous allons apprendre comment afficher une image 2D à l’écran.
Nous proposerons deux façon d’implémenter cet affichage.

Prérequis :

Avoir suivi la cinquième partie de ce tutoriel.

Quatrième partie :

Afficher une image 2D à l’écran.

Explications (1ère façon plus simple, sans utiliser de shader) :

Voici le fichier Sprite2D.h :

#ifndef SPRITE_2D_H
#define SPRITE_2D_H

#include <d3dx10.h>
#include <string>

class Sprite2D
{
public:
    Sprite2D(ID3D10Device* pDevice, WCHAR* sTextureFilename, int iImageWidth, int iImageHeight);
    virtual ~Sprite2D();

    bool Initialize();
    void Render();

    void SetPosition(int iPosX, int iPosY);

private:    
    bool Update();

    ID3D10Device* m_pDevice;

    int m_iImageWidth;
    int m_iImageHeight;

    int m_iPosX;
    int m_iPosY;

    ID3D10ShaderResourceView* m_pTextureRessourceView;
    
    std::wstring m_sTextureFilename;

    D3DX10_SPRITE m_pSpriteDesc;
    ID3DX10Sprite* m_pSprite;

    ID3D10Texture2D* m_pTexture;;

    D3DXMATRIX m_matProjection;
};

#endif

 

Voici le constructeur de la classe Sprite2D :

Sprite2D::Sprite2D(ID3D10Device* pDevice, WCHAR* sTextureFilename,
     int iImageWidth, int iImageHeight) :
m_pSprite(nullptr),
m_pTexture(nullptr),
m_pTextureRessourceView(nullptr),
m_sTextureFilename(sTextureFilename),
m_pDevice(pDevice),
m_iPosX(0),
m_iPosY(0),
m_iImageWidth(iImageWidth),
m_iImageHeight(iImageHeight)
{
    if (!Initialize())
    {
        MessageBoxA(NULL, "Erreur d'initialisation d'un Sprite !", "Erreur",
                    MB_ICONHAND | MB_OK);
    }
}

 

Voici la méthode d’initialisation :

bool Sprite2D::Initialize()
{
    HRESULT hr = D3DX10CreateTextureFromFile(m_pDevice, m_sTextureFilename.c_str(), nullptr, nullptr, (ID3D10Resource**)&m_pTexture, nullptr);

    if (FAILED(hr))
    {
         return false;
    }

    hr = m_pDevice->CreateShaderResourceView((ID3D10Resource*)m_pTexture, nullptr, &m_pTextureRessourceView);

    if (FAILED(hr))
    {
         return false;
    }

    ZeroMemory(&m_pSpriteDesc, sizeof(D3DX10_SPRITE));

    m_pSpriteDesc.pTexture = m_pTextureRessourceView;
    m_pSpriteDesc.TexCoord.x = 0;
    m_pSpriteDesc.TexCoord.y = 0;
    m_pSpriteDesc.TexSize.x = 1.0f;
    m_pSpriteDesc.TexSize.y = 1.0f;
    m_pSpriteDesc.TextureIndex = 0;
    m_pSpriteDesc.ColorModulate = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);

    hr = D3DX10CreateSprite(m_pDevice, 0, &m_pSprite);

    if (FAILED(hr))
    {
         return false;
    }

    D3DXMatrixOrthoOffCenterLH(&m_matProjection, 0.0f, D3D10_RENDERER->GetViewportWidth(), 0.0f,
        D3D10_RENDERER->GetViewportHeight(), 0.1f, 10);

    hr = m_pSprite->SetProjectionTransform(&m_matProjection);

    if (FAILED(hr))
    {
         return false;
    }

    Update();

    return true;
}

 

Voici les autres méthodes :

void Sprite2D::Render()
{
    m_pSprite->Begin(D3DX10_SPRITE_SORT_TEXTURE | D3DX10_SPRITE_SAVE_STATE);

        m_pSprite->DrawSpritesImmediate(&m_pSpriteDesc, 1, 0, 0);

    m_pSprite->End();
}

bool Sprite2D::Update()
{
    float fSpritePosX = m_iPosX;
    float fSpritePosY = m_iPosY;
    float fSpriteWidth = m_iImageWidth;
    float fSpriteHeight = m_iImageHeight;

    D3DXMATRIX matTranslation;
    D3DXMatrixTranslation(&matTranslation, (fSpriteWidth / 2) + fSpritePosX, (fSpriteHeight / 2) + fSpritePosY, 0.1);

    D3DXMATRIX matScaling;
    D3DXMatrixScaling(&matScaling, fSpriteWidth, fSpriteHeight, 1.0f);

    m_pSpriteDesc.matWorld = matScaling * matTranslation;

    return true;
}

void Sprite2D::SetPosition(int iPosX, int iPosY)
{
    m_iPosX = iPosX;
    m_iPosY = iPosY;

    Update();
}

 

 


 

 

Explications (2ème façon plus compliquée, en utilisant un shader) :

Mettre dans un fichier Sprite2D.h :

#ifndef SPRITE_2D
#define SPRITE_2D

#include <d3dx10.h>
#include <string>

class Sprite2D
{
public:
    // La structure du rectangle à afficher
    struct VertexType
    {
        D3DXVECTOR3 position;
        D3DXVECTOR2 texture;
    };

    Sprite2D(ID3D10Device* pDevice, WCHAR* sTextureFilename, int iImageWidth, int iImageHeight);
    virtual ~Sprite2D();

    bool Initialize();
    void Render();

    void SetPosition(int iPosX, int iPosY);

private:    
    // Met à jour la dimension du quad texturé
    bool Update();

    ID3D10Device* m_pDevice;

    ID3D10Buffer* m_pVertexBuffer;
    ID3D10Buffer* m_pIndexBuffer;

    int m_iVertexCount;
    int m_iIndexCount;

    int m_iScreenWidth;
    int m_iScreenHeight;

    int m_iImageWidth;
    int m_iImageHeight;

    /* Position de l'image */
    int m_iPosX;
    int m_iPosY;

    int m_iPreviousPosX;
    int m_iPreviousPosY;

    ID3D10ShaderResourceView* m_pTexture;
    ID3D10EffectShaderResourceVariable* m_pTextureVariable;

    ID3D10Effect* m_pEffect;
    ID3D10EffectTechnique* m_pTechnique;
    ID3D10InputLayout* m_pLayout;

    ID3D10EffectMatrixVariable* m_pWorldVariable;
    ID3D10EffectMatrixVariable* m_pViewVariable;
    ID3D10EffectMatrixVariable* m_pProjectionVariable;

    D3DXMATRIX m_worldMatrix;
    D3DXMATRIX m_viewMatrix;
    D3DXMATRIX m_orthoMatrix;
    
    // Le nom de la texture de l'image à afficher
    std::wstring m_sTextureFilename;
};

#endif

 

Créer un fichier Sprite2D.cpp où vous metterez :

bool Sprite2D::Initialize()
{
    // On prépare la matrice orthogonale, spécialement conçue pour
    // le rendu 2D.
    D3DXMatrixOrthoLH(&m_orthoMatrix, (float)m_iScreenWidth,
                      (float)m_iScreenHeight, 1.0f, 20.0f);

    VertexType* arVertices;
    unsigned long* indices;

    D3D10_BUFFER_DESC vertexBufferDesc;
    D3D10_BUFFER_DESC indexBufferDesc;

    D3D10_SUBRESOURCE_DATA vertexData;
    D3D10_SUBRESOURCE_DATA indexData;

    HRESULT hr;

    m_iVertexCount = 6;
    m_iIndexCount = m_iVertexCount;
    arVertices = new VertexType[m_iVertexCount];
    indices = new unsigned long[m_iIndexCount];

    memset(arVertices, 0, (sizeof(VertexType) * m_iVertexCount));
    for (unsigned int i = 0; i < m_iIndexCount; i++)
    {
        indices[i] = i;
    }

    vertexBufferDesc.Usage = D3D10_USAGE_DYNAMIC;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_iVertexCount;
    vertexBufferDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
    vertexBufferDesc.MiscFlags = 0;

    vertexData.pSysMem = arVertices;

    // Créér le vertex buffer
    hr = m_pDevice->CreateBuffer(&vertexBufferDesc, &vertexData, &m_pVertexBuffer);

    if (FAILED(hr))
    {
        return false;
    }

    indexBufferDesc.Usage = D3D10_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_iIndexCount;
    indexBufferDesc.BindFlags = D3D10_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;

    indexData.pSysMem = indices;

    // Créér l'index buffer
    hr = m_pDevice->CreateBuffer(&indexBufferDesc, &indexData, &m_pIndexBuffer);

    if (FAILED(hr))
    {
        return false;
    }

    delete [] arVertices;
    arVertices = 0;

    delete [] indices;
    indices = 0;

    /********* Partie préparation du shader *********/

    unsigned int numElements;
    D3D10_PASS_DESC passDesc;

    hr = D3DX10CreateEffectFromFile(L"Texture.fx", NULL, NULL, "fx_4_0",
                                    D3D10_SHADER_ENABLE_STRICTNESS, 0,
                                    m_pDevice, NULL, NULL, &m_pEffect, NULL, NULL);
    if (FAILED(hr))
    {
        return false;
    }

    m_pTechnique = m_pEffect->GetTechniqueByName("TextureTechnique");
    if (!m_pTechnique)
    {
        return false;
    }

    D3D10_INPUT_ELEMENT_DESC polygonLayout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };
    numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

    m_pTechnique->GetPassByIndex(0)->GetDesc(&passDesc);

    // Créér le vertex input layout
    hr = m_pDevice->CreateInputLayout(polygonLayout, numElements, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &m_pLayout);
    if (FAILED(hr))
    {
        return false;
    }

    m_pWorldVariable = m_pEffect->GetVariableByName("worldMatrix")->AsMatrix();
    m_pViewVariable = m_pEffect->GetVariableByName("viewMatrix")->AsMatrix();
    m_pProjectionVariable = m_pEffect->GetVariableByName("projectionMatrix")->AsMatrix();
    m_pTextureVariable = m_pEffect->GetVariableByName("shaderTexture")->AsShaderResource();

    // Créée la texture
    hr = D3DX10CreateShaderResourceViewFromFile(m_pDevice, m_sTextureFilename.c_str(), NULL, NULL, &m_pTexture, NULL);

    if (FAILED(hr))
    {
        return false;
    }

    return true;
}

 

La fonction de rendu / d’affichage :


void Sprite2D::Render()
{
    Update();

    // On désactive le ZBuffer pour que l'image soit affichée devant
    // tous les rendus
    D3D10_RENDERER->EnableZBuffer(false);

    unsigned int stride = sizeof(VertexType);
    unsigned int offset = 0;
   
    // On prépare les matrices de transformation
    D3DXMatrixIdentity(&m_worldMatrix);
    D3DXMatrixIdentity(&m_viewMatrix);

    D3DXVECTOR3 Eye(0.0f, 0.0f, -1.0f);
    D3DXVECTOR3 At(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 Up(0.0f, 1.0f, 0.0f);

    D3DXMatrixLookAtLH(&m_viewMatrix, &Eye, &At, &Up);

    // Initialise les variables du shader
    m_pWorldVariable->SetMatrix((float*) &m_worldMatrix);
    m_pViewVariable->SetMatrix((float*) &m_viewMatrix);
    m_pProjectionVariable->SetMatrix((float*) &m_orthoMatrix);

    m_pTextureVariable->SetResource(m_pTexture);

    // Prépare le vertex input layout
    m_pDevice->IASetInputLayout(m_pLayout);

    // Prépare les vertex et index buffers pour être affichés
    m_pDevice->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &stride, &offset);
    m_pDevice->IASetIndexBuffer(m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
    m_pDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    // Affiche le quad texturé
    D3D10_TECHNIQUE_DESC techniqueDesc;
    m_pTechnique->GetDesc(&techniqueDesc);
    for(unsigned int i = 0; i < techniqueDesc.Passes; i++)
    {
        m_pTechnique->GetPassByIndex(i)->Apply(0);
        m_pDevice->DrawIndexed(m_iIndexCount, 0, 0);
    }

    D3D10_RENDERER->EnableZBuffer(true);
}

 

Ici la fonction de mise à jour de la position de l’image :

bool Sprite2D::Update()
{
    float left, right, top, bottom;
    VertexType* arVertices;
    void* pVerticesPtr;
    HRESULT hr;

    // On calcul les coordonnées du quad en fonction de la position
    // de l'image
    left = (float)((m_iScreenWidth / 2) * -1) + (float)m_iPosX;
    right = left + (float)m_iImageWidth;

    top = (float)(m_iScreenHeight / 2) - (float)m_iPosY;
    bottom = top - (float)m_iImageHeight;

    arVertices = new VertexType[m_iVertexCount];

    // Premier triangle
    arVertices[0].position = D3DXVECTOR3(left, top, 0.0f);  // Top left.
    arVertices[0].texture = D3DXVECTOR2(0.0f, 0.0f);

    arVertices[1].position = D3DXVECTOR3(right, bottom, 0.0f);  // Bottom right.
    arVertices[1].texture = D3DXVECTOR2(1.0f, 1.0f);

    arVertices[2].position = D3DXVECTOR3(left, bottom, 0.0f);  // Bottom left.
    arVertices[2].texture = D3DXVECTOR2(0.0f, 1.0f);

    // Deuxième triangle
    arVertices[3].position = D3DXVECTOR3(left, top, 0.0f);  // Top left.
    arVertices[3].texture = D3DXVECTOR2(0.0f, 0.0f);

    arVertices[4].position = D3DXVECTOR3(right, top, 0.0f);  // Top right.
    arVertices[4].texture = D3DXVECTOR2(1.0f, 0.0f);

    arVertices[5].position = D3DXVECTOR3(right, bottom, 0.0f);  // Bottom right.
    arVertices[5].texture = D3DXVECTOR2(1.0f, 1.0f);

    hr = m_pVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&pVerticesPtr);

    if (FAILED(hr))
    {
        return false;
    }

    memcpy(pVerticesPtr, (void*)arVertices, (sizeof(VertexType) * m_iVertexCount));

    m_pVertexBuffer->Unmap();

    delete [] arVertices;
    arVertices = 0;

    return true;
}

 

Créez un fichier Texture.fx, où vous mettrez :

matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
Texture2D shaderTexture;

SamplerState SampleType
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
};

PixelInputType TextureVertexShader(VertexInputType input)
{
    PixelInputType output;
  
    input.position.w = 1.0f;

    output.position = mul(input.position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
    
    output.tex = input.tex;
    
    return output;
}

float4 TexturePixelShader(PixelInputType input) : SV_Target
{
    float4 textureColor;

    textureColor = shaderTexture.Sample(SampleType, input.tex);

    return textureColor;
}

technique10 TextureTechnique
{
    pass pass0
    {
        SetVertexShader(CompileShader(vs_4_0, TextureVertexShader()));
        SetPixelShader(CompileShader(ps_4_0, TexturePixelShader()));
        SetGeometryShader(NULL);
    }
}

Résumé :

Nous avons expliquées deux façon d’afficher du rendu 2D avec DirectX.

texture

Nous nous servirons de cet affichage 2D pour implémenter une UI (Interface Utilisateur).

Références :

– http://mscerts.programming4.us/programming/directx%2010%20game%20programming%20%20%20the%202d%20resurgence%20-%20sprites.aspx

Introduction à DirectX 10 – Lumière – partie 4

directx9c

Intro :

Dans cette quatrième partie, nous allons apprendre à intégrer une lumière afin d’éclairer notre cube.

Prérequis :

Avoir suivi la troisième partie de ce tutoriel.

Quatrième partie :

Afficher un petit cube éclairé d’une lumière.

Explications :

Nous allons utiliser cette structure de vertex :

struct SimpleVertex
{
    D3DXVECTOR3 Pos;
    D3DXVECTOR2 Tex;
    D3DXVECTOR3 Normal;
};

Elle contient un attribut supplémentaire par rapport à la partie 3 : la normale.

La normale d’un vertex est un vecteur perpendiculaire à celui-ci ; nous l’utiliserons afin de calculer le degré de nuance de lumière à cet endroit.
C’est par ce principe que nous effectuerons globalement le calcul lumineux.

Voir cet article pour comprendre comment modéliser la lumière dans un rendu 3D.

 


 

Nous allons utiliser ce programme HLSL – partie4.fx :

//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
matrix World;
matrix View;
matrix Projection;
float4 vLightDir;
Texture2D txDiffuse;

SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

//--------------------------------------------------------------------------------------
struct VS_INPUT
{
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD;
    float3 Norm : NORMAL;
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float2 Tex : TEXCOORD0;
    float3 Norm : TEXCOORD1;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = mul( input.Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Norm = mul( input.Norm, World );
    output.Tex = input.Tex;
    
    return output;
}

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( PS_INPUT input) : SV_Target
{
    float4 finalColor = (saturate( dot( (float3)vLightDir, input.Norm) )
    * txDiffuse.Sample( samLinear, input.Tex ));
    finalColor.a = 1;
    return finalColor;
}

//--------------------------------------------------------------------------------------
technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}

Si vous ne comprenez rien à ce programme : consulter cet article.

Il faut aussi connaître la notion de matrices et le principe de transformations de coordonnées
pour comprendre ce code : voir cet article.

 

Dans votre fichier D3D10Renderer.h, rajoutez :

// Variables matrices en cours
ID3D10EffectMatrixVariable* m_pWorldVariable;
ID3D10EffectMatrixVariable* m_pViewVariable;
ID3D10EffectMatrixVariable* m_pProjectionVariable;

ID3D10EffectVectorVariable* m_pLightDirVariable;
ID3D10EffectVectorVariable* m_pLightColorVariable; 

// Variable texture en cours
ID3D10EffectShaderResourceVariable* m_pDiffuseVariable;

Ce sont les variables que l’on enverra au shader HLSL.

 

Ensuite dans votre fichier D3D10Renderer.cpp à la fonction Initialize(bool bFullScreen), rajoutez :

/******************** Partie 2 & 3 & 4 ********************/

DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;

#if defined( DEBUG ) || defined( _DEBUG )
// Permet d'afficher les éventuelles erreurs de la compilation d'un shader
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif

// Pour la création du shader, on le compile
hr = D3DX10CreateEffectFromFile(L"Partie4.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0, m_pd3dDevice, NULL,
                                NULL, &m_pEffect, NULL, NULL);
if (FAILED(hr))
    return false;

// On acquiert la technique du shader HLSL
m_pTechnique = m_pEffect->GetTechniqueByName("Render");

// On acquiert les variables du shader HLSL
// cela permettra de faire le lien avec l'application et le programme shader HLSL
m_pWorldVariable = m_pEffect->GetVariableByName("World")->AsMatrix();
m_pViewVariable = m_pEffect->GetVariableByName("View")->AsMatrix();
m_pProjectionVariable = m_pEffect->GetVariableByName("Projection")->AsMatrix();
m_pLightDirVariable = m_pEffect->GetVariableByName( "vLightDir" )->AsVector();
m_pDiffuseVariable = m_pEffect->GetVariableByName("txDiffuse")->AsShaderResource();

/******************/ 

// On définit la structure d'un vertex, ici 3 attributs sont déclarés
D3D10_INPUT_ELEMENT_DESC layout[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = sizeof( layout ) / sizeof( layout[0] );

D3D10_PASS_DESC PassDesc;
m_pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);
hr = m_pd3dDevice->CreateInputLayout(layout, numElements, PassDesc.pIAInputSignature,
                                     PassDesc.IAInputSignatureSize, &m_pVertexLayout);
if (FAILED(hr))
    return false;

m_pd3dDevice->IASetInputLayout(m_pVertexLayout);

 

On créé le vertex buffer d’un cube : il est composé de la position, des coordonées de la texture et des normales de chaque vertex.

Les voici :

// Déclaration des vertices
SimpleVertex vertices[] =
{
    { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) },

    { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ),  D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) },
    { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ),  D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) },

    { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ),  D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) },
    { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ),  D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ),  D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) },

    { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ),    D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) },

    { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ),  D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ),  D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 1.0f ),  D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) },

    { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 0.0f ),  D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ),     D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ),  D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) },
};
// Déclaration des indices
DWORD indices[] =
{
    3,1,0,
    2,1,3,

    6,4,5,
    7,4,6,

    11,9,8,
    10,9,11,

    14,12,13,
    15,12,14,

    19,17,16,
    18,17,19,

    22,20,21,
    23,20,22
};

 

Toujours dans votre fichier D3D10Renderer.cpp à la fonction Initialize(bool bFullScreen), rajoutez :

D3DXMatrixIdentity(&m_worldMatrix);

D3DXVECTOR3 Eye(0.0f, 3.0f, -6.0f);
D3DXVECTOR3 At(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 Up(0.0f, 1.0f, 0.0f);

D3DXMatrixLookAtLH(&m_viewMatrix, &Eye, &At, &Up);

// Initialize the projection matrix
D3DXMatrixPerspectiveFovLH( &m_projectionMatrix, ( float )D3DX_PI * 0.25f, width / ( FLOAT )height, 0.1f, 100.0f );

// Update Variables that never change
m_pViewVariable->SetMatrix( ( float* )&m_viewMatrix );
m_pProjectionVariable->SetMatrix( ( float* )&m_projectionMatrix );
m_pDiffuseVariable->SetResource( m_pTextureRV );

 

Toujours dans votre fichier D3D10Renderer.cpp :

void D3D10Renderer::Render()
{
    // Couleur du fond de rendu
    static float afClearColor[4] = {0.0f, 0.125f, 0.3f, 1.0f}; 

    // Efface la surface de rendu
    m_pd3dDevice->ClearRenderTargetView(m_pRenderTargetView, afClearColor);
    
    /*********** Donnée d'une variable de temps ***********/
 
    static float t = 0.0f;
    static DWORD dwTimeStart = 0;
    DWORD dwTimeCur = GetTickCount();
    if( dwTimeStart == 0 )
        dwTimeStart = dwTimeCur;
    t = ( dwTimeCur - dwTimeStart ) / 1000.0f;
    
    /*******************************************************/
    
    // Direction de la lumière
    D3DXVECTOR4 vLightDirs( -1.577f, 0.577f, -0.577f, 1.0f );

    D3DXMATRIX mRotate;
    D3DXVECTOR4 vOutDir;
    D3DXMatrixRotationZ( &mRotate, -2.0f * t );
    D3DXVec3Transform( &vLightDirs, ( D3DXVECTOR3* )&vLightDirs, &mRotate );

    // On met à jour les variables du shader HLSL
    m_pWorldVariable->SetMatrix((float*)&m_worldMatrix);
    m_pLightDirVariable->SetFloatVectorArray((float*)vLightDirs, 0, 2);

    /**********************/

    // Affiche le cube
    D3D10_TECHNIQUE_DESC techDesc;
    m_pTechnique->GetDesc(&techDesc);
    for (UINT p = 0; p < techDesc.Passes; ++p)
    {
        m_pTechnique->GetPassByIndex( p )->Apply( 0 );
        m_pd3dDevice->DrawIndexed( 36, 0, 0 );
    }

    /**********************/

    // On affiche le front buffer
    m_pSwapChain->Present( 0, 0 );
}

Résumé :

Nous avons intégré une lumière près de notre cube afin de l’éclairer.

partie4

Voici l’archive du code complet pour cette partie :  DirectX 10 Tutoriel – Partie 4.zip

Introduction à DirectX 10 – Texturing – partie 3

directx9c

Intro :

Dans cette troisième partie, nous allons apprendre comment ajouter une texture à un petit cube auto généré.

Prérequis :

Avoir suivi la deuxième partie de ce tutoriel.

Troisième partie :

Afficher un petit cube avec une texture appliquée dessus.

Explications :

Dans ce tutoriel nous allons utiliser cette structure de vertex :

struct SimpleVertex
{
    D3DXVECTOR3 Pos;
    D3DXVECTOR2 Tex;
};

La deuxième variable indique que l’on va utiliser les coordonnées de la texture appliqués au cube.

Rajoutez dans votre fichier D3D10Renderer.h :


/* Variables HLSL des matrix.
   Ces variables seront utilisées et envoyées
   au vertex shader HLSL. */
ID3D10EffectMatrixVariable* m_pWorldVariable;
ID3D10EffectMatrixVariable* m_pViewVariable;
ID3D10EffectMatrixVariable* m_pProjectionVariable;

// Variable de la texture
ID3D10EffectShaderResourceVariable* m_pDiffuseVariable;

// Les matrix pour afficher le rendu
D3DXMATRIX m_worldMatrix;
D3DXMATRIX m_viewMatrix;
D3DXMATRIX m_projectionMatrix;

ID3D10ShaderResourceView* m_pTextureRV;

 

Voici le fichier effet HLSL utilisé – partie3.fx :

//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
Texture2D txDiffuse;
SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

cbuffer cbNeverChanges
{
    matrix View;
};

cbuffer cbChangeOnResize
{
    matrix Projection;
};

cbuffer cbChangesEveryFrame
{
    matrix World;
};

struct VS_INPUT
{
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD;
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float2 Tex : TEXCOORD0;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = mul( input.Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Tex = input.Tex;
    
    return output;
}

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( PS_INPUT input) : SV_Target
{
    return txDiffuse.Sample( samLinear, input.Tex );
}

//--------------------------------------------------------------------------------------
technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}

 

Dans le fichier D3D10Renderer.cpp à la fonction Initialize(bool bFullScreen) :

/******************** Partie 2 & 3 ********************/

DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;

#if defined( DEBUG ) || defined( _DEBUG )
// Permet d'afficher les éventuelles erreurs de la compilation d'un shader
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif

// Pour la création du shader, on le compile
hr = D3DX10CreateEffectFromFile(L"Partie3.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0, m_pd3dDevice, NULL,
                                NULL, &m_pEffect, NULL, NULL);
if (FAILED(hr))
    return false;

// On acquiert la technique du shader HLSL
m_pTechnique = m_pEffect->GetTechniqueByName("Render");

/* Voici les variables matrix utilisées par le shader HLSL */
m_pWorldVariable = m_pEffect->GetVariableByName("World")->AsMatrix();
m_pViewVariable = m_pEffect->GetVariableByName("View")->AsMatrix();
m_pProjectionVariable = m_pEffect->GetVariableByName("Projection")->AsMatrix();
m_pMeshColorVariable = m_pEffect->GetVariableByName("vMeshColor")->AsVector();
m_pDiffuseVariable = m_pEffect->GetVariableByName("txDiffuse")->AsShaderResource();

/******************************************************/

Voir cet article si vous ne comprenez pas à quoi sert les matrices World, View et Projection.

On définit la nouvelle structure d’un vertex :


D3D10_INPUT_ELEMENT_DESC layout[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = sizeof( layout ) / sizeof( layout[0] );

D3D10_PASS_DESC PassDesc;
m_pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);
hr = m_pd3dDevice->CreateInputLayout(layout, numElements, PassDesc.pIAInputSignature,
                                     PassDesc.IAInputSignatureSize, &m_pVertexLayout);
if(FAILED(hr))
    return false;

m_pd3dDevice->IASetInputLayout(m_pVertexLayout);

/******************************************************/

On définit les informations de vertex et index buffers d’un cube :

// On créé le vertex buffer d'un cube
SimpleVertex vertices[] =
{
    { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) },

    { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) },
    { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) },

    { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) },
    { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) },

    { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) },

    { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) },

    { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 0.0f ) },
    { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ) },
    { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ) },
};

// On créé l'index buffer de ce cube
DWORD indices[] =
{
    3,1,0,
    2,1,3,

    6,4,5,
    7,4,6,

    11,9,8,
    10,9,11,

    14,12,13,
    15,12,14,

    19,17,16,
    18,17,19,

    22,20,21,
    23,20,22
};

Voir cet article si vous ne comprenez pas à quoi sert les vertex buffer et index buffer.

On implémente les vertex et index buffers :

/********** Vertex buffer **********/

D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DEFAULT;
// Taille d'une entrée vertex
bd.ByteWidth = sizeof(SimpleVertex) * 24;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices;
hr = m_pd3dDevice->CreateBuffer(&bd, &InitData, &m_pVertexBuffer);
if(FAILED(hr))
    return false;

UINT stride = sizeof(SimpleVertex);
UINT offset = 0;
m_pd3dDevice->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &stride, &offset);

/********** Index buffer **********/

bd.Usage = D3D10_USAGE_DEFAULT;
// Taille d'une entrée index
bd.ByteWidth = sizeof( DWORD ) * 36;
bd.BindFlags = D3D10_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
InitData.pSysMem = indices;
hr = m_pd3dDevice->CreateBuffer(&bd, &InitData, &m_pIndexBuffer);
if(FAILED(hr))
    return false;

// Set index buffer
m_pd3dDevice->IASetIndexBuffer( m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );

/* On définit le type de primitive */
m_pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

/******************************************************/

 

On charge la texture qui sera utilisée :

// On charge la texture
hr = D3DX10CreateShaderResourceViewFromFile(m_pd3dDevice, L"seafloor.dds", NULL, NULL, &m_pTextureRV, NULL);
if(FAILED(hr))
    return false;

 

On initialise les matrices et la texture :

D3DXMatrixIdentity(&m_worldMatrix);

D3DXVECTOR3 Eye(0.0f, 3.0f, -6.0f);
D3DXVECTOR3 At(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 Up(0.0f, 1.0f, 0.0f);

D3DXMatrixLookAtLH(&m_viewMatrix, &Eye, &At, &Up);

// Initialize the projection matrix
D3DXMatrixPerspectiveFovLH( &m_projectionMatrix, ( float )D3DX_PI * 0.25f, width / ( FLOAT )height, 0.1f, 100.0f );

// Update Variables that never change
m_pViewVariable->SetMatrix( ( float* )&m_viewMatrix );
m_pProjectionVariable->SetMatrix( ( float* )&m_projectionMatrix );
m_pDiffuseVariable->SetResource( m_pTextureRV );

 

Résumé :

Au final nous avons affiché un petit cube texturé.

partie3

Voici l’archive du code complet pour cette partie :  DirectX 10 Tutoriel – Partie 3.zip

Introduction à DirectX 10 – Rendu basique – partie 2

directx9c

Intro :

Dans cette deuxième partie, nous allons utiliser un shader basique pour afficher un triangle et afficher du texte à l’écran.

Prérequis :

Avoir suivi la première partie de ce tutoriel.
Savoir le fonctionnement d’un shader HLSL : pour cela il faut suivre le tutoriel sur le HLSL.

Deuxième partie :

Afficher un triangle et du texte à l’écran.

Explications :

Nous allons utiliser ce programme shader nommé Partie2.fx :


//----------------------------------------------------------
// Partie2.fx
//----------------------------------------------------------

struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float4 Color : COLOR0;
};

//----------------------------------------------------------
// Vertex Shader
//----------------------------------------------------------
VS_OUTPUT VS( float4 Pos : POSITION, float4 Color : COLOR )
{
    VS_OUTPUT output = (VS_OUTPUT)0;
    
    output.Pos = Pos;
    output.Color = Color;
    
    return output;    
}

//----------------------------------------------------------
// Pixel Shader
//----------------------------------------------------------
float4 PS( VS_OUTPUT input ) : SV_Target
{
    return input.Color;
}

//----------------------------------------------------------
technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}

Si vous ne comprenez ce bout de code, voir l’article sur le HLSL.

Pensez à mettre ce fichier dans le répertoire de votre programme ! Vous aurez une erreur sinon.

Nous allons afficher un triangle à l’écran. Pour cela il faut que DirectX comprenne comment
sont agencés les vertices dans la mémoire vidéo.

Donc ici on déclare que toutes les vertices utilisées sont composées d’une variable position et d’une variable couleur. Cette structure suivante est utilisée par notre application.


struct SimpleVertex
{
    D3DXVECTOR3 Pos;
    D3DXVECTOR4 Color;
};

Et voilà un schéma des données vertices. Cette structure suivante est utilisée par DirectX.
Voir cet article pour mieux comprendre le mécanisme de cette structure.

D3D10_INPUT_ELEMENT_DESC layout[] =
{
    {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
    {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0},
};
UINT numElements = sizeof(layout) / sizeof(layout[0]);

 

On rajoute à la fonction Initialize du fichier D3D10Renderer.cpp donné dans la partie 1
du tutoriel :

/******************** Partie 2 ********************/

DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;

#if defined( DEBUG ) || defined( _DEBUG )
// Permet d'afficher les éventuelles erreurs de la compilation d'un shader
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif

// Pour la création du shader, on le compile
hr = D3DX10CreateEffectFromFile(L"Partie2.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0, m_pd3dDevice, NULL,
                                NULL, &m_pEffect, NULL, NULL);
if (FAILED(hr))
    return false;

// On acquiert la technique du shader HLSL
m_pTechnique = m_pEffect->GetTechniqueByName("Render");

// On définit la structure d'un vertex
D3D10_INPUT_ELEMENT_DESC layout[] =
{
    {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
    {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0},
};
UINT numElements = sizeof(layout) / sizeof(layout[0]);

D3D10_PASS_DESC PassDesc;
m_pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);
hr = m_pd3dDevice->CreateInputLayout(layout, numElements, PassDesc.pIAInputSignature,
                                      PassDesc.IAInputSignatureSize, &m_pVertexLayout);
if(FAILED(hr))
    return false;

m_pd3dDevice->IASetInputLayout(m_pVertexLayout);

// On définit les vertices
SimpleVertex vertices[] =
{
    D3DXVECTOR3( 0.0f, 0.5f, 0.5f ), D3DXVECTOR4( 0.0f, 1.0f, 1.0f, 1.0f ),
    D3DXVECTOR3( 0.5f, -0.5f, 0.5f ), D3DXVECTOR4( 0.0f, 0.0f, 1.0f, 1.0f ),
    D3DXVECTOR3( -0.5f, -0.5f, 0.5f ), D3DXVECTOR4( 1.0f, 0.0f, 1.0f, 1.0f ),
};
// On implémente le vertex buffer
D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DEFAULT;
bd.ByteWidth = sizeof(SimpleVertex) * 6;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices;
hr = m_pd3dDevice->CreateBuffer(&bd, &InitData, &m_pVertexBuffer);
if(FAILED(hr))
    return false;

// On définit le vertex buffer
UINT stride = sizeof(SimpleVertex);
// Décalage des informations du vertex buffer
UINT offset = 0;
m_pd3dDevice->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &stride, &offset);

// On définit le type de primitive
m_pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

/******** Font ********/

D3DX10CreateSprite(m_pd3dDevice, 0, &m_pSprite);

D3DX10_FONT_DESC fd;
fd.Height = 30;
fd.Width = 18;
fd.Weight = 0;
fd.MipLevels = 4;
fd.Italic = false;
fd.CharSet = OUT_DEFAULT_PRECIS;
fd.Quality = DEFAULT_QUALITY;
fd.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
wcscpy(fd.FaceName, L"Impact");

D3DX10CreateFontIndirect(m_pd3dDevice, &fd, &m_pFont);

 

La fonction de rendu va afficher un triangle et un texte comme convenu :

void D3D10Renderer::Render()
{
    static float afClearColor[4] = {0.0f, 0.125f, 0.3f, 1.0f}; 

    static D3DXCOLOR color(0.0f, 1.0f, 0.3f, 1.0f);
    static RECT rectangle = {10, 10, 0, 0};

    // Efface le backbuffer
    m_pd3dDevice->ClearRenderTargetView(m_pRenderTargetView, afClearColor);

    // Commence par afficher du texte
    m_pSprite->Begin(D3DX10_SPRITE_SAVE_STATE);
    m_pFont->DrawText(m_pSprite, L"Hello World!", -1, &rectangle, DT_NOCLIP, color);
    m_pSprite->End();
    
    // Rendu d'un triangle
    D3D10_TECHNIQUE_DESC techDesc;
    m_pTechnique->GetDesc(&techDesc);
    for (unsigned int p = 0; p < techDesc.Passes; ++p)
    {
        m_pTechnique->GetPassByIndex(p)->Apply(0);
        // On rend 3 vertex
        m_pd3dDevice->Draw(3, 0);
    }

    m_pSwapChain->Present(0, 0);
}

N’oubliez pas d’ajouter dans votre fichier System.h :

ID3DX10Font* m_pFont;
ID3DX10Sprite* m_pSprite;

Résumé :

Au final nous avons affiché un triangle multi-colorisé et affiché un petit texte :

triangle

Voici l’archive du code complet pour cette partie : DirectX 10 Tutoriel – Partie 2.zip

Introduction à DirectX 10 – Fenêtrage – partie 1

Intro :

Ceci constitue une introduction à la version 10 de DirectX.

DirectX est une interface entre le programmeur et la carte graphique.

Dans cette partie je vais vous apprendre à l’initialiser de manière rudimentaire.

Prérequis :

Savoir programmer en C++.

Première partie :

Création de la fenêtre de rendu.

Explications :

Pensez à configurer les chemins d’accès du SDK de DirectX  dans VC++ ! Si vous ne savez pas comment faire : lire ce tutoriel.

On a besoin d’utiliser une fonction qui boucle à chaque fois que l’application reçoit
un message :

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
 
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
 
    return 0;
}


Ensuite créez un fichier System.cpp et un fichier System.h où vous mettrez les fonctions que je vous énoncerai.

Voici le fichier System.h :


#ifndef SYSTEM_H
#define SYSTEM_H

#include "Defines.h"
#include "D3D10Renderer.h"

class System

{
public:
    System();
    virtual ~System();

    bool Initialize();
    void Shutdown();
    bool Frame();
    bool Run();

    HRESULT SetupTheWindow();
    void DestroyTheWindow();

private:
    // Objet qui gère le rendu de DirectX
    D3D10Renderer* m_pRenderer;
    LPCWSTR m_sApplicationName;
    HINSTANCE m_hInstance;
    HWND m_Hwnd;
    bool m_bFullScreen;
};

#endif

Dans le fichier System.cpp. On initialise d’abord une fenêtre Windows ; voici la fonction pour l’initialiser :


HRESULT System::SetupTheWindow()
{
    // Détermine la résolution de l'écran.
    unsigned int iScreenWidth  = GetSystemMetrics(SM_CXSCREEN);
    unsigned int iScreenHeight = GetSystemMetrics(SM_CYSCREEN);

    // Structure de la classe à configurer
    WNDCLASSEX wcex;

    // Taille de la structure WNDCLASSEX
    wcex.cbSize = sizeof( WNDCLASSEX );

    // Style de la classe
    wcex.style = CS_HREDRAW | CS_VREDRAW;

    // Pointeur de la fonction de boucle de fenêtre (Callback)
    wcex.lpfnWndProc = WndProc;

    // Inutile à comprendre
    wcex.cbClsExtra = 0;

    // Inutile à comprendre
    wcex.cbWndExtra = 0;

    // Obtention de l'instance de cette application
    wcex.hInstance = m_hInstance;

    // Icone de la fenêtre
    wcex.hIcon = LoadIcon(NULL, IDI_WINLOGO);

    // Icone du pointeur de la souris
    wcex.hCursor = LoadCursor( NULL, IDC_ARROW );

    // Inutile à comprendre
    wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );

    // Inutile à comprendre
    wcex.lpszMenuName = NULL;

    // Nom de la classe enregistrée
    wcex.lpszClassName = m_sApplicationName;

    wcex.hIconSm = wcex.hIcon;

    DEVMODE dmScreenSettings;

    if(m_bFullScreen)
    {
        ZeroMemory(&dmScreenSettings, sizeof(dmScreenSettings));

        dmScreenSettings.dmSize       = sizeof (dmScreenSettings);
        dmScreenSettings.dmPelsWidth  = (unsigned long) iScreenWidth;
        dmScreenSettings.dmPelsHeight = (unsigned long) iScreenHeight;
        dmScreenSettings.dmBitsPerPel = 32;            
        dmScreenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

        ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
    }

    if (!RegisterClassEx(&wcex))
        return E_FAIL;

    // Créé la fenêtre
    RECT rc = { 0, 0, 640, 480 };
    AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
    m_Hwnd = CreateWindow(m_sApplicationName, m_sApplicationName, WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, m_hInstance,
                          NULL);
    if (!m_Hwnd)
        return E_FAIL;

    ShowWindow(m_Hwnd, SW_SHOW);

    SetForegroundWindow(m_Hwnd);
    SetFocus(m_Hwnd);

    return S_OK;
}

 

Toujours dans System.cpp, la fonction pour détruire la fenêtre :


void System::DestroyTheWindow()
{
    // Change les paramètres d'affichage si l'on quitte le mode plein écran
    if(m_bFullScreen)
    {
        ChangeDisplaySettings(NULL, 0);
    }

    // Détruit la fenêtre.
    DestroyWindow(m_Hwnd);
    m_Hwnd = NULL;

    // Détruit l'instance de l'application
    UnregisterClass(m_sApplicationName, m_hInstance);
    m_hInstance = NULL;
}

 

La fonction pour initialiser la fenêtre et le Renderer de DirectX 10 :


bool System::Initialize()
{
    SetupTheWindow();

    m_pRenderer = new D3D10Renderer(m_Hwnd);
    bool bSucess = m_pRenderer->Initialize(m_bFullScreen);

    if (!bSucess)
    {
        MessageBoxA(NULL, "Erreur d'initialisation de DirectX !", "Erreur", MB_ICONHAND | MB_OK);
        return false;
    }

    return true;
}

 

 


 

La fonction WinMain est dans le fichier Main.cpp et est implémentée de cette façon :

#include "D3D10Renderer.h"
#include "System.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
{
    System* pSystem = new System();

    bool bSucess = pSystem->Initialize();

    if (bSucess)
    {
        pSystem->Run();
    }

    pSystem->Shutdown();

    SAFE_DELETE(pSystem);

    return 0;
}

 


 

Ensuite créez un fichier D3D10Renderer.cpp et un fichier D3D10Renderer.h où vous mettrez les fonctions que je vous énoncerai.


bool D3D10Renderer::Initialize(bool bFullScreen)
{
    HRESULT hr = S_OK;;
    RECT rc;

    GetClientRect(m_hWnd, &rc);
    UINT width = rc.right - rc.left;
    UINT height = rc.bottom - rc.top;

    UINT createDeviceFlags = 0;

#ifdef _DEBUG
    createDeviceFlags |= D3D10_CREATE_DEVICE_DEBUG;
#endif
    // Une swap chain représente en fait le back buffer
    // et le front buffer (principe nommé double buffering)
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 1;
    sd.BufferDesc.Width = width;
    sd.BufferDesc.Height = height;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = m_hWnd;
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = !bFullScreen;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    hr = D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags,
                                        D3D10_SDK_VERSION, &sd, &m_pSwapChain, &m_pd3dDevice);
    if (FAILED(hr))
        return false;

    // Créé le back buffer
    ID3D10Texture2D* pBackBuffer;
    hr = m_pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&pBackBuffer);
    if (FAILED(hr))
        return false;
    // Créé la render target
    hr = m_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &m_pRenderTargetView);
    pBackBuffer->Release();
    if (FAILED(hr))
        return false;

    m_pd3dDevice->OMSetRenderTargets( 1, &m_pRenderTargetView, NULL );

    // Création du viewport
    D3D10_VIEWPORT vp;
    vp.Width = width;
    vp.Height = height;
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;
    m_pd3dDevice->RSSetViewports( 1, &vp );

    
    return true;
}

 

La fonction pour afficher le rendu :


void D3D10Renderer::Render()
{
    float afClearColor[4] = {0.0f, 0.125f, 0.3f, 1.0f}; 

    m_pd3dDevice->ClearRenderTargetView(m_pRenderTargetView, afClearColor);
    m_pSwapChain->Present(1, 0);
}

 

Résumé :

Nous avons affiché une fenêtre prête à recevoir le rendu de DirectX.

fenetre

Voici l’archive du code complet pour cette partie : DirectX 10 Tutoriel – Partie 1.zip

Voilà le code et les explications pour initialiser la fenêtre de DirectX 10.
Il reste beaucoup de choses à intégrer pour faire un jeu mais vous saurez au moins comment intégrer DirectX 10.

Références :

– https://takinginitiative.wordpress.com/directx10-tutorials/

– http://www.rastertek.com/tutdx10.html

– DirectX SDK June 2010 Documentation Samples