Академический Документы
Профессиональный Документы
Культура Документы
Учреждение образования
«Гомельский государственный университет
имени Франциска Скорины»
Факультет математический
Кафедра вычислительной математики и программирования
Допущена к защите
Зав. кафедрой______________ Лубочкин А.В.
(подпись)
«____»_________________ 2009 г.
Дипломная работа
Исполнитель:
студент группы ПО-52 ________________ Демусев К. А.
шифр и номер группы подпись
Научный руководитель:
к.ф.-м.н., доцент ________________ Ружицкая Е. А.
подпись
Рецензент:
к.ф.-м.н., доцент ________________ Фамилия И. О.
подпись
Гомель 2009
2
СОДЕРЖАНИЕ
_
Введение...................................................................................................................3
П.Б Результаты работы программы.....................................................................77
3
ВВЕДЕНИЕ
При программировании графических приложений наибольшее внимание
должно быть уделено скорости обработки графики, а также современным
тенденциям в написании таких программных продуктов, для чего
используются унифицированные библиотеки обработки графики, одной из
которых является библиотека DirectX. Данная библиотека используется
преимущественно для написания видеоигр, использующих современные
возможности видеоадаптеров, что свидетельствует о рациональности
применения этой библиотеки при разработке графических приложений
любой сложности.
Написание сложных графических приложений при прямом обращении к
интерфейсам библиотеки DirectX является практически невозможным,
поэтому необходимо перед программированием таких приложений
разработать набор библиотек, предоставляющих основные функции по
обработке отдельных объектов трехмерной графики. Такой набор библиотек
называется ядром графического приложения (в англоязычной терминологии
используется название «graphic engine»). Существует большое количество
готовых библиотек, предоставляющих огромные возможности
программирования видеоигр и других мультимедийных приложений.
Целью данной работы является построение собственного графического ядра
без использования готовых работ третьих производителей. Графическое ядро
должно содержать большинство основных функций, необходимых при
написании видеоигр, и должно быть максимально оптимизировано в плане
производительности.
Среди основных функций должны быть следующие: создание и
инициализация графической составляющей приложения, обработка и
отображение двумерной графики, работа с виртуальной камерой, работа с
устройствами ввода, обработка и отображение трехмерных статических
объектов, обработка и отображение трехмерных анимированных объектов,
работа с графическими шейдерами, и quad-деревьями.
Технология шейдеров используется как при написании современных
видеоигр, так и при создании демонстрационных сцен, при моделировании
физических и химических процессов и при разработке сложных
программных комплексов, целью которых является отображение
графических объектов в наиболее реалистичном виде. Расчет освещения одна
из наиболее часто используемых возможностей шейдерных программ.
На данную работу были поставлены следующие задачи:
1 Изучить общие приемы работы с библиотекой DirectX в целом, ее
графическим компонентом Direct3D и функциями, ускоряющими реализацию
комбинированных математических алгоритмов.
2 Разработать основу графического ядра представляющую возможности
быстрой инициализации приложений, построенных на основе использования
трехмерной графики, выводу основных графических примитивов, загрузки и
отображения сложных трехмерных объектов из файла, позиционирования
4
2 РАЗРАБОТКА БИБЛИОТЕКИ-ЯДРА
При написании сложных мультимедийных приложений недостаточно
использования одной только библиотеки, предоставляющей
унифицированный способ взаимодействия с графическим конвейером.
Необходимо разработать библиотеку, являющуюся ядром разрабатываемого
приложения (в англоязычном варианте ядро графического приложения
называется Engine). Данная библиотека предоставит следующие
возможности:
– создание и инициализация графической составляющей приложения;
– обработка и отображение двумерной графики;
– работа с виртуальной камерой;
– работа с устройствами ввода (клавиатурой и мышью);
– обработка и отображение трехмерных статических объектов;
– обработка и отображение трехмерных анимированных объектов;
– работа со счетчиком времени для использования при анимации;
– отображение текстовой информации;
– работа с графическими шейдерами;
– работа с quad-деревьями;
– работа с ландшафтами;
– вспомогательные математические функции.
При разработке такой библиотеки ей было присвоено название Eleky,
исходный код которой представлен в приложении A. В библиотеке
присутствуют следующие классы:
– CEleky, предназначенный для инициализации приложения;
– CElekyCamera, выполняющий функции по работе со свободной камерой;
– CEleky3rdPersCamera, обеспечивающий работу камеры с видом от третьего
лица
– СElekySprite для работы с двухмерной графикой;
– CElekyText разработанный для отображения текстовой информации;
– CElekyTerrain выполняющий работу с ландшафтами, основанными на карте
высот;
– CElekyMesh для работы с объектами, хранящимися в файлах .x;
– СElekyTiming обеспечивающий синхронизацию по времени;
– CElekyCulling оптимизирующий обработку и вывод графики;
– и некоторые другие вспомогательные классы.
Для унификации интерфейса элементов разработанной библиотеки каждый
из классов при необходимости имеет функцию init, необходимую для
загрузки и инициализации данных объекта, возвращающую переменную типа
bool, принимающую значение true в случае успешной инициализации и false
в случае неудачи, а также функцию update, предназначенную для обновления
сведений об объекте, и функцию render, вызываемую при необходимости
отрисовки объекта в графический конвейер. Конструктор каждого из классов
принимает как минимум указатель на объект IDirect3dDevice9,
12
RECT src;
src.left = fontBuffer[strText[i]] * iLetterWidth;
src.top = 0;
src.right = (fontBuffer[strText[i]]+1) * iLetterWidth;
src.bottom = iLetterHeight;
pFont->render(&dest, &src);
x += iLetterWidth;
if(x >= iWidth)
15
{
x = 0;
y += iLetterHeight;
}
}
}
float vy = C - A; // A->C
height = A + Lerp(0.0f, uy, dx) + Lerp(0.0f, vy, dz);
}
else // нижний треугольник DCB
{
float uy = C - D; // D->C
float vy = B - D; // D->B
height = D + Lerp(0.0f, uy, 1.0f - dx) + Lerp(0.0f, vy, 1.0f -
dz);
}
return height;
}
inline float CElekyTerrain::Lerp(float a, float b, float t)
return a - (a*t) + (b*t);
2.6 Шейдеры
D3DXMATRIX matpos;
D3DXMatrixTranslation (&matpos, 0.0f, landHeight, 0.0f);
matView = matViewX*matViewY*matViewT;
matView *= matpos;
2.9 Оптимизация
Каждый из участков дорог имеет размеры 10 на 10, так как каждая ячейка
ландшафта имеет размеры 10 на 10. При разработке было принято, что одна
ячейка ландшафта соответствует размерам 10 на 10 метров реального мира.
Принимать определенный масштаб при разработке графических приложений
является хорошей практикой, потому что позволяет впоследствии
разрабатывать графические объекты соответствующих пропорций.
Метод init класса CPassRoads загружает последовательно объекты участков
дорог, затем читает файл карты дорог, который представляет набор
координат, перечисленных через пробел. Формат файла карты дорог имеет
следующую структуру:
N
X1 Z1 T1
X2 Z2 T2
42
…
XN ZN TN
где N – количество участков дорог, X и Z – координаты участка дорог с
округлением до 10 (так как каждый участок распложен в центре ячейки
ландшафта), T – тип участка дороги.
Типы участка дороги могут быть следующими: 1 – вертикальный участок, 2 –
горизонтальный участок, 3 – поворот с вертикального участка на 90 градусов
вправо, 4 – поворот с горизонтального участка на 90 градусов влево, 5 –
поворот с горизонтального участка на 90 градусов вправо, 6 – поворот с
вертикального участка на 90 градусов влево, 13-16 – Т-образные перекрестки,
17 – X-образный перекресток (рисунок 3.5).
При разработке было принято решение о том, что участки дорог с типами 3-
17 могут лежать только горизонтально (иметь угол поворота относительно
оси X ноль градусов), поэтому после загрузки сети дорог необходимо
выровнять карту высот и соответственно ландшафт на участках с типами 3-
17. Для того чтобы выровнять ландшафт по карте дорог, необходимо
отсортировать по убыванию высоты тайтлы, установив сначала те, которые
выше. Выравнивание ландшафта по карте дорог и установка углов поворота
по оси X для дороги типа 1-2 производится в функции update, вызываемой
для каждого из участков (листинг 3.2).
h2 = h;
}
float r = fabs(h2-h);
float sgn = h2 > h ? -1.0f : 1.0f;
float a = (float)atan( r / fCellSize);
pStraightMesh->IdentityMatrices();
pStraightMesh->SetScale(1.0f, 1.0f, sqrtf(fCellSize * fCellSize + r * r)
/ fCellSize);
pStraightMesh->RotateX(sgn*a);
if(vRoads[i].type == 2)
pStraightMesh->RotateY( D3DX_PI / 2.0f );
if(vRoads[i].type == 3 || vRoads[i].type == 15)
pStraightMesh->RotateY( D3DX_PI / 2.0f );
if(vRoads[i].type == 4 || vRoads[i].type == 14)
pStraightMesh->RotateY( - D3DX_PI / 2.0f );
if(vRoads[i].type == 5 || vRoads[i].type == 13)
pStraightMesh->RotateY( D3DX_PI );
pStraightMesh->SetPosition((vRoads[i].x-((pTerrain->nCols-1)/2.0f)) *
fCellSize,
((h+h2)/2.0f),
(vRoads[i].y - ((pTerrain->nRows - 1) /
2.0f)) * fCellSize);
pStraightMesh->Update();
pMatWorld[i] = pStraightMesh->GetMatWorld();
}
Класс CPassCar содержит методы init, update и render. Метод init вызывается
один раз только для загрузки модели автомобиля через класс CElekyMesh,
загрузки карты путей автомобиля и позиционирования автомобиля на дороге.
Карта путей автомобиля содержится в файле, имеющим следующую
структуру:
N
X1 Y1
X2 Y2
...
XN YN
XP YP ZP NP
где, N – количество ключевых точек, X и Y координаты каждой из ключевых
точек деленные на 10, чтобы соответствовать координатам высот на карте
высот. XP, YP, ZP – первоначальные координаты автомобиля в мировых
координатах. NP – первоначальное направление автомобиля (рисунок 3.6).
В каждой клетке ландшафта автомобиль имеет определенное направление,
хранимое в переменной-члене класса iDirOfCar, может принимать значения,
определяемые текущее направление автомобиля в пространстве (рисунок
3.9).
ЗАКЛЮЧЕНИЕ
В результате выполненной работы были разработаны библиотеки,
позволяющие на их основе создавать высокопроизводительные современные
приложения для обработки трехмерной графики. Примером таких
приложений могут быть видеоигры. Данные библиотеки содержат решения
проблем обработки трехмерной графики используя интерфейсы DirectX. Все
решения представляют в полном объеме современные технологии
обработки, трансформации, анимации и оптимизации объектов трехмерной
графики, такие как: работа с картами высот, генерирование ландшафтов,
работа со статическими объектами, шейдерами, скелетной анимацией,
виртуальной камерой и другие технологии.
Также в ходе выполнения работы была разработана видеоигра «Симулятор
дорожного движения», которая построена на основе разработанного ядра и
реализует большинство его возможностей. Видеоигра предназначена как для
демонстрации разработанных в библиотеках ядра функций, так и для
использования пользователями в развлекательных целях. Моделирование
объектов для приложения осуществлялось в программе 3ds max, однако
разработанное графическое ядро поддерживает формат объектов .x, который
широко распространен и может быть получен из многих других редакторов.
При разработке особое внимание было уделено возможности разделения
трехмерных пространств на отдельные части и быстрого ориентирования в
них для реализации функций отображения большого числа объектов на
любых расстояниях относительно независимо от возможностей аппаратного
обеспечения.
В основе разработанного приложения лежит большое количество
математических алгоритмов, решений проблем оптимизации и решений
начальных физических задач, что может быть использовано как при
дальнейшем усовершенствовании приложения, так и при разработке других
приложений. При разработке был максимально использован объектно-
ориентированный подход, что позволит эффективно как дополнять
разработанные библиотеки и приложение, так и использовать каждый из
компонентов в других приложениях.
В работе показана эффективность применения интерфейсов библиотеки
DirectX и разработанных на ее основе классов ядра графических программ
для решения сложных задач по обработке трехмерной графики. Результатом
грамотного внедрения библиотеки DirectX и разработанного ядра при
написании графических приложений является эффективное использование
ресурсов компьютера, уменьшение времени, затрачиваемого на разработку
подобных приложений, а также, что немаловажно, следование современным
тенденциям в программировании графических приложений. Разработанное
ядро может применяться при программировании как мультимедийных
приложений, требующих быстрой обработки трехмерной графики, так и
видеоигр различной сложности.
52
Приложение А
Листинг программы
#include "ElekyTerrain.h"
CElekyTerrain::CElekyTerrain(LPDIRECT3DDEVICE9 pd3dDevice,
int nCols,
int nRows,
int iSpacing,
float fHeightScale) :
terrainFVF(D3DFVF_XYZ | D3DFVF_TEX1)
{
this->pd3dDevice = pd3dDevice;
this->nVertsPerRow = nCols + 1;
this->nVertsPerCol = nRows + 1;
this->nCols = nCols;
this->nRows = nRows;
this->fHeightScale = fHeightScale;
D3DXMatrixIdentity (&matWorld);
}
CElekyTerrain::~CElekyTerrain(void)
{
}
if(!computeVertices())
return false;
if(!computeIndices())
return false;
54
if(strTexture)
{
if(!loadTexture(strTexture))
return false;
}
return true;
}
if(FAILED(hr))
return false;
return true;
}
if(inFile == 0)
return false;
inFile.read((char*)&in[0], // буффер
in.size()); // количество читаемых в буфер байт
inFile.close();
return true;
}
if(col < 0 || row < 0 || col > nCols || row > nRows)
return 0;
// A B
// *---*
// | / |
// *---*
// C D
float A = (float)getHeightmapForLerp((int)row, (int)col);
float B = (float)getHeightmapForLerp((int)row, (int)col+1);
float C = (float)getHeightmapForLerp((int)row+1, (int)col);
float D = (float)getHeightmapForLerp((int)row+1, (int)col+1);
float dx = x - col;
float dz = z - row;
float height;
if(dz < 1.0f - dx) // верхний треугольник ABC
{
float uy = B - A; // A->B
float vy = C - A; // A->C
return height;
}
bool CElekyTerrain::computeVertices()
{
HRESULT hr = 0;
hr = pd3dDevice->CreateVertexBuffer(nVertices * sizeof(TerrainVertex),
D3DUSAGE_WRITEONLY,
terrainFVF,
D3DPOOL_MANAGED,
56
&pVertexBuffer,
0);
if(FAILED(hr))
return false;
TerrainVertex* v = NULL;
pVertexBuffer->Lock(0, 0, (void**)&v, 0);
int i = 0;
for(int z = startZ; z >= endZ; z -= iSpacing)
{
int j = 0;
for(int x = startX; x <= endX; x += iSpacing)
{
int index = i * nVertsPerRow + j;
v[index].x = (float)x;
v[index].y = (float)vHeightmap[index];
v[index].z = (float)z;
//v[index].rhw = 1.0f;
v[index].u = (float)j * 1,
v[index].v = (float)i * 1,
j++;
}
i++;
}
pVertexBuffer->Unlock();
return true;
}
TerrainVertex* v = NULL;
pVertexBuffer->Lock(0, 0, (void**)&v, 0);
int index = 0;
for(int i = 0; i <= nRows; i++)
{
for(int j = 0; j <= nCols; j++)
{
D3DXVECTOR3 s1, s2, s3;
57
pVertexBuffer->Unlock();
return false;
}
bool CElekyTerrain::computeIndices()
{
HRESULT hr = 0;
hr = pd3dDevice->CreateIndexBuffer(nTriangles * 3 * sizeof(WORD), // 3
индекса на треугольник
D3DUSAGE_WRITEONLY,
D3DFMT_INDEX16,
D3DPOOL_MANAGED,
&pIndexBuffer,
0);
58
if(FAILED(hr))
return false;
baseIndex += 6;
}
}
pIndexBuffer->Unlock();
return true;
}
void CElekyTerrain::render()
{
pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
pd3dDevice->SetStreamSource( 0, pVertexBuffer, 0,
sizeof(TerrainVertex) );
pd3dDevice->SetFVF( terrainFVF );
pd3dDevice->SetIndices( pIndexBuffer );
pd3dDevice->SetTexture( 0, pTexture );
pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST,
0,
0,
nVertices,
0,
nTriangles );
#include "ElekyCulling.h"
miny = 0;
maxy = 255;
CElekyCulling::~CElekyCulling(void)
{
deleteTree(pTopNode);
delete [] pNodes;
}
// Right plane
vFrustum[1].a = matViewProj._14 - matViewProj._11;
vFrustum[1].b = matViewProj._24 - matViewProj._21;
vFrustum[1].c = matViewProj._34 - matViewProj._31;
vFrustum[1].d = matViewProj._44 - matViewProj._41;
// Top plane
vFrustum[2].a = matViewProj._14 - matViewProj._12;
vFrustum[2].b = matViewProj._24 - matViewProj._22;
vFrustum[2].c = matViewProj._34 - matViewProj._32;
vFrustum[2].d = matViewProj._44 - matViewProj._42;
// Bottom plane
vFrustum[3].a = matViewProj._14 + matViewProj._12;
vFrustum[3].b = matViewProj._24 + matViewProj._22;
vFrustum[3].c = matViewProj._34 + matViewProj._32;
vFrustum[3].d = matViewProj._44 + matViewProj._42;
// Near plane
vFrustum[4].a = matViewProj._13;
vFrustum[4].b = matViewProj._23;
vFrustum[4].c = matViewProj._33;
vFrustum[4].d = matViewProj._43;
// Far plane
vFrustum[5].a = matViewProj._14 - matViewProj._13;
vFrustum[5].b = matViewProj._24 - matViewProj._23;
vFrustum[5].c = matViewProj._34 - matViewProj._33;
vFrustum[5].d = matViewProj._44 - matViewProj._43;
}
pNode->pBranches[0] = NULL;
pNode->pBranches[1] = NULL;
pNode->pBranches[2] = NULL;
pNode->pBranches[3] = NULL;
void CElekyCulling::fillTree()
{
for(int i = -nCells / 2; i < nCells / 2; i++)
{
for(int j = -nCells / 2; j < nCells / 2; j++)
{
int index = (i + nCells / 2) * nCells + (j + nCells / 2);
pNodes[index] = getPointer(i * fNodeSize + fNodeSize / 2.0f,
j * fNodeSize +
fNodeSize / 2.0f,
pTopNode);
if(!pNodes[index])
int t = 2;
}
}
}
return pNode;
}
return NULL;
}
return false;
}
return true;
}
if(in(pNode->pBranches[0]))
passTreeToRender(pNode->pBranches[0]);
if(in(pNode->pBranches[1]))
passTreeToRender(pNode->pBranches[1]);
if(in(pNode->pBranches[2]))
passTreeToRender(pNode->pBranches[2]);
if(in(pNode->pBranches[3]))
passTreeToRender(pNode->pBranches[3]);
}
else
{
for(size_t i = 0; i < pNode->pObjects->size(); i++)
{
CElekyMesh *pMesh = (CElekyMesh *)pNode->pObjects->at(i);
if(!pMesh->rendered())
pMesh->render();
}
}
}
void CElekyCulling::render()
{
passTreeToRender(pTopNode);
passTreeAfterRender(pTopNode);
}
}
}
}
CElekySprite::CElekySprite(LPDIRECT3DDEVICE9 pd3dDevice)
{
this->pd3dDevice = pd3dDevice;
pSprite = NULL;
}
CElekySprite::~CElekySprite(void)
{
if(pSprite)
pSprite->Release();
}
hr = D3DXCreateSprite(pd3dDevice, &pSprite);
if(FAILED(hr))
return false;
hr = D3DXCreateTextureFromFileEx(pd3dDevice, strFile,
D3DX_DEFAULT,
D3DX_DEFAULT,
D3DX_DEFAULT,
NULL,
D3DFMT_UNKNOWN,
D3DPOOL_MANAGED,
D3DX_FILTER_NONE,
//D3DX_DEFAULT,
D3DX_DEFAULT,
dwTranspColor,
NULL,
NULL,
&pTexture);
64
if(FAILED(hr))
return false;
return true;
}
pSprite->End();
}
pSprite->End();
}
pSprite->End();
}
#include "PassRoad.h"
pStraightMesh = NULL;
pRoundMesh = NULL;
pTMesh = NULL;
pXMesh = NULL;
vRoads = NULL;
pMatWorld = NULL;
iTotalRoads = 0;
fCellSize = (float)pTerrain->iSpacing;
}
CPassRoad::~CPassRoad(void)
{
if(pStraightMesh)
65
delete pStraightMesh;
if(pRoundMesh)
delete pRoundMesh;
if(pTMesh)
delete pTMesh;
if(pXMesh)
delete pXMesh;
if(vRoads)
delete [] vRoads;
if(pMatWorld)
delete [] pMatWorld;
}
fclose(file);
VROAD v = vRoads[i];
vRoads[i] = vRoads[i+1];
vRoads[i+1] = v;
t = true;
}
}
}
float CPassRoad::getMaxTerHeight(int i)
{
float h = (float)pTerrain->getHeightmapEntry(vRoads[i].y, vRoads[i].x);
if(h < (float)pTerrain->getHeightmapEntry(vRoads[i].y + 1, vRoads[i].x))
h = (float)pTerrain->getHeightmapEntry(vRoads[i].y + 1,
vRoads[i].x);
if(h < (float)pTerrain->getHeightmapEntry(vRoads[i].y, vRoads[i].x + 1))
h = (float)pTerrain->getHeightmapEntry(vRoads[i].y, vRoads[i].x +
1);
if(h < (float)pTerrain->getHeightmapEntry(vRoads[i].y + 1, vRoads[i].x +
1))
h = (float)pTerrain->getHeightmapEntry(vRoads[i].y + 1,
vRoads[i].x + 1);
return h;
}
void CPassRoad::update(int i)
{
float h, h2;
if(vRoads[i].type == 1)
{
h = pTerrain->getHeightmapEntry(vRoads[i].y, vRoads[i].x) >
pTerrain->getHeightmapEntry(vRoads[i].y, vRoads[i].x+1) ?
(float)pTerrain->getHeightmapEntry(vRoads[i].y, vRoads[i].x)
:
(float)pTerrain->getHeightmapEntry(vRoads[i].y,
vRoads[i].x+1);
h2 = pTerrain->getHeightmapEntry(vRoads[i].y + 1, vRoads[i].x) >
pTerrain->getHeightmapEntry(vRoads[i].y + 1, vRoads[i].x +
1) ?
(float)pTerrain->getHeightmapEntry(vRoads[i].y + 1,
vRoads[i].x) :
(float)pTerrain->getHeightmapEntry(vRoads[i].y + 1,
vRoads[i].x + 1);
}
else if(vRoads[i].type == 2)
{
67
float r = fabs(h2-h);
float sgn = h2 > h ? -1.0f : 1.0f;
float a = (float)atan( r / fCellSize);
pStraightMesh->IdentityMatrices();
pStraightMesh->SetScale(1.0f, 1.0f,
sqrtf(fCellSize * fCellSize + r * r) / fCellSize);
pStraightMesh->RotateX(sgn*a);
if(vRoads[i].type == 2)
pStraightMesh->RotateY( D3DX_PI / 2.0f );
pStraightMesh->SetPosition((vRoads[i].x - ((pTerrain->nCols - 1) /
2.0f)) * fCellSize,
((h+h2)/2.0f),
(vRoads[i].y - ((pTerrain->nRows - 1) / 2.0f)) * fCellSize);
pStraightMesh->Update();
pMatWorld[i] = pStraightMesh->GetMatWorld();
}
void CPassRoad::render()
{
for(int i = 0; i < iTotalRoads; i++)
{
if(vRoads[i].type == 1 || vRoads[i].type == 2 ||
vRoads[i].type == 11 || vRoads[i].type == 12)
{
pStraightMesh->SetMatWorld(pMatWorld[i]);
pStraightMesh->render();
}
68
#include "PassCar.h"
if(iDirOfCar == 2)
pCarMesh->RotateY(D3DX_PI / 2);
else if(iDirOfCar == 3)
pCarMesh->RotateY(D3DX_PI);
else if(iDirOfCar == 4)
pCarMesh->RotateY(-D3DX_PI / 2);
pCarMesh->Update();
pCarMesh->Strife(fStrife);
pCarMesh->Update();
bool CPassCar::stopIfNeed()
{
// проверяем следующую ячейку
int i = (iCurPoint >= (iTotalPoints - 1)) ? 0 : iCurPoint + 1;
// ячейка свободна
if(pTitles->GetIsFree(iPoints[i].y, iPoints[i].x, iDirOfCar) == (-1))
{
69
// занимаем ячейку
pTitles->SetIsFree(iPoints[i].y, iPoints[i].x, id, iDirOfCar);
return false;
}
// ячейка занята мной
else if(pTitles->GetIsFree(iPoints[i].y, iPoints[i].x, iDirOfCar) == id)
return false;
// ячейка занята
return true;
}
bool CPassCar::slowIfNeed()
{
// проверяем следующую ячейку
int i = (iCurPoint >= (iTotalPoints - 1)) ? 0 : iCurPoint + 1;
return true;
}
void CPassCar::update()
{
// следующая точка
int iNextPoint = iCurPoint >= (iTotalPoints - 1) ? 0 : iCurPoint + 1;
pTerrain->getHeightmapEntry(iPoints[iNextPoint].y,
iPoints[iNextPoint].x + 1) ?
(float) pTerrain->getHeightmapEntry(iPoints[iNextPoint].y,
iPoints[iNextPoint].x) :
(float) pTerrain->getHeightmapEntry(iPoints[iNextPoint].y,
iPoints[iNextPoint].x + 1);
}
else if(iDirOfCar == 3 || iDirOfCar == 23 || iDirOfCar == 43)
{
h = pTerrain->getHeightmapEntry(iPoints[iCurPoint].y + 1,
iPoints[iCurPoint].x) >
pTerrain->getHeightmapEntry(iPoints[iCurPoint].y + 1,
iPoints[iCurPoint].x + 1) ?
(float) pTerrain->getHeightmapEntry(iPoints[iCurPoint].y + 1,
iPoints[iCurPoint].x) :
(float) pTerrain->getHeightmapEntry(iPoints[iCurPoint].y + 1,
iPoints[iCurPoint].x + 1);
h2 = pTerrain->getHeightmapEntry(iPoints[iNextPoint].y + 1,
iPoints[iNextPoint].x) >
pTerrain->getHeightmapEntry(iPoints[iNextPoint].y + 1,
iPoints[iNextPoint].x + 1) ?
(float) pTerrain->getHeightmapEntry(iPoints[iNextPoint].y +
1, iPoints[iNextPoint].x) :
(float) pTerrain->getHeightmapEntry(iPoints[iNextPoint].y +
1, iPoints[iNextPoint].x + 1);
}
else if(iDirOfCar == 2 || iDirOfCar == 12 || iDirOfCar == 32)
{
h = pTerrain->getHeightmapEntry(iPoints[iCurPoint].y,
iPoints[iCurPoint].x) >
pTerrain->getHeightmapEntry(iPoints[iCurPoint].y + 1,
iPoints[iCurPoint].x) ?
(float) pTerrain->getHeightmapEntry(iPoints[iCurPoint].y,
iPoints[iCurPoint].x) :
(float) pTerrain->getHeightmapEntry(iPoints[iCurPoint].y + 1,
iPoints[iCurPoint].x);
h2 = pTerrain->getHeightmapEntry(iPoints[iNextPoint].y,
iPoints[iNextPoint].x) >
pTerrain->getHeightmapEntry(iPoints[iNextPoint].y + 1,
iPoints[iNextPoint].x) ?
(float) pTerrain->getHeightmapEntry(iPoints[iNextPoint].y,
iPoints[iNextPoint].x) :
(float) pTerrain->getHeightmapEntry(iPoints[iNextPoint].y +
1, iPoints[iNextPoint].x);
}
else if(iDirOfCar == 4 || iDirOfCar == 14 || iDirOfCar == 34)
{
h = pTerrain->getHeightmapEntry(iPoints[iCurPoint].y,
iPoints[iCurPoint].x + 1) >
pTerrain->getHeightmapEntry(iPoints[iCurPoint].y + 1,
iPoints[iCurPoint].x + 1) ?
(float) pTerrain->getHeightmapEntry(iPoints[iCurPoint].y,
iPoints[iCurPoint].x + 1) :
(float) pTerrain->getHeightmapEntry(iPoints[iCurPoint].y + 1,
iPoints[iCurPoint].x + 1);
h2 = pTerrain->getHeightmapEntry(iPoints[iNextPoint].y,
iPoints[iNextPoint].x + 1) >
pTerrain->getHeightmapEntry(iPoints[iNextPoint].y + 1,
iPoints[iNextPoint].x + 1) ?
(float) pTerrain->getHeightmapEntry(iPoints[iNextPoint].y,
iPoints[iNextPoint].x + 1) :
71
(float) pTerrain->getHeightmapEntry(iPoints[iNextPoint].y +
1, iPoints[iNextPoint].x + 1);
}
float r = fabs(h2-h); // средняя высота
float sgn = h2 > h ? -1.0f : 1.0f; // знак поворота
float a = (float)atan( r / fCellSize ); // угол поворота
pCarMesh->RotateXFromZero( sgn * a ); // непосредственное вращение
if(fabs(h2 - h) < 0.0001f)
pCarMesh->SetHeight(h);
float fThisCellSize = sqrtf(fCellSize*fCellSize + r*r);
// ускорение
if(bExtremeSlow)
fAccelerate = 0;
else if(fFixedAccel >= 0.001f)
fAccelerate = fFixedAccel;
}
else if((iDirOfCar >= 1 && iDirOfCar <=4) && !slowIfNeed())
{
bExtremeSlow = false;
fExtreme_a = 0;
}
if(iDirOfCar == 1 || iDirOfCar == 3)
fYPos += fToMove;
else if(iDirOfCar == 2 || iDirOfCar == 4)
fXPos += fToMove;
else // поворот
{
float fToRotate; // угол поворота за данный тик
float rotsgn; // знак поворота (по часовой или против)
float devcell; // в какой части тайтла необходимо
if(iDirOfCar == 12 || iDirOfCar == 23 ||
iDirOfCar == 34 || iDirOfCar == 41)
{
rotsgn = 1.0f;
devcell = 0.5f - fStrife / fCellSize;
}
Else
{
rotsgn = -1.0f;
devcell = 0.5f + fStrife / fCellSize;
}
float fArcLen = (fThisCellSize * devcell) * (D3DX_PI / 2.0f);
if(slowIfNeed())
{
if(!bExtremeSlow)
{
bExtremeSlow = true;
fExtreme_a = - (fSpeed * fSpeed) / (2 * (fArcLen - 1.5f));
fAccelerate = 0;
}
}
else
{
bExtremeSlow = false;
fExtreme_a = 0;
}
if((fL + fToMove) > fArcLen)
fToMove = fArcLen - fL;
pCarMesh->Update();
pCarMesh->MoveForward(2.0f * (fThisCellSize * devcell) *
sinf(fToRotate / 2.0f));
pCarMesh->Update();
pCarMesh->RotateY(rotsgn * fToRotate / 2.0f);
fAlpha += fToRotate;
fL += fToMove;
pCarMesh->Update();
// проверка нужно ли перейти к следующему тайтлу (направлению)
if((iDirOfCar == 1 || iDirOfCar == 3)
&& fYPos >= (fThisCellSize - 0.00001f))
{
fYPos = 0;
pTitles->ReleaseCell(iPoints[iCurPoint].y, iPoints[iCurPoint].x, iDirOfCar);
// при въезде в следующую освобождает текущую
iCurPoint++;
}
else if((iDirOfCar == 2 || iDirOfCar == 4) &&
fXPos >= (fThisCellSize - 0.00001f))
{
fXPos = 0;
pTitles->ReleaseCell(iPoints[iCurPoint].y, iPoints[iCurPoint].x, iDirOfCar);
// при въезде в следующую освобождает текущую
iCurPoint++;
}
else if(fAlpha >= (D3DX_PI / 2.0f - 0.001f)
{
fAlpha = 0.0f;
fL = 0.0f;
pTitles->ReleaseCell(iPoints[iCurPoint].y, iPoints[iCurPoint].x, iDirOfCar);
// при въезде в следующую освобождает текущую
iCurPoint++;
Приложение Б