Чтение и отображение анимации из файла *.FBX
Я хочу прочитать 3d модель из fbx
файл и отобразить его в openGL es 2.0
двигатель на iPhone (не используется Unity
), а также я хочу отображать анимацию для чтения 3D-объекта.
Как я могу получить анимацию от fbx
файл?
В настоящее время я могу получить список названий поз с, как я понял, идущим с матрицей преобразования, полным списком поз в слое, стеках, слоях и множестве кривых.
Как эта вся информация может быть объединена для отображения правильной анимации?
Я также пытаюсь разобрать некоторую информацию в TakeInfo
, но результат немного странный, как для меня, например:
FbxTakeInfo *ltakeInfo = pScene->GetTakeInfo(lAnimStack->GetName());
FbxTime start = ltakeInfo->mLocalTimeSpan.GetStart();
FbxTime end = ltakeInfo->mLocalTimeSpan.GetStop();
self.startTime = start.GetSecondDouble();
self.endTime = end.GetSecondDouble();
здесь я получил start = 0
а также end = 0.014
для каждого из проанализированных слоев, так что я думаю, это неправильно (fbx
файл, который я хочу отобразить, содержит 1 меш с простой анимацией продолжительностью 5 секунд).
Обновить
После нескольких часов расследования я получил следующие вещи:
Для справки это структура объекта теста, который я хочу отобразить:
Здесь вы можете увидеть множество костей (более конкретно - 19). Мне удалось получить (как отмечено выше) 19 анимированных объектов в списке (например, имена костей / объектов) и 19 групп кривых в пределах 151 кадра каждая (с частотой кадров). 30 ровно 5 сек анимации - 30*5=150 + 1 последняя единичная матрица).
Если я пытаюсь использовать каждую группу кривых для своей сетки по одной (я могу анализировать только 1 сетку), я вижу анимацию различной части сетки, примененной ко всей сетке (например, вертикальное вращение или горизонтальное перемещение), поэтому я думаю, что это Кривые в каждой группе должны применяться точно к конкретной кости, и в результате я получу анимацию для моей сетки. Проблема в том, что я не знаю, как анимировать только выбранную часть костной вершины.
Теперь проблема заключается в том, как применить все анимации, разделенные на отдельные группы, относящиеся к кости, ко всему объекту (потому что у меня только одна сетка)?
Как я могу получить 1 глобальный список кривой для каждого кадра из списка со всеми группами кривой?
Update2
Благодаря совету @codetiger, я следую инструкциям в предоставленной ссылке в комментарии, и с помощью этой техники я могу получить список матов, специфичных для костей, с начальным и конечным временем и необходимыми преобразованиями, но это почти то же самое, что я получал раньше с кривыми - единственное отличие в том, что с помощью кривых я должен создать мат из 9 кривых (перевести / масштабировать / повернуть для x y z) вместо использования полной матрицы, но проблема все еще остается - как я могу объединить их в 1 глобальную матрицу?
Код, который я использую (нашел несколько ссылок на него):
FbxNode* modelNode = _fbxScene->GetRootNode();
FbxAMatrix geometryTransform = GetGeometryTransformation(modelNode);
for (unsigned int deformerIndex = 0; deformerIndex < numOfDeformers; ++deformerIndex) {
FbxSkin* currSkin = reinterpret_cast<FbxSkin*>(mesh->GetDeformer(deformerIndex, FbxDeformer::eSkin));
if (!currSkin) {
continue;
}
unsigned int numOfClusters = currSkin->GetClusterCount();
for (unsigned int clusterIndex = 0; clusterIndex < numOfClusters; ++clusterIndex) {
FbxCluster* currCluster = currSkin->GetCluster(clusterIndex);
std::string currJointName = currCluster->GetLink()->GetName();
FbxAMatrix transformMatrix;
FbxAMatrix transformLinkMatrix;
FbxAMatrix globalBindposeInverseMatrix;
currCluster->GetTransformMatrix(transformMatrix);
currCluster->GetTransformLinkMatrix(transformLinkMatrix);
globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix * geometryTransform;
FbxAnimStack* currAnimStack = _fbxScene->GetSrcObject<FbxAnimStack>(0);
FbxString animStackName = currAnimStack->GetName();
char *mAnimationName = animStackName.Buffer();
FbxTakeInfo* takeInfo = _fbxScene->GetTakeInfo(animStackName);
FbxTime start = takeInfo->mLocalTimeSpan.GetStart();
FbxTime end = takeInfo->mLocalTimeSpan.GetStop();
FbxLongLong mAnimationLength = end.GetFrameCount(FbxTime::eFrames24) - start.GetFrameCount(FbxTime::eFrames24) + 1;
for (FbxLongLong i = start.GetFrameCount(FbxTime::eFrames24); i <= end.GetFrameCount(FbxTime::eFrames24); ++i) {
FbxTime currTime;
currTime.SetFrame(i, FbxTime::eFrames24);
FbxAMatrix currentTransformOffset = modelNode->EvaluateGlobalTransform(currTime) * geometryTransform;
FbxAMatrix mat = currentTransformOffset.Inverse() * currCluster->GetLink()->EvaluateGlobalTransform(currTime);
}
}
}
Здесь я получаю 121 матрицу вместо 151 - но длительность некоторых матричных преобразований занимает больше, чем длительность рисования 1 кадра, поэтому q-ty здесь я думаю также правильным
float duration = end.GetSecondDouble() - start.GetSecondDouble(); //return 5 as and expected
Я думаю, что это Autodesk SDK
есть способ получить 1 глобальное преобразование за каждый розыгрыш
Какие-либо предложения? Это возможно?
Добавьте награду для всех, кто может описать, как отображать анимацию на iPhone в openGLES 2.0 с помощью Autodesk SDK... (извините за опечатку вместо Facebook)
Вот что я могу получить
Оригинальный объект в blender
Если я рисую кость отдельно (тот же VBO с разным преобразованием и только индексы для каждой кости соответственно)
1 ответ
Вот как визуализировать анимированную сетку в OpenGL ES. Это даст вам информацию о плате за проезд и о том, какие данные вам нужно прочитать из файла.
Метод 1: (Скинг на GPU)
Этот метод работает только на ограниченном количестве костей в зависимости от возможностей оборудования. Обычно это зависит от количества матриц, которые вы можете отправить в шейдер.
Привязать информацию о сетке к графическому процессору, используя BindAttributes, один раз и отправить матрицы шейдеру в униформе.
Шаг 1: Считайте всю матрицу костей, создайте массив матриц и отправьте эти данные в шейдер в униформе.
Шаг 2: В вершинном шейдере вычислите gl_position, как показано ниже
attribute vec3 vertPosition;
attribute vec3 vertNormal;
attribute vec2 texCoord1;
attribute vec3 vertTangent;
attribute vec3 vertBitangent;
attribute vec4 optionalData1;
attribute vec4 optionalData2;
uniform mat4 mvp, jointTransforms[jointsSize];
void main()
{
mat4 finalMatrix;
int jointId = int(optionalData1.x);
if(jointId > 0)
finalMatrix = jointTransforms[jointId - 1] * optionalData2.x;
jointId = int(optionalData1.y);
if(jointId > 0)
finalMatrix = finalMatrix + (jointTransforms[jointId - 1] * optionalData2.y);
jointId = int( optionalData1.z);
if(jointId > 0)
finalMatrix = finalMatrix + (jointTransforms[jointId - 1] * optionalData2.z);
jointId = int( optionalData1.w);
if(jointId > 0)
finalMatrix = finalMatrix + (jointTransforms[jointId - 1] * optionalData2.w);
gl_Position = mvp * finalMatrix * vec4(vertPosition, 1.0);
}
В этом шейдере я отправляю информацию о массе краски в атрибутах AdditionalData1 и OptionData2. Данные упакованы следующим образом: (BoneId1, BoneId2, BoneId3, BoneId4) и (Weight4Bone1, Weight4Bone2, Weight4Bone3, Weight4Bone4). Так что в этом случае вы ограничены максимум четырьмя костями, влияющими на каждый атрибут. Часть фрагмента шейдера обычная.
Способ 2: (Скинг процессора)
Если вы не можете жить с ограничением скинов графического процессора, то единственный способ - это выполнить вычисления на стороне процессора.
Шаг 1: Рассчитайте положение вершины в текущем кадре, умножив матрицы, которые принадлежат костям, которые влияют на вершину.
Шаг 2: Соберите новые позиции для текущего кадра и отправьте информацию в GPU в Атрибутах Вершины.
Шаг 3: пересчитать новые позиции вершин в каждом кадре и обновить буфер атрибутов.
Этот метод переносит нагрузку на вычисления в CPU.