Этот урок демонстрирует способ программирования вида от 3 лица. На самом деле есть еще немало вещей, связанных с ним, но тут показаны основы.
Сделано всё действительно просто. Вместо вращения вектора взгляда вокруг позиции мы просто вращаем нашу позицию вокруг точки, куда смотрим. Я просто взял RotateView() и поменял в ней местами m_vView и m_vPosition.
Однако, мы используем точку vCenter, так как нам может понадобится вращяться вокруг другой точки, не вектора взгляда. Это расширяемый способ, позволяющий вращаться не только вокруг фиксированной точки.
Файл init.cpp
С прошлого урока ничего не изменилось.
Файл main.h
В описание класса камеры мы добавили функцию RotateAroundPoin().
Это позволило нам вращать камеру вокруг некой точки (В случае игры — вокруг игрового персонажа).
public:
……….
……….
……….
// Функция вращает камеру вокруг точки (например вашего персонажа).
// vCenter — Точка, вокруг которой она вращается.
void RotateAroundPoint(CVector3 vCenter, float angle, float x, float y, float z);
……….
……….
};
В класс камеры мы добавили функцию RotateAroundPoint(). Она принимает точку, вокруг которой требуется вращаться, ось вращения и требуемый угол.
Файл camera.cpp:
/////
///// This rotates the position around a given point
/////
///////////////////////////////// ROTATE AROUND POINT \\\\*void CCamera::RotateAroundPoint(CVector3 vCenter, float angle, float x, float y, float z)
{
CVector3 vNewPosition;// Чтобы вращать нашу позицию вокруг точки, всё, что нам надо — найти вектор
// от нашей позиции к центральной точке вращения. Один раз получив вектор,
// мы вращаемся вокруг точки с заданной скоростью/углом.
// Новый вектор vCenter — точка, вокруг которой мы вращаемся.
// Получим центр, вокруг которого нужно вращатся
CVector3 vPos = m_vPosition — vCenter;
// Вычислим синус и косинус угла
float cosTheta = (float)cos(angle);
float sinTheta = (float)sin(angle);
// Найдем значение X точки вращения
vNewPosition.x = (cosTheta + (1 — cosTheta) * x * x) * vPos.x;
vNewPosition.x += ((1 — cosTheta) * x * y — z * sinTheta) * vPos.y;
vNewPosition.x += ((1 — cosTheta) * x * z + y * sinTheta) * vPos.z;
// И значение Y
vNewPosition.y = ((1 — cosTheta) * x * y + z * sinTheta) * vPos.x;
vNewPosition.y += (cosTheta + (1 — cosTheta) * y * y) * vPos.y;
vNewPosition.y += ((1 — cosTheta) * y * z — x * sinTheta) * vPos.z;
// И Z…
vNewPosition.z = ((1 — cosTheta) * x * z — y * sinTheta) * vPos.x;
vNewPosition.z += ((1 — cosTheta) * y * z + x * sinTheta) * vPos.y;
vNewPosition.z += (cosTheta + (1 — cosTheta) * z * z) * vPos.z;
// Теперь просто прибавим новый вектор к нашей позиции,
// чтобы установить новую позицию камеры.
m_vPosition = vCenter + vNewPosition;
}
Файл main.cpp
Тут мы только изменяем реакцию на input, а также отрисовку сцены.
/////
///// Добавьте эту функцию в файл main.cpp
///// Это обработка клавиш, заменяем ею CheckForMovement()
/////
///////////////////////////////////////////////////////////////////////////
void KeyPressed()
{
if(GetKeyState(VK_UP) & 0x80) { // если нажата «ВВЕРХ»
g_Camera.MoveCamera(kSpeed); // Движемся вперед
}
if(GetKeyState(VK_DOWN) & 0x80) { // Если нажата «ВНИЗ»
g_Camera.MoveCamera(—kSpeed); // Назад
}// Мы сделали два изменения с прошлого урока. Вместо gCamera.RotateView()
// мы используем новую ф-ю RotateAroundPoint(), передавая ей точку
// взгляда камеры.
if(GetKeyState(VK_LEFT) & 0x80) {
// Вращаем вокруг оси Y с положительной скоростью
g_Camera.RotateAroundPoint(g_Camera.m_vView, kSpeed, 0, 1, 0);
}
if(GetKeyState(VK_RIGHT) & 0x80) {
// А тут — с отрицательной
g_Camera.RotateAroundPoint(g_Camera.m_vView, —kSpeed, 0, 1, 0);
}
}///////////////////////////////// CREATE PYRAMID \\\\*
/////
///// Новая функция.
///// Создает пирамиду с центром в X,Y,Z
///// Ширина и высота передаются в ф-ю.
/////
///////////////////////////////// CREATE PYRAMID \\\\*
void CreatePyramid(float x, float y, float z, int width, int height)
{
// Ниже мы создаем пирамиду. Её составляют 4 треугольника по бокам и квадрат внизу.
// 4 треугольника для сторон:
glBegin(GL_TRIANGLES);
// Задняя сторона
glColor3ub(255, 0, 0); glVertex3f(x, y + height, z);
glColor3ub(0, 255, 255); glVertex3f(x — width, y — height, z — width);
glColor3ub(255, 0, 255); glVertex3f(x + width, y — height, z — width);
// Передняя сторона
glColor3ub(255, 0, 0); glVertex3f(x, y + height, z);
glColor3ub(0, 255, 255); glVertex3f(x + width, y — height, z + width);
glColor3ub(255, 0, 255); glVertex3f(x — width, y — height, z + width);
// Левая сторона
glColor3ub(255, 0, 0); glVertex3f(x, y + height, z);
glColor3ub(255, 0, 255); glVertex3f(x — width, y — height, z + width);
glColor3ub(0, 255, 255); glVertex3f(x — width, y — height, z — width);
// Правая сторона
glColor3ub(255, 0, 0); glVertex3f(x, y + height, z);
glColor3ub(255, 0, 255); glVertex3f(x + width, y — height, z — width);
glColor3ub(0, 255, 255); glVertex3f(x + width, y — height, z + width);
glEnd();
// Теперь дно пирамиды
glBegin(GL_QUADS);
glColor3ub(0, 0, 255); glVertex3f(x — width, y — height, z + width);
glColor3ub(0, 0, 255); glVertex3f(x + width, y — height, z + width);
glColor3ub(0, 0, 255); glVertex3f(x + width, y — height, z — width);
glColor3ub(0, 0, 255); glVertex3f(x — width, y — height, z — width);
glEnd();
}
///////////////////////////////// DRAW 3D GRID \\\\*
/////
///// Ещё одна новая функция.
///// Она создаёт простую сетку для лучшего восприятия анимации
/////
///////////////////////////////// DRAW 3D GRID \\\\*
void Draw3DSGrid()
{
// Просто рисуем по 100 зеленых линий вертикально и горизонтально по осям X и Z
glColor3ub(0, 255, 0);
// Рисуем сетку 1х1 вдоль осей
for(float i = —50; i <= 50; i+=1)
{
glBegin(GL_LINES);
// Ось Х
glVertex3f(—50, 0, i);
glVertex3f(50, 0, i);
// Ось Z
glVertex3f(i, 0, —50);
glVertex3f(i, 0, 50);
glEnd();
}
}
///////////////////////////////// RENDER SCENE \\\\*
/////
///// Отрисовка сцены теперь выглядит так:
/////
///////////////////////////////// RENDER SCENE \\\\*
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
//Скормим OpenGL данные камеры
gluLookAt(g_Camera.m_vPosition.x, g_Camera.m_vPosition.y, g_Camera.m_vPosition.z,
g_Camera.m_vView.x, g_Camera.m_vView.y, g_Camera.m_vView.z,
g_Camera.m_vUpVector.x, g_Camera.m_vUpVector.y,g_Camera.m_vUpVector.z);
//Рисуем сетку
Draw3DSGrid();
// Создаем 4 пирамиды, просто для ориентирования в мире
CreatePyramid(—6, 3, 6, 1, 6);
CreatePyramid(6, 3, 6, 1, 6);
CreatePyramid(6, 3, —6, 1, 6);
CreatePyramid(—6, 3, —6, 1, 6);
// Переместим «персонажа» в точку нашего взгляда. Таким образом
// мы всегда будем смотреть на обьект, а обьект всегда будет перед нами,
// куда бы мы не передвигались.
glTranslatef(g_Camera.m_vView.x, 0, g_Camera.m_vView.z);
// Теперь создаем 2 пирамиды, одна из которых перевернута.
CreatePyramid(0, 2, 0, 1, 1);
glRotatef(180, 1, 0, 0);
CreatePyramid(0, —1, 0, 1, 1);
//*************************************************************************
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho( 0,SCREEN_WIDTH,SCREEN_HEIGHT, 0,0, 1 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//*************************************************************************
CalculateFrameRate();
glDisable(GL_DEPTH_TEST);
glColor3f(1.0f,1.0f,1.0f);
wchar_t buf[256];
swprintf(buf, sizeof(buf), L«Текущие FPS: %f», FPS);
Font->Print(10,20, buf); // Выводим текст
glEnable(GL_DEPTH_TEST);
//*************************************************************************
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
//*************************************************************************
SwapBuffers(g_hDC);
}
// Ну и последнее, в функции MainLoop() замените вызов CheckForMovement() на KeyPressed().
Вот и всё. Теперь, скомпилировав программу, вы сможете клавишами влево и вправо вращаться вокруг «персонажа», а клавишами вперед и назад — перемещать его =)