Модераторы: Rickert
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Движок на пальцах: "От" и "До", часть #2 
:(
    Опции темы
Rickert
Дата 20.10.2008, 08:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ситхи не пройдут!
****


Профиль
Группа: Комодератор
Сообщений: 3356
Регистрация: 11.7.2006
Где: Лакрима

Репутация: 6
Всего: 52



Цитата
Sic luceat lux...

Перед тем как приступить, я попрошу вас самостоятельно перегрузить операции сложения, вычитания, умножения и деления для векторов.
Движок, движок, движок... Итак, у нас на мази след. пункты:
  •  Общая структура
  •  Вспомогательный класс по вычислениям
Общая структура
Можно очень много спорить о том как должен быть построен движок; как будет эффективнее и на чём писать. Я предлагаю след. систему:
  •  У нас есть профили игроков, в которых хранятся настройки управления, видео и т.д.
  •  Класс системный, который устанавливает связь между OpenGL, окном и нашими параметрами
  •  Также класс нашего игрового мира, в котором описаны объекты и осуществляется взаимосвязь между ними
Это очень общная структура. Неисключено, что понадобиться вводить ещё классы такого же общего уровня в будущем. Ну и, конечно, будет ещё тьма мелких классов, которые нам понадобятся для сухой и комфортной жизни smile

Вспомогательный класс
Так как мы собираемся работать с графикой, аналитической геометрией, то нам придётся часто производить различные геометрические операции, как то: поиск пересечения линии и плоскости, попадание точки в треугольник, получение нормали к плоскости и т.п. Следовательно нам надо их релизовать. Но вот вопрос: как это лучше сделать? Половина ответят - засунуть функции работы с векторами в класс векторов, а другая половина скажет: засунуть их в отдельный синглтон. Я солидарен со вторыми, хотябы потому что этот метод избавит на от бессмысленного копирования одних и тех же функций тысячи раз, при создании объектов.
Итак создаём файл описания нашего вспомогательного класса aAux.cpp:
Код
//=============================================================================

#ifndef AAUX_H
  #define AAUX_H

//=============================================================================

#include <math.h>
#include "aVector.h"

//=============================================================================

class aAux
{
  private:
    aAux();

Нам не нужно чтобы была возможность создавать отдельный экземпляр класса, потому что он у нас является синглтоном, а значит надо как-то предостеречь вариант его создания. Что мы и делаем, записывая конструктор как private.
Код
  public:
    inline float aaGetPi() {return pi;} //получить значение числа пи. ~3.14
    inline float aaGetPid() {return pid;}

Функции типа cos / sin оперируют углами не в градусах, а в радианах. Чтобы перевести градус в радины, нам надо умножить его на Пи и разделить на 180. Константа pid = pi/180.0f;
Код
    inline float aaGetPidi() {return pidi;}

Но иногда требуется обратное преобразование радиан в градусы, которое получается через умножение на 180 и деление на пи, pidi = 180.0f/pi;
Код
    inline float aaGetMagnitude(const aVector& vec) {return sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z);}

Вообще, всем и каждому, кто мало знаком с аналитической геометрией / векторной алгеброй, я советую обзавестись рядом книг или же пошустрить инет на тему основных операций над векторами. Данная функция является одной из фундаметальных - поиск длинны вектора.
Помним, что вектор - это направленный отрезок.
Код
    inline float aaDotMulty(const aVector& vec, const aVector& vec2) {return (vec.x*vec2.x + vec.y*vec2.y + vec.z*vec2.z);}

Это функция скалярного произведения двух векторов. В плане геометрического смысла, скалярное произведение - это длинна проекции первого вектора на второй. Запомните этот момент, он нам понадобится, когда будет писать функцию проекции вектора на вектор.
Код
    inline float ator(float angle) {return (angle*pid);}// как я и говорил: иногда надо переводить угол из градусов в радианы. Именно этим и занимается функция
    inline float aaFloatAbs(float f) {return (f < 0.0 ? (-f):f);}// данная функция, как нетрудно понять, просто возвращает модуль числа.
    inline aVector aaGetVectorProjection(const aVector& vec, const aVector& vec2) {return (vec2* (/*тут вам надо подумать совсем немного*/) );}

Читатель должен чётко понимать смысл всех геометрических преобразований с векторами. Допустим что вычитая один вектор из другого он получает третий, направление которого идёт от второго к первому. Я ещё раз настоятельно рекомендую обрадиться к учебникам, если вы до сих пор не знакомы с векторными операциями и что самое главное их геометрическим смыслом.
Итак функция "aaGetVectorProjection(...)" - это функция которая должна вернуть проекцию первого вектора на второй. Как нам её получить? Мы пытаемся умножить вектор vec2 на число. Какое должно быть число? Подумайте.
Код
    inline aVector aaVVVXYZ2pos(const aVector& i, const aVector& j, const aVector& k, const aVector& vec) {return ((i*vec.x) + (j*vec.y + k*vec.z));}

Очень нужная функция. Давайте задумаемся о том, а что такое вообще "координаты"? x, y, z... Для того чтобы ответить на этот вопрос нам надо разобраться с тем, что такое базисы прямоугольной системы координат. Базисы - это три вектора, которые обозначают направление мировых осей . Самые первые базисы, с которыми все знакомятся: (1.0, 0.0, 0.0) - ось абсцисс, (0.0, 1.0, 0.0) - ось ординат, (0.0, 0.0, 1.0) - ось аппликат. Обозначим их i, j, k - соответственно. Возьмите в руки карандаш и лист бумаги, поставьте в любом месте точку - это будет начало отсчёта координат. Теперь проведите вектор (1.0, 0.0, 0.0) и остальные два. Что у вас получается? Три вектора, каждый из которых направлен перпендикулярно двум остальным. Теперь, запоминаем, что любая точка в трёхмерном пространстве это p = x*i + y*j + z*k. Т.е. получается, точка - это сумма базисных векторов, каждый из которых умножен на координату. А что бывает когда мы умножаем вектор, единичной длинны, на любое число? Правильно - получается вектор длинны равной этому числу. Следовательно получается, что координата - это некий коэффициент для абсциссы, который будет характеризовать её направление.
А теперь, чтобы получить точку, в нашей заданной, векторами (абсциссами) i, j, k,  прямоугольной системе координат нам надо умножить каждую из этих абсцисс, на каждую из координат: i - на x, j - на y, z - на k.
Но ведь можно задать i, j, k не только (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)? Правильно - и тогда вы получите другую систему координат. Об этом мы ещё поговорим, когда выведем своё первое изображение на экран. Так вот, вернёмся к нашей функции "aaVVVXYZ2pos()" - что она делает, посмотрите и попытайтесь понять сами. Если не сможете - спрашивайте.
Код
    inline aVector aaGetNormal(const aVector& vec, const aVector& vec2, const aVector& vec3) {return aaNormalize(aaCrossMulty((vec2 - vec), (vec3 - vec)));}

Функция, как можно понять из названия возвращает нормаль к каким - то трём векторам. Три точки, три вектора - это минимум, с помощью которого можно конкретно задать плоскость в трёхмерном пространстве. В этой функции очень важен порядок вершин, потому что от него зависит то, в какую сторону будет направлен вектор нормали. И не только порядок но и расположение вершин. Перечислены они должны быть по часово стрелке от первой к третьей. Мы берём разницу первого вектора и второго + разницу первого и третьего - тем самым получаем два вектора, которые выходят из одной вершины. А как известно, векторное произведение дву векторов даёт третий вектор, который будет направлен перпендикулярно двум данным. Всё намного проще, если нарисовать треугольник на бумаге и слева направо - снизу вверх, по часовой стрелке, обозначить вершины vec, vec2, vec3. Теперь смотрим: "(vec2 - vec)": получится вектор, выходящий из vec в vec2; теперь "(vec3 - vec)" - и у нас вектор идущий из vec в vec3. А их векторное произведение - это нормаль, которая перпендикулярна плоскости листа бумаги и направлена от нас.
Функцию "aaCrossMultiply()" мы с вами ещё не смотрели, но смысл её прост - она возвращает векторное произведение двух векторов, геометрический смысл которого - получение вектора перпендикулярного двум данным.
Код
    inline bool aaVectorOnLine(const aVector& l1, const aVector& l2, const aVector& p) {/*здесь будет ваша реклама*/}

Функция проверки лежит ли данная точка "p" на линии, заданной векторами "l1" и "l2". Напишите её сами, предварительно посмотрев описание функций "aaGetLength()" и aaCompareFloat
Описание нижеследующих функций будет приведено в файле реализации aAux.cpp
Код
    static aAux& aaGetInstance()
    {
      static aAux aux;

      return aux;
    }//получаем наш синглтон

    //uVector
    aVector aaCrossMulty(const aVector& vec, const aVector& vec2);//функция получения векторного произведения двух векторов
    aVector aaNormalize(const aVector& vec);//функция нормализации вектора
    float aaGetAngle(const aVector& v, const aVector& v2);//получение угла между векторами
    aVector aaRotateAroundVector(const aVector& v, const aVector& v2, float angle); //вращение вектора v, относительно вектора v2 на угол angle
    bool aaCompareFloat(float f1, float f2);//функция сравнения двух чисел с плавующей точкой
    float aaGetLength(const aVector& vec, const aVector& vec2);//получение расстояния от одной точки до другой
    bool aaCheckVectorIn(const aVector& p, const aVector& vec, const aVector& vec2, const aVector& vec2);//функция проверки лежит ли данная точка в заданном треугольнике
    aVector aaGetIntersection(const aVector& l1, const aVector& l2, const aVector& s1, const aVector& s2, const aVector& s3);//пересекает ли линия плоскость, заданную тремя точками

    ~aAux();

  private:
    float pi; //наше число pi
    float pid; // (pi/180.0)
    float pidi; // (180.0/pi)
};

//=============================================================================

#endif

//=============================================================================

Остановились, отдышались...
Переходим к файлу реализации aAux.cpp:
Код
//=============================================================================

#include <math.h>
#include "../inc/aAux.h"

//=============================================================================

aAux::aAux()
{
  pi = 3.1415926535f; //наши постоянные значения
  pid = 0.017453292f;
  pidi = 57.2957795f;
}

//=============================================================================

aVector aAux::aaCrossMulty(const aVector& vec, const aVector& vec2)
{
  /*
    Векторное произведение векторов, на выходе даёт вектор,
    перпендикулярный двум данным.
  */
  aVector res;

  res.x = (vec.y*vec2.z) - (vec.z*vec2.y);
  res.y = (vec.z*vec2.x) - (vec.x*vec2.z);
  res.z = (vec.x*vec2.y) - (vec.y*vec2.x);

  return res;
}

//=============================================================================

aVector aAux::aaNormalize(const aVector& v)
{
  /*
    Нормализация вектора - это процесс получения длинны
    вектора и деление каждой координаты на неё.
    Необходимо для того, чтобы длинна вектора
    была не больше 1. Это необходимое условие,
    например, для нормалей.
  */
  aVector vec(v);
  float m = aaGetMagnitude(vec);

  if (aaCompareFloat(m, 0.0f))
    return aVector(0.0f, 0.0f, 0.0f);

  vec.x /= m;
  vec.y /= m;
  vec.z /= m;

  return vec;
}

//=============================================================================

float aAux::aaGetAngle(const aVector& v, const aVector& v2)
{
  /*
    Получить угол между двумя векторами.
    Мы сначала нормализуем векторы, а затем
    получаем угол через арккосинус от длинны
    их скалярного произведения.
    осильте сами.
  */
  float scal = 0.0f;
  //... ?
  return scal;
}

//=============================================================================

aVector aAux::aaRotateAroundVector(const aVector& v, const aVector& v2, float angle)
{
  /*
    Функция, которая поворачивает вектор v,
    относительно вектора v2 на угол angle.
    Что это значит? Чтобы разобраться, сделайте
    след.: нарисуйте на листе бумаги стандартные
    базисы. Если вы возьмёте ось абсцисс и повернёте
    на (-90/270) градусов относительно оси ординат,
    то получите ось аппликат. Пробуйте.
  */
  aVector dx, dy, dz;
  aVector vec(v);
  aVector vec2(v2);
  float dot = aaDotMulty(vec, vec2);
  float angle2 = ator(angle);

  dz.x = vec2.x * dot;
  dz.y = vec2.y * dot;
  dz.z = vec2.z * dot;

  dx.x = vec.x - dz.x;
  dx.y = vec.y - dz.y;
  dx.z = vec.z - dz.z;

  dy = aaCrossMulty(dx, vec2);

  vec.x = dx.x*cos(angle2) + dy.x*sin(angle2) + dz.x;
  vec.y = dx.y*cos(angle2) + dy.y*sin(angle2) + dz.y;
  vec.z = dx.z*cos(angle2) + dy.z*sin(angle2) + dz.z;

  return vec;
}

//=============================================================================


bool aAux::aaCompareFloat(float f1, float f2)
{
  /*
    Весьма важная функция: сравнение двух
    чисел с плавующей точкой. По причинам, которые
    я предлогаю назвать вам в этом топике,
    сам компьютер не может сравнить их достоверно.
    Поэтому приходится брать модуль разницы чисел
    и сравнивать с достаточно маленькой величиной.
  */
  if (aaFloatAbs((f1) - (f2)) < 0.0001f)
    return 1;

  return 0;
}

//=============================================================================

float aAux::aaGetLength(const aVector& vec, const aVector& vec2)
{
  /*
    Получение расстояния между точками похоже на получение
    длинны вектора.
  */
  float vx = vec2.x - vec.x;
  float vy = vec2.y - vec.y;
  float vz = vec2.z - vec.z;

  return sqrt(vx*vx + vy*vy + vz*vz);
}

//=============================================================================


bool aAux::aaCheckVectorIn(const aVector& p, const aVector& vec1, const aVector& vec2, const aVector& vec3)
{
  /*
    Проверка, находится ли данная вершина p,
    в треугольнике, заданном вершинами vec1,
    vec2, vec3. Чтобы понять процесс, нарисуйте
    на листе бумаги треугольник и точку (в нём,
    или вне его), после чего проведите от неё
    линии к каждой вершине треугольника. А теперь
    узнайте угол между каждой получившейся прямой
    и соседней к ней. Если их сумма ~= 360 градусов,
    то точка лежит внутри треугольника.
  */
  aVector tmpVector2, tmpVector3, tmpVector4;

  tmpVector2 = vec1 - p;
  tmpVector3 = vec2 - p;
  tmpVector4 = vec3 - p;

  float angle = aAux::aaGetInstance().aaGetAngle(tmpVector2, tmpVector3)*aAux::aaGetInstance().aaGetPidi();

  angle += aAux::aaGetInstance().aaGetAngle(tmpVector3, tmpVector4)*aAux::aaGetInstance().aaGetPidi();
  angle += aAux::aaGetInstance().aaGetAngle(tmpVector4, tmpVector2)*aAux::aaGetInstance().aaGetPidi();

  if ((angle >= 359.9f) && (angle <= 360.1f))
    return 1;

  return 0;
}

//=============================================================================

aVector aAux::aaGetIntersection(const aVector& l1, const aVector& l2, const aVector& s1, const aVector& s2, const aVector& s3)
{
  /*
    Проверяем: пересекает ли данная линия l1 - l2
    плоскость, заданную тремя вершинами s1, s2, s3.
    Как это получается? Подумайте...
  */
  aVector tmpVector = aaGetNormal(s1, s2, s3);

  float r1 = aaDotMulty(tmpVector, (l1 - s1));
  float r2 = aaDotMulty(tmpVector, (l2 - s1));

  tmpVector = aaAdd(l1, aaMultiply((l2 - l1), -r1/(r2 - r1)));

  if (aaVectorOnTriangleEdges(tmpVector, s1, s2, s3))
    return tmpVector; 

  if ((r1*r2) < 0.0)
    return tmpVector;

  return aVector(0.0f, 0.0f, 0.0f);
}

//=============================================================================

aAux::~aAux()
{
  //деструктор, он и в африке деструктор
}

//=============================================================================


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

Да прибудет с вами великая сила smile 

Это сообщение отредактировал(а) Rickert - 22.1.2010, 10:09


--------------------
Ни что не внушает сна крепче, чем день приисполненный трудов!
PM MAIL WWW Skype GTalk   Вверх
mr.DUDA
Дата 10.11.2008, 11:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 7
Всего: 232



Да пребудет с тобой великая сила, тоже. Придраться не к чему, только грамматические ошибки поправить не мешало бы. Ну и по мелочам:

Цитата(Rickert @  20.10.2008,  08:29 Найти цитируемый пост)
Но вот вопрос: как это лучше сделать? Половина ответят - засунуть функции работы с векторами в класс векторов, а другая половина скажет: засунуть их в отдельный синглтон. Я солидарен со вторыми, хотябы потому что этот метод избавит на от бессмысленного копирования одних и тех же функций тысячи раз, при создании объектов.

Код функций не копируется при создании объекта.

Цитата(Rickert @  20.10.2008,  08:29 Найти цитируемый пост)
Три точки, три вектора - это минимум, с помощью которого можно конкретно задать плоскость в трёхмерном пространстве.

Можно вектором и флоатом задать (нормаль и расстояние от {0,0,0}).

P.S. а функцию aaVVVXYZ2pos ты часто используешь?  smile 


--------------------
user posted image
PM MAIL WWW   Вверх
Rickert
Дата 10.11.2008, 11:25 (ссылка) |   (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ситхи не пройдут!
****


Профиль
Группа: Комодератор
Сообщений: 3356
Регистрация: 11.7.2006
Где: Лакрима

Репутация: 6
Всего: 52



Цитата(mr.DUDA @  10.11.2008,  11:14 Найти цитируемый пост)
P.S. а функцию aaVVVXYZ2pos ты часто используешь?

Да, когда будем разбирать скелетную анимацию smile 
Цитата(mr.DUDA @  10.11.2008,  11:14 Найти цитируемый пост)
Можно вектором и флоатом задать (нормаль и расстояние от {0,0,0}).

По сути - одно и тоже.
Цитата(mr.DUDA @  10.11.2008,  11:14 Найти цитируемый пост)
Код функций не копируется при создании объекта.

Разве?


--------------------
Ни что не внушает сна крепче, чем день приисполненный трудов!
PM MAIL WWW Skype GTalk   Вверх
mr.DUDA
Дата 11.11.2008, 10:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 7
Всего: 232



Цитата(Rickert @  10.11.2008,  11:25 Найти цитируемый пост)
Разве?

Не сомневайся  smile 

Вот тебе пример простой:
Код
class CSome
{
public:
    int Func(int arg)
    {
        return arg;
    }
};

int main()
{
    CSome x;
    CSome y;
    CSome *z = new CSome();

    x.Func(0);
    y.Func(1);
    z->Func(2);
}


Вот скриншоты с адресом вызываемой функции: раздватри.

Добавлено через 1 минуту и 17 секунд
Цитата(Rickert @  10.11.2008,  11:25 Найти цитируемый пост)
Да, когда будем разбирать скелетную анимацию

О, вот это тема.  smile 


--------------------
user posted image
PM MAIL WWW   Вверх
Rickert
Дата 26.11.2008, 08:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ситхи не пройдут!
****


Профиль
Группа: Комодератор
Сообщений: 3356
Регистрация: 11.7.2006
Где: Лакрима

Репутация: 6
Всего: 52



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

Это сообщение отредактировал(а) Rickert - 26.11.2008, 08:24


--------------------
Ни что не внушает сна крепче, чем день приисполненный трудов!
PM MAIL WWW Skype GTalk   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Программирование игр, графики и искуственного интеллекта"
Rickert

НА ЗЛОБУ ДНЯ: Дорогие посетители, прошу обратить внимание что новые темы касающиеся новых вопросов создаются кнопкой "Новая тема" а не "Ответить"! Любые оффтопиковые вопросы, заданные в текущих тематических темах будут удалены а их авторы, при рецедиве, забанены.

  • Литературу, связанную с программированием графики, обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы связанные с программированием графики и мультимедии на языках С++ и Delphi
  • Вопросы по реализации алгоритмов рассматриваются здесь

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Rickert.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Программирование игр, графики и искусственного интеллекта | Следующая тема »


 




[ Время генерации скрипта: 0.1321 ]   [ Использовано запросов: 20 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.