Рассматривая примеры, можно заметить, что значительная часть кода не имеет привязки к конкретной программе и повторяется от примера к примеру. Чтобы избежать повторов, напишем класс-обертку Application над основными функциями GLUT, и сделаем методы этого класса виртуальными. Тогда, для каждого нового приложения нужно создавать подкласс Application, в который будут добавляться новые методы (или переопределяться имеющихся), оставляя основу неизменной.

Следующая программа рисует сферу. По сути, она отличается от рассмотренных ранее примеров только тем, что основные графические операции в ней вынесены в функции. Возьмем эту программу за основу, и переработаем, введя класс-обертку Application.

#include <GL/glut.h>

// Размеры окна
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 320;

void initGraphics()
{
    glClearColor(0.9f, 0.95f, 1.0f, 1.0f);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_SMOOTH);
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 1.0,  0.0, 0.0, 0.0,  0.0, 1.0, 0.0);

    glColor3f(0.0f, 0.0f, 0.0f);
    glutSolidSphere(0.1f, 50, 50);

    glutSwapBuffers();
}

void setProjectionMatrix(int w, int h)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (double)w/(double)h, 1.0, 500.0);
}

void setModelviewMatrix()
{
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void reshape(int width, int height)
{
    if (height <= 0) height = 1;

    glViewport(0, 0, width, height);

    setProjectionMatrix(width, height);
    setModelviewMatrix();
}

void update()
{
    glutPostRedisplay();
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);

    // #1: Инициализация и создание окна GLUT
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("Sphere");

    initGraphics();

    // #2: Регистрация функций-обработчиков событий
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutIdleFunc(update);

    // #3: Запуск основного цикла GLUT
    glutMainLoop();
}

Результат работы программы.

Предположим, что класс Application уже создан. Запишем основной файл, использующий этот класс.

main.cpp

#include <GL/glut.h>
#include "engine/app.h"    // включаем каркас приложения

// Размеры окна
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 320;

// Функция, возвращающая указатель на подкласс Application
// Должна быть реализована в .cpp файле подкласса
extern Application* getApplication();

// Глобальный объект Application
Application* app;

void createWindow(const char* title)
{
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(WINDOW_WIDTH,WINDOW_HEIGHT);
    glutInitWindowPosition(0,0);
    glutCreateWindow(title);
}

void update()
{
    app->update();
}

void display()
{
    app->display();

    glutSwapBuffers();
}

void reshape(int width, int height)
{
    app->resize(width, height);
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);

    // #1: Инициализация и создание окна GLUT
    app = getApplication();
    createWindow(app->getTitle());

    app->initGraphics();

    // #2: Регистрация функций-обработчиков событий
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glutIdleFunc(update);

    // #3: Запуск основного цикла GLUT
    glutMainLoop();
}

Как видно, основная работа по рисованию (функции display(), reshape() и update()) переложена "на плечи" объекта класса Application (или его наследников).

Функция getApplication(), возвращающая указатель на подкласс Application, введена затем, чтобы сосредоточить все изменения в классе-наследнике Application. Иначе, пришлось бы каждый раз переписывать main.cpp, указывая вызов объекта нового класса в этом месте:

    // #1: Инициализация и создание окна GLUT
    app = getApplication();

Функция getApplication() должна быть записана в файле реализации подкласса.

Наследником класса Application в нашем случае будет класс Sphere, задачи которого сводятся к заданию заголовка окна и отображению сферы.

sphere.cpp

#include <GL/glut.h>
#include "engine/app.h"

class Sphere : public Application
{
public:

    virtual const char* getTitle() { return "Sphere"; }
    virtual void display();
};

void Sphere::display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 1.0,  0.0, 0.0, 0.0,  0.0, 1.0, 0.0);

    glColor3f(0.0f, 0.0f, 0.0f);
    glutSolidSphere(0.1f, 50, 50);
}

// Вызывается из main.cpp для создания нового объекта
Application* getApplication()
{
    return new Sphere();
}

Запишем, наконец, класс Application.

app.h

#ifndef APP_H
#define APP_H

class Application
{
protected:

    int height;
    int width;

public:

    virtual const char* getTitle();
    virtual void initGraphics();
    virtual void setProjectionMatrix(int w, int h);
    virtual void setModelviewMatrix();
    virtual void display();
    virtual void update();
    virtual void resize(int width, int height);
};

#endif // APP_H

app.cpp

#include <GL/glut.h>
#include "app.h"

void Application::initGraphics()
{
    glClearColor(0.9f, 0.95f, 1.0f, 1.0f);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_SMOOTH);
}

void Application::setProjectionMatrix(int w, int h)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (double)w/(double)h, 1.0, 500.0);
}

void Application::setModelviewMatrix()
{
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void Application::display()
{
}

const char* Application::getTitle()
{
    return "Application";
}

void Application::update()
{
    glutPostRedisplay();
}

void Application::resize(int width, int height)
{
    if (height <= 0) height = 1;

    Application::width = width;
    Application::height = height;
    glViewport(0, 0, width, height);

    setProjectionMatrix(width, height);
    setModelviewMatrix();
}

В результате работы программы получаем точно такую же сферу, что и в предыдущем примере.

Сфера, нарисованная программой, использующей класс Application.

Файлы main.cpp, app.h и app.cpp будут отвечать за визуализацию результатов моделирования во всех наших следующих примерах. Кроме того, их можно использовать независимо от физического движка, в качестве каркаса приложения, основанного на OpenGL и GLUT.

Скачать файлы



Комментарии

comments powered by Disqus