Вывод графики - первые очертания

Графические ресурсы

Простите, но другого названия для раздела я придумать не смог. Согласен, что звучит довольно туманно, поэтому постараюсь все сразу объяснить. Во-первых, нам известно, что для того чтобы вывести что-нибудь на экран, в Direct3D используются вершины, индексные буферы, текстуры и т.д. Можно было пойти по пути наименьшего сопротивления и написать по классу для каждого из них. Но тогда в программе пришлось бы определять по объекту класса для каждой текстуры или буфера вершин, что бывает не совсем удобно. Поэтому волевым решением решил я ввести в движок новое понятие - индентификаторы. Что они из себя представляют?

Индентификатор - это простое целое число, типа int, которое содержит уникальный номер для каждого графического ресурса: буфер вершин, текстура, эффект и т.д. Чтобы получить индентификатор, достаточно лишь вызвать функцию загрузки ресурса, например:

idTexture = engine->LoadTexture("skin.tga");

Потом, вся дальнейшая работа с этой тестурой будет производится через ее индентификатор.

Реализация

Начал я с того, что определил шаблонный класс стека. Вообще, стек - полезная вещь в любой программе, а создание именно шаблонного класса позволит нам хранить в нем любые типы данных.

/*--------------------------------------------------------------
tStack

Шаблонный класс стека
--------------------------------------------------------------*/
template class TENGINE_DLL tStack
{
public:
    tStack(int size);
    virtual ~tStack();
    //
    T PopStack(void);
    void PushStack(T stackElement);
protected:
    int m_size; // Размер или глубина стека
    T* m_top; // Указатель на начало стека
    T* m_current; // Указатель на текущий элемент стека
};

/*--------------------------------------------------------------
tStack::tStack()

Конструктор, инициализирующий стек
--------------------------------------------------------------*/
template tStack::tStack(int size)
{
    m_size = size;
    m_top = m_current = new T[m_size];
};

/*--------------------------------------------------------------
tStack::~tStack()

Деструктор
--------------------------------------------------------------*/
template tStack::~tStack()
{
    delete[] m_top;
};

/*--------------------------------------------------------------
tStack::PopStack()

Извлечь из стека
--------------------------------------------------------------*/
template T tStack::PopStack(void)
{
    if(m_current!=m_top)
        m_current--;
    return *m_current;
};

/*--------------------------------------------------------------
tStack::PushStack()

Занести в стек
--------------------------------------------------------------*/
template void tStack::PushStack(T stackElement)
{
    if(m_current<=(m_top+m_size-1))
    {
        *m_current = stackElement;
        m_current++;
    }
    else
        *(m_current-1) = stackElement;
};

Как видно, класс прост до безобразия. Применим его в менджере текстур - новом классе, работающим с текстурами.

/*--------------------------------------------------------------
tTextures

Класс менеджера текстур
--------------------------------------------------------------*/
class TENGINE_DLL tTextures : virtual public tD3D
{
public:
    tTextures();
    virtual ~tTextures();
    //
    int LoadTexture(const char* name);
    void SetTexture(int id,int stage);
    void ReleaseTexture(int id);
protected:
    LPDIRECT3DTEXTURE8 m_textures[TTEXTURESMAXCOUNT]; // Массив текстур
    tStack* m_indexStack; // Стек индексов массива
};

В менеджере хранится массив из указателей на текстуры Direct3D и организован стек для динамического управления этим массивом. Как это происходит:

  1. В конструкторе класса стек заполняется индексами от TTEXTURESMAXCOUNT до 1. Предполагается, что все ячейки массива свободны.
  2. При загрузке текстуры функцией LoadTexture() из стека берется индекс ячейки массива, которая свободна, и этот индекс возвращается программисту в качестве индентификатора этой текстуры.
  3. Если текстура удаляется (ReleaseTexture()),то в стек заносится индекс освободившейся ячейки массива.

Для полной ясности посмотрите на реализацию функции LoadTexture():

/*--------------------------------------------------------------
tTextures::LoadTexture()

Загрузить текстуру из файла
--------------------------------------------------------------*/
int tTextures::LoadTexture(const char* name)
{
    HRESULT hResult;

    // Извлекаем номер индекса из стека
    int id = m_indexStack->PopStack();
    // Загружаем текстуру
    hResult = D3DXCreateTextureFromFileEx(m_d3dDevice,
        name,
        D3DX_DEFAULT,D3DX_DEFAULT,D3DX_DEFAULT,
        0,
        D3DFMT_UNKNOWN,
        D3DPOOL_MANAGED,
        D3DX_DEFAULT,
        D3DX_DEFAULT,
        0,
        NULL,
        NULL,
        &m_textures[id-1]);
    if(hResult!=D3D_OK)
        throw tError(__FILE__,__LINE__,"tTextures::LoadTexture",
                        "Fail to load texture! / name - %s (%s)",name,DXGetErrorString8(hResult));
    return id;
}

Структура движка

Не в первый и в непоследний раз меняется наш движок.

Как видно добавились новые классы: tTextures, tVertexBuffers, tIndexBuffers, tEffects, которые являются менеджерами соответствующих графических ресурсов, о которых говорилось в самом начале.

Пример

В качестве примера, решил я показать вывод двух квадратов. Вы уж меня простите, что я опустился до такого простейшего уровня, хотел продемострировать то, как теперь легко работать с движком.

/*--------------------------------------------------------------
Главная функция игры
--------------------------------------------------------------*/
void Main(void)
{
    try
    {
        engine->InitRender(100,100,400,300);
        //
        vb1 = engine->LoadVertexBuffer(&vertices,sizeof(vertices));
        effect1 = engine->LoadEffect("Base\\Effects\\first.eff");
        //
        engine->SetEngineThread(Render);
    }
    catch(tError &error)
    {
        error.AddErrorHistory(" <-thread 0x%x",&Main);
        throw;
    }
}

Во-первых, в функции Main() загружаем наши ресурсы. А в Render(), организовываем вывод.

/*--------------------------------------------------------------
Вывод
--------------------------------------------------------------*/
void Render(void)
{
    engine->BeginRender();
    {
        SetMatrices();
        engine->BeginEffect(effect1);
        {
            engine->PassEffect(effect1,0);
            engine->SetVertexBuffer(vb1,0);
            engine->DrawRender(4);
        }
        engine->EndEffect(effect1);
    }
    engine->EndRender();
}

В итоге получаем вот это:

Сайт создан в системе uCoz