glRotatef в чистом C
У меня проблема с написанием альтернативной функции для glRotatef на чистом C. Мне нужно реализовать функцию, аргументами которой являются: список точек, угол и вектор поворота. Функция должна возвращать список точек после поворота. Моя функция выглядит так:
void rotate(float * current, float a, float x, float y, float z)
{
float sina = sinf(a);
float cosa = cosf(a);
float rotateMatrix[9] = //creating rotate matrix
{
x*x*(1-cosa) + cosa, x*y*(1-cosa) - z*sina, x*z*(1-cosa) + y*sina,
y*x*(1-cosa) + z*sina, y*y*(1-cosa) + cosa, y*z*(1-cosa) - x*sina,
z*x*(1-cosa) - y*sina, z*y*(1-cosa) + x*sina, z*z*(1-cosa) + cosa
};
float *resultVertexList = current; //temporary help
int i;
for(i=0;current[i] != 0;i++) //multiplying CURRENT_MATRIX * ROTATE_MATRIX
{
int currentVertex = (i/3) * 3;
int rotateColumn = i%3;
resultVertexList[i] =
current[currentVertex] * rotateMatrix[rotateColumn] +
current[currentVertex+1] * rotateMatrix[rotateColumn+3] +
current[currentVertex+2] * rotateMatrix[rotateColumn+6];
}
current = resultVertexList;
}
Я называю это как здесь: rotate(current, M_PI/10, 0, 1, 0);
После этого я беру current
список точек и просто нарисовать их с OpenGL. Для тестирования я попытался повернуть список точек, представляющих куб. Он вращается, но с каждым вызовом rotate
функция сжимается. Понятия не имею почему. Смотрите некоторые скриншоты:
После многих призваний rotate
Функция сжимается до одной точки.
Что я делаю неправильно?
2 ответа
Эта строка кода:
float *resultVertexList = current; //temporary help
не копирует ваш список вершин. Вы только копируете указатель на список, поэтому после этого у вас есть два указателя, указывающих на один и тот же список. Из-за этого следующий цикл использует уже повернутые координаты x/y для вычисления новых координат y/z, что, очевидно, неверно.
Я также интересуюсь вашим условием прекращения:
current[i] != 0
Это не так, но это мешает вам иметь любую вершину с нулевой координатой. Вместо этого я бы предложил дополнительный параметр для явной передачи количества вершин.
Я бы также повернул на каждую вершину, а не на координату, это просто более естественно и легче понять:
void rotate(float * current, int vertexCount, float a, float x, float y, float z)
{
float sina = sinf(a);
float cosa = cosf(a);
float rotateMatrix[9] =
{
x*x*(1 - cosa) + cosa, x*y*(1 - cosa) - z*sina, x*z*(1 - cosa) + y*sina,
y*x*(1 - cosa) + z*sina, y*y*(1 - cosa) + cosa, y*z*(1 - cosa) - x*sina,
z*x*(1 - cosa) - y*sina, z*y*(1 - cosa) + x*sina, z*z*(1 - cosa) + cosa
};
int i;
for (i = 0; i < vertexCount; ++i)
{
float* vertex = current + i * 3;
float x = rotateMatrix[0] * vertex[0] + rotateMatrix[1] * vertex[1] + rotateMatrix[2] * vertex[2];
float y = rotateMatrix[3] * vertex[0] + rotateMatrix[4] * vertex[1] + rotateMatrix[5] * vertex[2];
float z = rotateMatrix[6] * vertex[0] + rotateMatrix[7] * vertex[1] + rotateMatrix[8] * vertex[2];
vertex[0] = x;
vertex[1] = y;
vertex[2] = z;
}
}
То, как вы реализуете матричный вектор-мультипликацию, очень грубое и, что более важно, просто неверное.
Проблема в том, что вы перезаписываете данные, которые вам все еще нужны. Таким образом, в самом первом случае вы эффективно пишете current[0]
(и ваш resultVertexList
Указатель не поможет вообще. Он указывает на вашу единственную копию данных, перезаписывая их на месте. В следующих двух итерациях новое значение для current[0]
будет использоваться вместо оригинального.
Я предлагаю вам просто реализовать прямолинейный mat3 * vec3
функция, которая может быть легко вызвана в цикле и которая заботится о том, чтобы не перезаписывать нужные данные.