Совместимость векторной математики C и OpenGL

#c #opengl #vector

#c #opengl #векторный

Вопрос:

Я много занимался векторной математикой и написал для нее свой собственный шаблон.

Мои требования — это множество векторных математических операций (сложение, вычитание, масштабирование, перекрестная обработка и точечная обработка), также мне нужно иметь возможность передавать свой вектор в виде float [], чтобы OpenGL мог его использовать.

Я довольно успешно использую это в течение некоторого времени, сегодня лектор увидел это и застонал. Были две вещи, которые он особенно ненавидел (одну из которых я понимаю): мое использование наследования, потому что оно, казалось, не соответствовало is a стилю. И моим кастингом (T*)this , конечно, у него было не так много вариантов решения.

Во-первых: из-за наследования мне нужно иметь возможность использовать векторы от vec2 до vec4, поэтому я спроектировал свои векторы следующим образом.

 template<typename T>
Vector2D
{
 public:
 getX(), getY(), setX(), setY() ....
};

template<typename T>
Vector3D : public Vector2D<T>
{
 public:
 getZ(), setZ() ...
}

template<typename T>
Vector4D : public Vector3D<T>
{
 public:
 getW(), setW() ...
}
  

Почему это плохо? и tbh я не вижу, как это улучшить. Мне нужно (хочу) иметь возможность определять тип и иметь какие-либо методы получения и настройки. Если бы я изменил его следующим образом

 template<typename T, int _size>
VectorT
  

Я бы потерял свой .getX() , .setX() материал и должен был заменить его чем-то вроде .at() или [] . tbh Я предпочитаю удобочитаемость .getX() , хотя это упростило бы определения операторов.

Второе: я понимаю, почему это плохо, но чтобы сделать так, чтобы я мог передавать эти векторы в метод OpenGL, который ожидает массив float, я перегрузил оператор splat

 // Defined in Vector2D<T>
operator*() { return (T*)this; }
  

Насколько я понимаю, нет гарантии, что компилятор поместит переменные-члены x, y, z, w в начало класса, и, если не соблюдать осторожность, я могу в конечном итоге передать вместо этого v-table. Однако я должен признать, что до сих пор у меня не было проблем.

Единственный способ, который я могу найти в этой ситуации, — поддерживать возвращаемый массив. Что, я полагаю, было бы проще, если бы я изменил способ работы с векторами в первую очередь.

Комментарии:

1. Опубликуйте больше кода. Где это operator* определено? Где находятся методы getX() и setX()?

Ответ №1:

Возможно, вы захотите рассмотреть возможность использования вместо этого GLM. Он выполняет все, что вы описали (хотя я обнаружил, что документации не хватает), включая интеграцию с OpenGL.

Комментарии:

1. Почему вы обнаружили отсутствие документации? Я согласен с: нет функции поиска и угадывания, в каком пространстве имен она находится.

2. @Marnix: Руководство ( glm.g-truc.net/glm-0.9.1.pdf ) содержит недостаточно информации о том, как использовать все функции, только базовые (например, нет упоминания об использовании шаблонных версий векторов или матриц). Документация по API ( glm.g-truc.net/api-0.9.1/index.html ), похоже, использовал минималистский подход к описанию функции аргументов (например, что ‘m’ делает в glm.g-truc.net/api-0.9.1 /… ?)

3. Лично я бы порекомендовал vmmlib или Eigen вместо GLM. Оба они легко работают с OpenGL, просты в использовании и хорошо документированы.

Ответ №2:

Вы можете слушать своего преподавателя и использовать частичную специализацию (предупреждение: не тестировалось) :

 template<typename T, int size>
class Vector;

template< typename T >
class Vector< T, 2 >
{
  public :
    Vector() : data() {}

    T GetX() const { return data[0]; };
    T GetY() const { return data[1]; };

    void SetX( const T v ) const { data[0]=v; };
    void SetY( const T v ) const { data[1]=v; };

  private :
    T data[2];
};

template< typename T >
class Vector< T, 3 >
{
  public :
    Vector() : data() {}

    T GetX() const { return data[0]; };
    T GetY() const { return data[1]; };
    T GetZ() const { return data[2]; };

    void SetX( const T v ) const { data[0]=v; };
    void SetY( const T v ) const { data[1]=v; };
    void SetZ( const T v ) const { data[2]=v; };

  private :
    T data[3];
};
  

Комментарии:

1. Вероятно, вы захотите использовать size_t (или какой-либо другой целочисленный тип без знака вместо signed int) для типа size, чтобы защититься от аргументов шаблона с отрицательным размером.

Ответ №3:

Как насчет этого:

 template<class T, int _dim>
class Vector
{
    T v[_dim];
    operator*(){return v;}

friend T inner_product(Vector<T, _dim> const amp;v1, Vector<T, _dim> const amp;v2);
};

template<class T, int _dim>
T inner_product(Vector<T, _dim> const amp;v1, Vector<T, _dim> const amp;v2)
{
    T p = 0.;
    for(int i; i < _dim; i  )
        p  = v1.v[i] * v2.v[i];
    return p;
}

template<class T>
class Vector2 : Vector<T, 2>
{
    float getX() const {return v[0];}
    float getS() const {return v[0];}

    float getY() const {return v[1];}
    float getT() const {return v[1];}
}

template<class T>
class Vector3 : Vector<T, 3>, Vector2<T>
{
    float getZ() const {return v[2];}
    float getR() const {return v[2];}
}

template<class T>
class Vector4 : Vector<T, 4>, Vector3<T>
{
    float getW() const {return v[3];}
    float getQ() const {return v[3];}
}
  

Обратите внимание, что создание inner_product друга, не являющегося частью класса, позволяет вам использовать его для всех производных типов!

Ответ №4:

Как вы сказали, вы злоупотребляете природой наследования «is-a». Проблема может возникнуть, если вы напишете функцию, подобную этой

точечный продукт с плавающей точкой (Vector2D a, Vector2D b);

Вы могли бы передать 3D-вектор и получить скалярный результат, когда на самом деле скалярное произведение 2d-вектора и 3d-вектора не определено, и это на самом деле ошибка, которая может привести к странному поведению. Конечно, это не имеет большого значения, но вы отбрасываете часть проверки типов, и если вы собираетесь иметь дело со сложностями статической типизации, вы также можете воспользоваться ее преимуществами при возникновении подобных ошибок.

Поддержка массива, безусловно, является превосходным решением, вы не хотите полагаться на неопределенное поведение, потому что вы никогда не знаете, когда это окончательно подведет вас.

Комментарии:

1. Ах, я не подумал о том, что произойдет, если вы передадите Vector2D вместо 3 (или наоборот), спасибо.

Ответ №5:

Я бы, вероятно, сделал что-то вроде этого:

 template<typename T>
class VectorT{
protected:
  T* m_data;
  int m_size;

public:
  VectorT(unsigned int size) 
  : m_size(size)
  {
    m_data=new T[size];
  }

  virtual ~VectorT()
  {
    delete[] m_data;
  }

  T* operator*() { return m_data; }
  Tamp; operator[](int ii) { return m_data[ii]; }
}

template<typename T>
class Vector3 : public VectorT<T>
{
public:
  Vector3() : VectorT(3) {}
  T getX() { return m_data[0]; }
  T getY() { return m_data[1]; }
  T getZ() { return m_data[2]; }

  Vector3 crossP(const Vector3amp; vv) { ... }
}
  

Ответ №6:

Вот полная математическая библиотека в стиле OpenGL (с открытым исходным кодом) для c

http://glm.g-truc.net/