Рассматривая примеры, можно заметить, что значительная часть кода не имеет привязки к конкретной программе и повторяется от примера к примеру. Чтобы избежать повторов, напишем класс-обертку 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();
}
В результате работы программы получаем точно такую же сферу, что и в предыдущем примере.
Файлы main.cpp
, app.h
и app.cpp
будут отвечать за визуализацию результатов моделирования во всех наших следующих примерах. Кроме того, их можно использовать независимо от физического движка, в качестве каркаса приложения, основанного на OpenGL и GLUT.
Комментарии
comments powered by Disqus