脖子不正常---骨骼动画
neck is abnormal---skeletal animation
库:assimp;模型:*.fbx
骨骼动画可以正常播放了。
但是角色的脖子是伸着的,一动不动
使用renderdoc发现输入vertex shader的顶点是没有问题的。但是输出顶点显示颈部异常。
我还是不知道问题出在哪里
角色的头不能动,脖子被拉长了。
我想知道到底哪里错了?有什么问题可以让头动不了?
这是错误发生时的结果:
将输入传递给着色器
for (size_t i = 0; i < MAX_BONES; ++i){
g_Bones.bones[i] = glm::mat4(1.0f);//为了让没有骨头的模型正常显示
}
for (int32_t i = 0; i < g_Model->meshes.size(); ++i) {
DVKMesh* mesh = g_Model->meshes[i];
g_Position.model = mesh->linkNode->GetGlobalMatrix();
for (int32_t j = 0; j < mesh->bones.size(); j++) {
DVKBone* bone = g_Model->bones[mesh->bones[j]];
g_Bones.bones[j] = bone->finalTransform;
g_Bones.bones[j] = glm::inverse(mesh->linkNode->GetGlobalMatrix()) * g_Bones.bones[j];
}
if (mesh->bones.empty()) {
g_Bones.bones[mesh->bones.size()] = glm::mat4(1.0f);
}
bufferData(g_VulkanBasic.device, sizeof(Position_UBO), &g_Position, g_PositionBuffer.memory, i * g_MinUniformBufferOffset);
}
bufferData(g_VulkanBasic.device, sizeof(BonesTransformBlock), g_Bones.bones, g_BonesBuffer.memory);
加载骨骼和加载皮肤
void FillMatrixWithAiMatrix(glm::mat4& matrix, const aiMatrix4x4& aiMatrix) {
matrix[0][0] = aiMatrix.a1;
matrix[0][1] = aiMatrix.a2;
matrix[0][2] = aiMatrix.a3;
matrix[0][3] = aiMatrix.a4;
matrix[1][0] = aiMatrix.b1;
matrix[1][1] = aiMatrix.b2;
matrix[1][2] = aiMatrix.b3;
matrix[1][3] = aiMatrix.b4;
matrix[2][0] = aiMatrix.c1;
matrix[2][1] = aiMatrix.c2;
matrix[2][2] = aiMatrix.c3;
matrix[2][3] = aiMatrix.c4;
matrix[3][0] = aiMatrix.d1;
matrix[3][1] = aiMatrix.d2;
matrix[3][2] = aiMatrix.d3;
matrix[3][3] = aiMatrix.d4;
matrix = glm::transpose(matrix);
}
void DVKModel::LoadBones(const aiScene* aiScene){
std::unordered_map<std::string, int32_t> boneIndexMap;
for (int32_t i = 0; i < (int32_t)aiScene->mNumMeshes; ++i){
aiMesh* aimesh = aiScene->mMeshes[i];
for (int32_t j = 0; j < (int32_t)aimesh->mNumBones; ++j){
aiBone* aibone = aimesh->mBones[j];
std::string name = aibone->mName.C_Str();
auto it = boneIndexMap.find(name);
if (it == boneIndexMap.end()){
// new bone
int32_t index = (int32_t)bones.size();
DVKBone* bone = new DVKBone();
bone->index = index;
bone->parent = -1;
bone->name = name;
FillMatrixWithAiMatrix(bone->inverseBindPose, aibone->mOffsetMatrix);
bones.push_back(bone);
bonesMap.insert(std::make_pair(name, bone));
boneIndexMap.insert(std::make_pair(name, index));
}
}
}
}
void DVKModel::LoadSkin(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene){
std::unordered_map<int32_t, int32_t> boneIndexMap;
for (int32_t i = 0; i < (int32_t)aiMesh->mNumBones; ++i){
aiBone* boneInfo = aiMesh->mBones[i];
std::string boneName(boneInfo->mName.C_Str());
int32_t boneIndex = bonesMap[boneName]->index;
// bone在mesh中的索引
int32_t meshBoneIndex = 0;
auto it = boneIndexMap.find(boneIndex);
if (it == boneIndexMap.end()){
meshBoneIndex = (int32_t)mesh->bones.size();
mesh->bones.push_back(boneIndex);
boneIndexMap.insert(std::make_pair(boneIndex, meshBoneIndex));
}
else{
meshBoneIndex = it->second;
}
for (uint32_t j = 0; j < boneInfo->mNumWeights; ++j){
uint32_t vertexID = boneInfo->mWeights[j].mVertexId;
float weight = boneInfo->mWeights[j].mWeight;
// 顶点->Bone
if (skinInfoMap.find(vertexID) == skinInfoMap.end()){
skinInfoMap.insert(std::make_pair(vertexID, DVKVertexSkin()));
}
DVKVertexSkin* info = &(skinInfoMap[vertexID]);
info->indices[info->used] = meshBoneIndex;
info->weights[info->used] = weight;
++info->used;
if (info->used >= 4){
break;
}
}
}
for (auto it = skinInfoMap.begin(); it != skinInfoMap.end(); ++it){
DVKVertexSkin& info = it->second;
for (int32_t i = info.used; i < 4; ++i){
info.indices[i] = 0;
info.weights[i] = 0.0f;
}
}
mesh->isSkin = true;
}
glsl 顶点着色器:
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec2 inUV0;
layout (location = 2) in vec3 inNormal;
layout (location = 3) in vec4 inSkinIndex;
layout (location = 4) in vec4 inSkinWeight;
layout (binding = 0) uniform MVPBlock {
mat4 modelMatrix;
mat4 viewMatrix;
mat4 projectionMatrix;
} uboMVP;
#define MAX_BONES 64
layout (binding = 1) uniform BonesTransformBlock{
mat4 bones[MAX_BONES];
} bonesData;
layout (location = 0) out vec2 outUV;
layout (location = 1) out vec3 outNormal;
layout (location = 2) out vec4 outColor;
void main() {
mat4 boneMatrix = bonesData.bones[int(inSkinIndex.x)] * inSkinWeight.x;
boneMatrix += bonesData.bones[int(inSkinIndex.y)] * inSkinWeight.y;
boneMatrix += bonesData.bones[int(inSkinIndex.z)] * inSkinWeight.z;
boneMatrix += bonesData.bones[int(inSkinIndex.w)] * inSkinWeight.w;
mat4 modeMatrix = uboMVP.modelMatrix * boneMatrix;
mat3 normalMatrix = transpose(inverse(mat3(modeMatrix)));
vec3 normal = normalize(normalMatrix * inNormal.xyz);
outUV = inUV0;
outNormal = normal;
outColor = inSkinWeight;
gl_Position = uboMVP.projectionMatrix * uboMVP.viewMatrix * modeMatrix * vec4(inPosition.xyz, 1.0);
}
这是全部代码:
dvkmodel.h
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include "vulkanFrame.h"
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
struct DVKVertex {
glm::vec3 pos;
glm::vec2 uvs;
glm::vec3 normals;
glm::vec4 boneIDs;
glm::vec4 weights;
static VkVertexInputBindingDescription GetInputBinding(){
VkVertexInputBindingDescription vertexInputBinding = {};
vertexInputBinding.binding = 0;
vertexInputBinding.stride = sizeof(DVKVertex);
vertexInputBinding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return vertexInputBinding;
}
static std::vector<VkVertexInputAttributeDescription>GetInputAttributes(){
VkFormat format[] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT };
uint32_t offset[] = { offsetof(DVKVertex, pos), offsetof(DVKVertex, uvs), offsetof(DVKVertex, normals), offsetof(DVKVertex, boneIDs), offsetof(DVKVertex, weights) };
std::vector<VkVertexInputAttributeDescription> vertexInputAttributs(sizeof(format) / sizeof(VkFormat));
for (size_t i = 0; i < vertexInputAttributs.size(); ++i) {
vertexInputAttributs[i].binding = 0;
vertexInputAttributs[i].location = i;
vertexInputAttributs[i].format = format[i];
vertexInputAttributs[i].offset = offset[i];
}
return vertexInputAttributs;
}
};
struct DVKBoundingBox {
glm::vec3 min;
glm::vec3 max;
glm::vec3 corners[8];
DVKBoundingBox(){
}
DVKBoundingBox{
}
};
struct DVKPrimitive{
BufferInfo indexBuffer;
BufferInfo vertexBuffer;
std::vector<DVKVertex>vertices;
std::vector<uint32_t> indices;
int32_t vertexCount = 0;
int32_t triangleNum = 0;
DVKPrimitive(){
}
~DVKPrimitive(){
}
void DrawOnly(VkCommandBuffer cmdBuffer){
if (vertexBuffer.size){
vkCmdDraw(cmdBuffer, vertices.size(), 1, 0, 0);
}
else{
vkCmdDrawIndexed(cmdBuffer, vertices.size(), 1, 0, 0, 0);
}
}
void BindOnly(VkCommandBuffer cmdBuffer){
VkDeviceSize offset = 0;
if (vertexBuffer.size){
vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &vertexBuffer.buffer, &offset);
}
if (indexBuffer.size){
vkCmdBindIndexBuffer(cmdBuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
}
}
void BindDrawCmd(VkCommandBuffer cmdBuffer){
BindOnly(cmdBuffer);
DrawOnly(cmdBuffer);
}
};
struct DVKMaterialInfo{
std::string diffuse;
std::string normalmap;
std::string specular;
};
struct DVKBone{
std::string name;
int32_t index = -1;
int32_t parent = -1;
glm::mat4 inverseBindPose;
glm::mat4 finalTransform;
};
struct DVKVertexSkin{
int32_t used = 0;
int32_t indices[4];
glm::vec4 weights;
};
template <class ValueType>
struct DVKAnimChannel{
std::vector<float> keys;
std::vector<ValueType> values;
void GetValue(float key, ValueType& outPrevValue, ValueType& outNextValue, float& outAlpha){
outAlpha = 0.0f;
if (keys.size() == 0){
return;
}
if (key <= keys.front()){
outPrevValue = values.front();
outNextValue = values.front();
outAlpha = 0.0f;
return;
}
if (key >= keys.back()){
outPrevValue = values.back();
outNextValue = values.back();
outAlpha = 0.0f;
return;
}
int32_t frameIndex = 0;
for (int32_t i = 0; i < keys.size() - 1; ++i){
if (key <= keys[i + 1]){
frameIndex = i;
break;
}
}
outPrevValue = values[frameIndex + 0];
outNextValue = values[frameIndex + 1];
float prevKey = keys[frameIndex + 0];
float nextKey = keys[frameIndex + 1];
outAlpha = (key - prevKey) / (nextKey - prevKey);
}
};
struct DVKAnimationClip {
std::string nodeName;
float duration;
DVKAnimChannel<glm::vec3> positions;
DVKAnimChannel<glm::vec3> scales;
DVKAnimChannel<glm::quat> rotations;
};
struct DVKAnimation{
std::string name;
float time = 0.0f;
float duration = 0.0f;
float speed = 1.0f;
std::unordered_map<std::string, DVKAnimationClip> clips;
};
struct DVKMesh{
typedef std::vector<DVKPrimitive*> DVKPrimitives;
DVKPrimitives primitives;
DVKBoundingBox bounding;
DVKNode* linkNode;
std::vector<int32_t>bones;
bool isSkin = false;
DVKMaterialInfo material;
int32_t vertexCount;
int32_t triangleCount;
DVKMesh()
: linkNode(nullptr)
, vertexCount(0)
, triangleCount(0){
}
void BindOnly(VkCommandBuffer cmdBuffer){
for (int i = 0; i < primitives.size(); ++i){
primitives[i]->BindOnly(cmdBuffer);
}
}
void DrawOnly(VkCommandBuffer cmdBuffer){
for (int i = 0; i < primitives.size(); ++i){
primitives[i]->DrawOnly(cmdBuffer);
}
}
void BindDrawCmd(VkCommandBuffer cmdBuffer){
for (int i = 0; i < primitives.size(); ++i){
primitives[i]->BindDrawCmd(cmdBuffer);
}
}
~DVKMesh(){
for (int i = 0; i < primitives.size(); ++i){
delete primitives[i];
}
primitives.clear();
linkNode = nullptr;
}
};
struct DVKNode{
std::string name;
std::vector<DVKMesh*> meshes;
DVKNode* parent;
std::vector<DVKNode*> children;
glm::mat4 localMatrix;
glm::mat4 globalMatrix;
DVKNode()
: name("None")
, parent(nullptr){
}
const glm::mat4& GetLocalMatrix() {
return localMatrix;
}
glm::mat4& GetGlobalMatrix() {
globalMatrix = localMatrix;
if (parent) {
//globalMatrix.Append(parent->GetGlobalMatrix());
globalMatrix = parent->GetGlobalMatrix() * globalMatrix;
}
return globalMatrix;
}
void CalcBounds(DVKBoundingBox& outBounds) {
if (meshes.size() > 0) {
const glm::mat4& matrix = GetGlobalMatrix();
for (int32_t i = 0; i < meshes.size(); ++i) {
glm::vec3 mmin = matrix * glm::vec4(meshes[i]->bounding.min, 1.0f);
glm::vec3 mmax = matrix * glm::vec4(meshes[i]->bounding.max, 1.0f);
outBounds.min = glm::min(outBounds.min, mmin);
outBounds.min = glm::min(outBounds.min, mmax);
outBounds.max = glm::max(outBounds.max, mmin);
outBounds.max = glm::max(outBounds.max, mmax);
}
}
for (int32_t i = 0; i < children.size(); ++i) {
children[i]->CalcBounds(outBounds);
}
}
DVKBoundingBox GetBounds() {
DVKBoundingBox bounds;
bounds.min = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
bounds.max = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
CalcBounds(bounds);
bounds.UpdateCorners();
return bounds;
}
~DVKNode(){
for (int32_t i = 0; i < meshes.size(); ++i){
delete meshes[i];
}
meshes.clear();
for (int32_t i = 0; i < children.size(); ++i){
delete children[i];
}
children.clear();
}
};
class DVKModel{
public:
DVKModel()
: device(nullptr)
, rootNode(nullptr){
}
~DVKModel(){
delete rootNode;
rootNode = nullptr;
device = nullptr;
meshes.clear();
linearNodes.clear();
for (int32_t i = 0; i < bones.size(); ++i){
delete bones[i];
}
bones.clear();
}
void LoadFromFile(VkDevice device, const std::string& filename);
protected:
DVKNode* LoadNode(const aiNode* node, const aiScene* scene);
DVKMesh* LoadMesh(const aiMesh* mesh, const aiScene* scene);
void LoadBones(const aiScene* aiScene);
void LoadSkin(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene);
void LoadVertexDatas(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, std::vector<DVKVertex>& vertices, glm::vec3& mmax, glm::vec3& mmin, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene);
void LoadIndices(std::vector<uint32_t>& indices, const aiMesh* aiMesh, const aiScene* aiScene);
void LoadPrimitives(std::vector<DVKVertex>& vertices, std::vector<uint32_t>& indices, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene);
void LoadAnim(const aiScene* aiScene);
public:
typedef std::unordered_map<std::string, DVKNode*> NodesMap;
typedef std::unordered_map<std::string, DVKBone*> BonesMap;
VkDevice device;
DVKNode* rootNode;
std::vector<DVKNode*> linearNodes;
std::vector<DVKMesh*> meshes;
NodesMap nodesMap;
std::vector<DVKBone*> bones;
BonesMap bonesMap;
std::vector<DVKAnimation> animations;
int32_t animIndex = -1;
private:
bool loadSkin = false;
};
dvkmodel.cpp
#include "DVKModel.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/cimport.h>
void FillMatrixWithAiMatrix(glm::mat4& matrix, const aiMatrix4x4& aiMatrix) {
matrix[0][0] = aiMatrix.a1;
matrix[0][1] = aiMatrix.a2;
matrix[0][2] = aiMatrix.a3;
matrix[0][3] = aiMatrix.a4;
matrix[1][0] = aiMatrix.b1;
matrix[1][1] = aiMatrix.b2;
matrix[1][2] = aiMatrix.b3;
matrix[1][3] = aiMatrix.b4;
matrix[2][0] = aiMatrix.c1;
matrix[2][1] = aiMatrix.c2;
matrix[2][2] = aiMatrix.c3;
matrix[2][3] = aiMatrix.c4;
matrix[3][0] = aiMatrix.d1;
matrix[3][1] = aiMatrix.d2;
matrix[3][2] = aiMatrix.d3;
matrix[3][3] = aiMatrix.d4;
matrix = glm::transpose(matrix);
}
void DVKModel::LoadFromFile(VkDevice device, const std::string& filename){
this->device = device;
int assimpFlags = aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenUVCoords | aiProcess_GenSmoothNormals;
loadSkin = true;
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(filename, assimpFlags);
LoadBones(scene);
LoadNode(scene->mRootNode, scene);
LoadAnim(scene);
}
void DVKModel::LoadBones(const aiScene* aiScene){
std::unordered_map<std::string, int32_t> boneIndexMap;
for (int32_t i = 0; i < (int32_t)aiScene->mNumMeshes; ++i){
aiMesh* aimesh = aiScene->mMeshes[i];
for (int32_t j = 0; j < (int32_t)aimesh->mNumBones; ++j){
aiBone* aibone = aimesh->mBones[j];
std::string name = aibone->mName.C_Str();
auto it = boneIndexMap.find(name);
if (it == boneIndexMap.end()){
// new bone
int32_t index = (int32_t)bones.size();
DVKBone* bone = new DVKBone();
bone->index = index;
bone->parent = -1;
bone->name = name;
FillMatrixWithAiMatrix(bone->inverseBindPose, aibone->mOffsetMatrix);
// 记录Bone信息
bones.push_back(bone);
bonesMap.insert(std::make_pair(name, bone));
// cache
boneIndexMap.insert(std::make_pair(name, index));
}
}
}
}
void DVKModel::LoadSkin(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene){
std::unordered_map<int32_t, int32_t> boneIndexMap;
for (int32_t i = 0; i < (int32_t)aiMesh->mNumBones; ++i){
aiBone* boneInfo = aiMesh->mBones[i];
std::string boneName(boneInfo->mName.C_Str());
int32_t boneIndex = bonesMap[boneName]->index;
// bone在mesh中的索引
int32_t meshBoneIndex = 0;
auto it = boneIndexMap.find(boneIndex);
if (it == boneIndexMap.end()){
meshBoneIndex = (int32_t)mesh->bones.size();
mesh->bones.push_back(boneIndex);
boneIndexMap.insert(std::make_pair(boneIndex, meshBoneIndex));
}
else{
meshBoneIndex = it->second;
}
for (uint32_t j = 0; j < boneInfo->mNumWeights; ++j){
uint32_t vertexID = boneInfo->mWeights[j].mVertexId;
float weight = boneInfo->mWeights[j].mWeight;
if (skinInfoMap.find(vertexID) == skinInfoMap.end()){
skinInfoMap.insert(std::make_pair(vertexID, DVKVertexSkin()));
}
DVKVertexSkin* info = &(skinInfoMap[vertexID]);
info->indices[info->used] = meshBoneIndex;
info->weights[info->used] = weight;
++info->used;
if (info->used >= 4){
break;
}
}
}
for (auto it = skinInfoMap.begin(); it != skinInfoMap.end(); ++it){
DVKVertexSkin& info = it->second;
for (int32_t i = info.used; i < 4; ++i){
info.indices[i] = 0;
info.weights[i] = 0.0f;
}
}
mesh->isSkin = true;
}
void DVKModel::LoadVertexDatas(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, std::vector<DVKVertex>& vertices, glm::vec3& mmax, glm::vec3& mmin, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene) {
for (int32_t i = 0; i < (int32_t)aiMesh->mNumVertices; ++i){
DVKVertex v;
v.pos = glm::vec3(aiMesh->mVertices[i].x, aiMesh->mVertices[i].y, aiMesh->mVertices[i].z);
if (aiMesh->HasTextureCoords(0))
v.uvs = glm::vec2(aiMesh->mTextureCoords[0][i].x, aiMesh->mTextureCoords[0][i].y);
v.normals = glm::vec3(aiMesh->mNormals[i].x, aiMesh->mNormals[i].y, aiMesh->mNormals[i].z);
DVKVertexSkin& skin = skinInfoMap[i];
if (mesh->isSkin) {
v.boneIDs = glm::vec4(skin.indices[0], skin.indices[1], skin.indices[2], skin.indices[3]);//默认0
v.weights = glm::vec4(skin.weights[0], skin.weights[1], skin.weights[2], skin.weights[3]);//默认1, 0, 0, 0
}
else {
v.boneIDs = glm::vec4(.0f);
v.weights = glm::vec4(1.0f, .0f, .0f, .0f);
}
vertices.push_back(v);
}
}
void DVKModel::LoadIndices(std::vector<uint32_t>& indices, const aiMesh* aiMesh, const aiScene* aiScene){
for (int32_t i = 0; i < (int32_t)aiMesh->mNumFaces; ++i){
indices.push_back(aiMesh->mFaces[i].mIndices[0]);
indices.push_back(aiMesh->mFaces[i].mIndices[1]);
indices.push_back(aiMesh->mFaces[i].mIndices[2]);
}
}
void DVKModel::LoadPrimitives(std::vector<DVKVertex>& vertices, std::vector<uint32_t>& indices, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene){
std::unordered_map<uint32_t, uint32_t> indicesMap;
DVKPrimitive* primitive = new DVKPrimitive();
primitive->vertices = vertices;
primitive->indices = indices;
createBuffer(device, indices.size() * sizeof(uint32_t), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitive->indexBuffer);
bufferData(device, indices.size() * sizeof(uint32_t), indices.data(), primitive->indexBuffer.memory);
createBuffer(device, vertices.size() * sizeof(DVKVertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitive->vertexBuffer);
bufferData(device, vertices.size() * sizeof(DVKVertex), vertices.data(), primitive->vertexBuffer.memory);
mesh->primitives.push_back(primitive);
}
DVKMesh* DVKModel::LoadMesh(const aiMesh* aiMesh, const aiScene* aiScene){
DVKMesh* mesh = new DVKMesh();
// load material
aiMaterial* material = aiScene->mMaterials[aiMesh->mMaterialIndex];
// load bones
std::unordered_map<uint32_t, DVKVertexSkin> skinInfoMap;
if (aiMesh->mNumBones > 0 && loadSkin){
LoadSkin(skinInfoMap, mesh, aiMesh, aiScene);
}
// load vertex data
std::vector<DVKVertex> vertices;
glm::vec3 mmin(FLT_MAX, FLT_MAX, FLT_MAX);
glm::vec3 mmax(-FLT_MAX, -FLT_MAX, -FLT_MAX);
LoadVertexDatas(skinInfoMap, vertices, mmax, mmin, mesh, aiMesh, aiScene);
#else
Vector3 mmin(MAX_FLT, MAX_FLT, MAX_FLT);
Vector3 mmax(-MAX_FLT, -MAX_FLT, -MAX_FLT);
LoadVertexDatas(skinInfoMap, vertices, mmax, mmin, mesh, aiMesh, aiScene);
// load indices
std::vector<uint32_t> indices;
LoadIndices(indices, aiMesh, aiScene);
// load primitives
LoadPrimitives(vertices, indices, mesh, aiMesh, aiScene);
mesh->bounding.min = mmin;
mesh->bounding.max = mmax;
mesh->bounding.UpdateCorners();
return mesh;
}
DVKNode* DVKModel::LoadNode(const aiNode* aiNode, const aiScene* aiScene){
DVKNode* vkNode = new DVKNode();
vkNode->name = aiNode->mName.C_Str();
if (rootNode == nullptr){
rootNode = vkNode;
}
// mesh
if (aiNode->mNumMeshes > 0){
for (uint32_t i = 0; i < aiNode->mNumMeshes; ++i){
DVKMesh* vkMesh = LoadMesh(aiScene->mMeshes[aiNode->mMeshes[i]], aiScene);
vkMesh->linkNode = vkNode;
vkNode->meshes.push_back(vkMesh);
meshes.push_back(vkMesh);
}
}
// nodes map
nodesMap.insert(std::make_pair(vkNode->name, vkNode));
linearNodes.push_back(vkNode);
// bones parent
int32_t boneParentIndex = -1;{
auto it = bonesMap.find(vkNode->name);
if (it != bonesMap.end()){
boneParentIndex = it->second->index;
}
}
// children node
for (int32_t i = 0; i < (int32_t)aiNode->mNumChildren; ++i){
DVKNode* childNode = LoadNode(aiNode->mChildren[i], aiScene);
childNode->parent = vkNode;
vkNode->children.push_back(childNode);
// bones relationship
{
auto it = bonesMap.find(childNode->name);
if (it != bonesMap.end()){
it->second->parent = boneParentIndex;
}
}
}
return vkNode;
}
void DVKModel::LoadAnim(const aiScene* aiScene) {
for (int32_t i = 0; i < (int32_t)aiScene->mNumAnimations; ++i) {
aiAnimation* aianimation = aiScene->mAnimations[i];
float timeTick = aianimation->mTicksPerSecond != 0 ? (float)aianimation->mTicksPerSecond : 25.0f
animations.push_back(DVKAnimation());
DVKAnimation& dvkAnimation = animations.back();
for (int32_t j = 0; j < (int32_t)aianimation->mNumChannels; ++j){
aiNodeAnim* nodeAnim = aianimation->mChannels[j];
std::string nodeName = nodeAnim->mNodeName.C_Str();
dvkAnimation.clips.insert(std::make_pair(nodeName, DVKAnimationClip()));
DVKAnimationClip& animClip = dvkAnimation.clips[nodeName];
animClip.nodeName = nodeName;
animClip.duration = 0.0f;
// position
for (int32_t index = 0; index < (int32_t)nodeAnim->mNumPositionKeys; ++index){
aiVectorKey& aikey = nodeAnim->mPositionKeys[index];
animClip.positions.keys.push_back((float)aikey.mTime / timeTick);
animClip.positions.values.push_back(glm::vec3(aikey.mValue.x, aikey.mValue.y, aikey.mValue.z));
animClip.duration = glm::max((float)aikey.mTime / timeTick, animClip.duration);
}
// scale
for (int32_t index = 0; index < (int32_t)nodeAnim->mNumScalingKeys; ++index){
aiVectorKey& aikey = nodeAnim->mScalingKeys[index];
animClip.scales.keys.push_back((float)aikey.mTime / timeTick);
animClip.scales.values.push_back(glm::vec3(aikey.mValue.x, aikey.mValue.y, aikey.mValue.z));
animClip.duration = glm::max((float)aikey.mTime / timeTick, animClip.duration);
}
// rotation
for (int32_t index = 0; index < (int32_t)nodeAnim->mNumRotationKeys; ++index){
aiQuatKey& aikey = nodeAnim->mRotationKeys[index];
animClip.rotations.keys.push_back((float)aikey.mTime / timeTick);
animClip.rotations.values.push_back(glm::quat(aikey.mValue.w, aikey.mValue.x, aikey.mValue.y, aikey.mValue.z));
animClip.duration = glm::max((float)aikey.mTime / timeTick, animClip.duration);
}
dvkAnimation.duration = glm::max(animClip.duration, dvkAnimation.duration);
}
}
}
void AppendScale(glm::mat4& m, const glm::vec3& scale) {
glm::mat4 matrix;
matrix = glm::mat4(1.0f);
matrix[0][0] = scale.x;
matrix[1][1] = scale.y;
matrix[2][2] = scale.z;
m = matrix * m;
}
void DVKModel::GotoAnimation(float time) {
if (animIndex == -1) {
return;
}
DVKAnimation& animation = animations[animIndex];
animation.time = glm::clamp(time, 0.0f, animation.duration);
// update nodes animation
for (auto it = animation.clips.begin(); it != animation.clips.end(); ++it){
DVKAnimationClip& clip = it->second;
DVKNode* node = nodesMap[clip.nodeName];
float alpha = 0.0f;
// rotation
glm::quat prevRot(1, 0, 0, 0);
glm::quat nextRot(1, 0, 0, 0);
clip.rotations.GetValue(animation.time, prevRot, nextRot, alpha);
glm::quat retRot = glm::lerp(prevRot, nextRot, alpha);
// position
glm::vec3 prevPos(0, 0, 0);
glm::vec3 nextPos(0, 0, 0);
clip.positions.GetValue(animation.time, prevPos, nextPos, alpha);
glm::vec3 retPos = prevPos + alpha * (nextPos - prevPos);//MMath::Lerp(prevPos, nextPos, alpha);
// scale
glm::vec3 prevScale(1, 1, 1);
glm::vec3 nextScale(1, 1, 1);
clip.scales.GetValue(animation.time, prevScale, nextScale, alpha);
glm::vec3 retScale = prevScale + alpha * (nextScale - prevScale);
node->localMatrix = glm::mat4(1.0f);
AppendScale(node->localMatrix, retScale);
node->localMatrix *= glm::mat4_cast(retRot);
node->localMatrix[3][0] += retPos.x;
node->localMatrix[3][1] += retPos.y;
node->localMatrix[3][2] += retPos.z;
}
// update bones
for (int32_t i = 0; i < bones.size(); ++i){
DVKBone* bone = bones[i];
DVKNode* node = nodesMap[bone->name];
// 注意行列矩阵的区别
bone->finalTransform = bone->inverseBindPose;
bone->finalTransform = node->GetGlobalMatrix() * bone->finalTransform;
}
}
我没有详细查看您的代码,但略读了一下,您似乎没有使用 aiNode::mTransformation
,您必须使用它才能获得正确的转换 w.r.t.骨骼的父骨骼。 ASSIMP 的文档对此参数的描述如下:
/** The transformation relative to the node's parent. */
C_STRUCT aiMatrix4x4 mTransformation;
并且在将 ASSIMP 的矩阵(行优先)转换为通常用于 OpenGL 或 Vulkan 的矩阵(通常是列优先)时必须小心。以下代码在这种情况下进行必要的转换:
/** Convert from a row-major ASSIMP matrix to a column-major GLM matrix */
glm::mat4 to_mat4(const aiMatrix4x4& aAssimpMat)
{
return glm::transpose(*reinterpret_cast<const glm::mat4*>(&aAssimpMat[0][0]));
}
库:assimp;模型:*.fbx
骨骼动画可以正常播放了。 但是角色的脖子是伸着的,一动不动
使用renderdoc发现输入vertex shader的顶点是没有问题的。但是输出顶点显示颈部异常。 我还是不知道问题出在哪里
角色的头不能动,脖子被拉长了。 我想知道到底哪里错了?有什么问题可以让头动不了?
这是错误发生时的结果:
将输入传递给着色器
for (size_t i = 0; i < MAX_BONES; ++i){
g_Bones.bones[i] = glm::mat4(1.0f);//为了让没有骨头的模型正常显示
}
for (int32_t i = 0; i < g_Model->meshes.size(); ++i) {
DVKMesh* mesh = g_Model->meshes[i];
g_Position.model = mesh->linkNode->GetGlobalMatrix();
for (int32_t j = 0; j < mesh->bones.size(); j++) {
DVKBone* bone = g_Model->bones[mesh->bones[j]];
g_Bones.bones[j] = bone->finalTransform;
g_Bones.bones[j] = glm::inverse(mesh->linkNode->GetGlobalMatrix()) * g_Bones.bones[j];
}
if (mesh->bones.empty()) {
g_Bones.bones[mesh->bones.size()] = glm::mat4(1.0f);
}
bufferData(g_VulkanBasic.device, sizeof(Position_UBO), &g_Position, g_PositionBuffer.memory, i * g_MinUniformBufferOffset);
}
bufferData(g_VulkanBasic.device, sizeof(BonesTransformBlock), g_Bones.bones, g_BonesBuffer.memory);
加载骨骼和加载皮肤
void FillMatrixWithAiMatrix(glm::mat4& matrix, const aiMatrix4x4& aiMatrix) {
matrix[0][0] = aiMatrix.a1;
matrix[0][1] = aiMatrix.a2;
matrix[0][2] = aiMatrix.a3;
matrix[0][3] = aiMatrix.a4;
matrix[1][0] = aiMatrix.b1;
matrix[1][1] = aiMatrix.b2;
matrix[1][2] = aiMatrix.b3;
matrix[1][3] = aiMatrix.b4;
matrix[2][0] = aiMatrix.c1;
matrix[2][1] = aiMatrix.c2;
matrix[2][2] = aiMatrix.c3;
matrix[2][3] = aiMatrix.c4;
matrix[3][0] = aiMatrix.d1;
matrix[3][1] = aiMatrix.d2;
matrix[3][2] = aiMatrix.d3;
matrix[3][3] = aiMatrix.d4;
matrix = glm::transpose(matrix);
}
void DVKModel::LoadBones(const aiScene* aiScene){
std::unordered_map<std::string, int32_t> boneIndexMap;
for (int32_t i = 0; i < (int32_t)aiScene->mNumMeshes; ++i){
aiMesh* aimesh = aiScene->mMeshes[i];
for (int32_t j = 0; j < (int32_t)aimesh->mNumBones; ++j){
aiBone* aibone = aimesh->mBones[j];
std::string name = aibone->mName.C_Str();
auto it = boneIndexMap.find(name);
if (it == boneIndexMap.end()){
// new bone
int32_t index = (int32_t)bones.size();
DVKBone* bone = new DVKBone();
bone->index = index;
bone->parent = -1;
bone->name = name;
FillMatrixWithAiMatrix(bone->inverseBindPose, aibone->mOffsetMatrix);
bones.push_back(bone);
bonesMap.insert(std::make_pair(name, bone));
boneIndexMap.insert(std::make_pair(name, index));
}
}
}
}
void DVKModel::LoadSkin(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene){
std::unordered_map<int32_t, int32_t> boneIndexMap;
for (int32_t i = 0; i < (int32_t)aiMesh->mNumBones; ++i){
aiBone* boneInfo = aiMesh->mBones[i];
std::string boneName(boneInfo->mName.C_Str());
int32_t boneIndex = bonesMap[boneName]->index;
// bone在mesh中的索引
int32_t meshBoneIndex = 0;
auto it = boneIndexMap.find(boneIndex);
if (it == boneIndexMap.end()){
meshBoneIndex = (int32_t)mesh->bones.size();
mesh->bones.push_back(boneIndex);
boneIndexMap.insert(std::make_pair(boneIndex, meshBoneIndex));
}
else{
meshBoneIndex = it->second;
}
for (uint32_t j = 0; j < boneInfo->mNumWeights; ++j){
uint32_t vertexID = boneInfo->mWeights[j].mVertexId;
float weight = boneInfo->mWeights[j].mWeight;
// 顶点->Bone
if (skinInfoMap.find(vertexID) == skinInfoMap.end()){
skinInfoMap.insert(std::make_pair(vertexID, DVKVertexSkin()));
}
DVKVertexSkin* info = &(skinInfoMap[vertexID]);
info->indices[info->used] = meshBoneIndex;
info->weights[info->used] = weight;
++info->used;
if (info->used >= 4){
break;
}
}
}
for (auto it = skinInfoMap.begin(); it != skinInfoMap.end(); ++it){
DVKVertexSkin& info = it->second;
for (int32_t i = info.used; i < 4; ++i){
info.indices[i] = 0;
info.weights[i] = 0.0f;
}
}
mesh->isSkin = true;
}
glsl 顶点着色器:
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec2 inUV0;
layout (location = 2) in vec3 inNormal;
layout (location = 3) in vec4 inSkinIndex;
layout (location = 4) in vec4 inSkinWeight;
layout (binding = 0) uniform MVPBlock {
mat4 modelMatrix;
mat4 viewMatrix;
mat4 projectionMatrix;
} uboMVP;
#define MAX_BONES 64
layout (binding = 1) uniform BonesTransformBlock{
mat4 bones[MAX_BONES];
} bonesData;
layout (location = 0) out vec2 outUV;
layout (location = 1) out vec3 outNormal;
layout (location = 2) out vec4 outColor;
void main() {
mat4 boneMatrix = bonesData.bones[int(inSkinIndex.x)] * inSkinWeight.x;
boneMatrix += bonesData.bones[int(inSkinIndex.y)] * inSkinWeight.y;
boneMatrix += bonesData.bones[int(inSkinIndex.z)] * inSkinWeight.z;
boneMatrix += bonesData.bones[int(inSkinIndex.w)] * inSkinWeight.w;
mat4 modeMatrix = uboMVP.modelMatrix * boneMatrix;
mat3 normalMatrix = transpose(inverse(mat3(modeMatrix)));
vec3 normal = normalize(normalMatrix * inNormal.xyz);
outUV = inUV0;
outNormal = normal;
outColor = inSkinWeight;
gl_Position = uboMVP.projectionMatrix * uboMVP.viewMatrix * modeMatrix * vec4(inPosition.xyz, 1.0);
}
这是全部代码: dvkmodel.h
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include "vulkanFrame.h"
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
struct DVKVertex {
glm::vec3 pos;
glm::vec2 uvs;
glm::vec3 normals;
glm::vec4 boneIDs;
glm::vec4 weights;
static VkVertexInputBindingDescription GetInputBinding(){
VkVertexInputBindingDescription vertexInputBinding = {};
vertexInputBinding.binding = 0;
vertexInputBinding.stride = sizeof(DVKVertex);
vertexInputBinding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
return vertexInputBinding;
}
static std::vector<VkVertexInputAttributeDescription>GetInputAttributes(){
VkFormat format[] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT };
uint32_t offset[] = { offsetof(DVKVertex, pos), offsetof(DVKVertex, uvs), offsetof(DVKVertex, normals), offsetof(DVKVertex, boneIDs), offsetof(DVKVertex, weights) };
std::vector<VkVertexInputAttributeDescription> vertexInputAttributs(sizeof(format) / sizeof(VkFormat));
for (size_t i = 0; i < vertexInputAttributs.size(); ++i) {
vertexInputAttributs[i].binding = 0;
vertexInputAttributs[i].location = i;
vertexInputAttributs[i].format = format[i];
vertexInputAttributs[i].offset = offset[i];
}
return vertexInputAttributs;
}
};
struct DVKBoundingBox {
glm::vec3 min;
glm::vec3 max;
glm::vec3 corners[8];
DVKBoundingBox(){
}
DVKBoundingBox{
}
};
struct DVKPrimitive{
BufferInfo indexBuffer;
BufferInfo vertexBuffer;
std::vector<DVKVertex>vertices;
std::vector<uint32_t> indices;
int32_t vertexCount = 0;
int32_t triangleNum = 0;
DVKPrimitive(){
}
~DVKPrimitive(){
}
void DrawOnly(VkCommandBuffer cmdBuffer){
if (vertexBuffer.size){
vkCmdDraw(cmdBuffer, vertices.size(), 1, 0, 0);
}
else{
vkCmdDrawIndexed(cmdBuffer, vertices.size(), 1, 0, 0, 0);
}
}
void BindOnly(VkCommandBuffer cmdBuffer){
VkDeviceSize offset = 0;
if (vertexBuffer.size){
vkCmdBindVertexBuffers(cmdBuffer, 0, 1, &vertexBuffer.buffer, &offset);
}
if (indexBuffer.size){
vkCmdBindIndexBuffer(cmdBuffer, indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
}
}
void BindDrawCmd(VkCommandBuffer cmdBuffer){
BindOnly(cmdBuffer);
DrawOnly(cmdBuffer);
}
};
struct DVKMaterialInfo{
std::string diffuse;
std::string normalmap;
std::string specular;
};
struct DVKBone{
std::string name;
int32_t index = -1;
int32_t parent = -1;
glm::mat4 inverseBindPose;
glm::mat4 finalTransform;
};
struct DVKVertexSkin{
int32_t used = 0;
int32_t indices[4];
glm::vec4 weights;
};
template <class ValueType>
struct DVKAnimChannel{
std::vector<float> keys;
std::vector<ValueType> values;
void GetValue(float key, ValueType& outPrevValue, ValueType& outNextValue, float& outAlpha){
outAlpha = 0.0f;
if (keys.size() == 0){
return;
}
if (key <= keys.front()){
outPrevValue = values.front();
outNextValue = values.front();
outAlpha = 0.0f;
return;
}
if (key >= keys.back()){
outPrevValue = values.back();
outNextValue = values.back();
outAlpha = 0.0f;
return;
}
int32_t frameIndex = 0;
for (int32_t i = 0; i < keys.size() - 1; ++i){
if (key <= keys[i + 1]){
frameIndex = i;
break;
}
}
outPrevValue = values[frameIndex + 0];
outNextValue = values[frameIndex + 1];
float prevKey = keys[frameIndex + 0];
float nextKey = keys[frameIndex + 1];
outAlpha = (key - prevKey) / (nextKey - prevKey);
}
};
struct DVKAnimationClip {
std::string nodeName;
float duration;
DVKAnimChannel<glm::vec3> positions;
DVKAnimChannel<glm::vec3> scales;
DVKAnimChannel<glm::quat> rotations;
};
struct DVKAnimation{
std::string name;
float time = 0.0f;
float duration = 0.0f;
float speed = 1.0f;
std::unordered_map<std::string, DVKAnimationClip> clips;
};
struct DVKMesh{
typedef std::vector<DVKPrimitive*> DVKPrimitives;
DVKPrimitives primitives;
DVKBoundingBox bounding;
DVKNode* linkNode;
std::vector<int32_t>bones;
bool isSkin = false;
DVKMaterialInfo material;
int32_t vertexCount;
int32_t triangleCount;
DVKMesh()
: linkNode(nullptr)
, vertexCount(0)
, triangleCount(0){
}
void BindOnly(VkCommandBuffer cmdBuffer){
for (int i = 0; i < primitives.size(); ++i){
primitives[i]->BindOnly(cmdBuffer);
}
}
void DrawOnly(VkCommandBuffer cmdBuffer){
for (int i = 0; i < primitives.size(); ++i){
primitives[i]->DrawOnly(cmdBuffer);
}
}
void BindDrawCmd(VkCommandBuffer cmdBuffer){
for (int i = 0; i < primitives.size(); ++i){
primitives[i]->BindDrawCmd(cmdBuffer);
}
}
~DVKMesh(){
for (int i = 0; i < primitives.size(); ++i){
delete primitives[i];
}
primitives.clear();
linkNode = nullptr;
}
};
struct DVKNode{
std::string name;
std::vector<DVKMesh*> meshes;
DVKNode* parent;
std::vector<DVKNode*> children;
glm::mat4 localMatrix;
glm::mat4 globalMatrix;
DVKNode()
: name("None")
, parent(nullptr){
}
const glm::mat4& GetLocalMatrix() {
return localMatrix;
}
glm::mat4& GetGlobalMatrix() {
globalMatrix = localMatrix;
if (parent) {
//globalMatrix.Append(parent->GetGlobalMatrix());
globalMatrix = parent->GetGlobalMatrix() * globalMatrix;
}
return globalMatrix;
}
void CalcBounds(DVKBoundingBox& outBounds) {
if (meshes.size() > 0) {
const glm::mat4& matrix = GetGlobalMatrix();
for (int32_t i = 0; i < meshes.size(); ++i) {
glm::vec3 mmin = matrix * glm::vec4(meshes[i]->bounding.min, 1.0f);
glm::vec3 mmax = matrix * glm::vec4(meshes[i]->bounding.max, 1.0f);
outBounds.min = glm::min(outBounds.min, mmin);
outBounds.min = glm::min(outBounds.min, mmax);
outBounds.max = glm::max(outBounds.max, mmin);
outBounds.max = glm::max(outBounds.max, mmax);
}
}
for (int32_t i = 0; i < children.size(); ++i) {
children[i]->CalcBounds(outBounds);
}
}
DVKBoundingBox GetBounds() {
DVKBoundingBox bounds;
bounds.min = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
bounds.max = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
CalcBounds(bounds);
bounds.UpdateCorners();
return bounds;
}
~DVKNode(){
for (int32_t i = 0; i < meshes.size(); ++i){
delete meshes[i];
}
meshes.clear();
for (int32_t i = 0; i < children.size(); ++i){
delete children[i];
}
children.clear();
}
};
class DVKModel{
public:
DVKModel()
: device(nullptr)
, rootNode(nullptr){
}
~DVKModel(){
delete rootNode;
rootNode = nullptr;
device = nullptr;
meshes.clear();
linearNodes.clear();
for (int32_t i = 0; i < bones.size(); ++i){
delete bones[i];
}
bones.clear();
}
void LoadFromFile(VkDevice device, const std::string& filename);
protected:
DVKNode* LoadNode(const aiNode* node, const aiScene* scene);
DVKMesh* LoadMesh(const aiMesh* mesh, const aiScene* scene);
void LoadBones(const aiScene* aiScene);
void LoadSkin(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene);
void LoadVertexDatas(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, std::vector<DVKVertex>& vertices, glm::vec3& mmax, glm::vec3& mmin, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene);
void LoadIndices(std::vector<uint32_t>& indices, const aiMesh* aiMesh, const aiScene* aiScene);
void LoadPrimitives(std::vector<DVKVertex>& vertices, std::vector<uint32_t>& indices, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene);
void LoadAnim(const aiScene* aiScene);
public:
typedef std::unordered_map<std::string, DVKNode*> NodesMap;
typedef std::unordered_map<std::string, DVKBone*> BonesMap;
VkDevice device;
DVKNode* rootNode;
std::vector<DVKNode*> linearNodes;
std::vector<DVKMesh*> meshes;
NodesMap nodesMap;
std::vector<DVKBone*> bones;
BonesMap bonesMap;
std::vector<DVKAnimation> animations;
int32_t animIndex = -1;
private:
bool loadSkin = false;
};
dvkmodel.cpp
#include "DVKModel.h"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/cimport.h>
void FillMatrixWithAiMatrix(glm::mat4& matrix, const aiMatrix4x4& aiMatrix) {
matrix[0][0] = aiMatrix.a1;
matrix[0][1] = aiMatrix.a2;
matrix[0][2] = aiMatrix.a3;
matrix[0][3] = aiMatrix.a4;
matrix[1][0] = aiMatrix.b1;
matrix[1][1] = aiMatrix.b2;
matrix[1][2] = aiMatrix.b3;
matrix[1][3] = aiMatrix.b4;
matrix[2][0] = aiMatrix.c1;
matrix[2][1] = aiMatrix.c2;
matrix[2][2] = aiMatrix.c3;
matrix[2][3] = aiMatrix.c4;
matrix[3][0] = aiMatrix.d1;
matrix[3][1] = aiMatrix.d2;
matrix[3][2] = aiMatrix.d3;
matrix[3][3] = aiMatrix.d4;
matrix = glm::transpose(matrix);
}
void DVKModel::LoadFromFile(VkDevice device, const std::string& filename){
this->device = device;
int assimpFlags = aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenUVCoords | aiProcess_GenSmoothNormals;
loadSkin = true;
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(filename, assimpFlags);
LoadBones(scene);
LoadNode(scene->mRootNode, scene);
LoadAnim(scene);
}
void DVKModel::LoadBones(const aiScene* aiScene){
std::unordered_map<std::string, int32_t> boneIndexMap;
for (int32_t i = 0; i < (int32_t)aiScene->mNumMeshes; ++i){
aiMesh* aimesh = aiScene->mMeshes[i];
for (int32_t j = 0; j < (int32_t)aimesh->mNumBones; ++j){
aiBone* aibone = aimesh->mBones[j];
std::string name = aibone->mName.C_Str();
auto it = boneIndexMap.find(name);
if (it == boneIndexMap.end()){
// new bone
int32_t index = (int32_t)bones.size();
DVKBone* bone = new DVKBone();
bone->index = index;
bone->parent = -1;
bone->name = name;
FillMatrixWithAiMatrix(bone->inverseBindPose, aibone->mOffsetMatrix);
// 记录Bone信息
bones.push_back(bone);
bonesMap.insert(std::make_pair(name, bone));
// cache
boneIndexMap.insert(std::make_pair(name, index));
}
}
}
}
void DVKModel::LoadSkin(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene){
std::unordered_map<int32_t, int32_t> boneIndexMap;
for (int32_t i = 0; i < (int32_t)aiMesh->mNumBones; ++i){
aiBone* boneInfo = aiMesh->mBones[i];
std::string boneName(boneInfo->mName.C_Str());
int32_t boneIndex = bonesMap[boneName]->index;
// bone在mesh中的索引
int32_t meshBoneIndex = 0;
auto it = boneIndexMap.find(boneIndex);
if (it == boneIndexMap.end()){
meshBoneIndex = (int32_t)mesh->bones.size();
mesh->bones.push_back(boneIndex);
boneIndexMap.insert(std::make_pair(boneIndex, meshBoneIndex));
}
else{
meshBoneIndex = it->second;
}
for (uint32_t j = 0; j < boneInfo->mNumWeights; ++j){
uint32_t vertexID = boneInfo->mWeights[j].mVertexId;
float weight = boneInfo->mWeights[j].mWeight;
if (skinInfoMap.find(vertexID) == skinInfoMap.end()){
skinInfoMap.insert(std::make_pair(vertexID, DVKVertexSkin()));
}
DVKVertexSkin* info = &(skinInfoMap[vertexID]);
info->indices[info->used] = meshBoneIndex;
info->weights[info->used] = weight;
++info->used;
if (info->used >= 4){
break;
}
}
}
for (auto it = skinInfoMap.begin(); it != skinInfoMap.end(); ++it){
DVKVertexSkin& info = it->second;
for (int32_t i = info.used; i < 4; ++i){
info.indices[i] = 0;
info.weights[i] = 0.0f;
}
}
mesh->isSkin = true;
}
void DVKModel::LoadVertexDatas(std::unordered_map<uint32_t, DVKVertexSkin>& skinInfoMap, std::vector<DVKVertex>& vertices, glm::vec3& mmax, glm::vec3& mmin, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene) {
for (int32_t i = 0; i < (int32_t)aiMesh->mNumVertices; ++i){
DVKVertex v;
v.pos = glm::vec3(aiMesh->mVertices[i].x, aiMesh->mVertices[i].y, aiMesh->mVertices[i].z);
if (aiMesh->HasTextureCoords(0))
v.uvs = glm::vec2(aiMesh->mTextureCoords[0][i].x, aiMesh->mTextureCoords[0][i].y);
v.normals = glm::vec3(aiMesh->mNormals[i].x, aiMesh->mNormals[i].y, aiMesh->mNormals[i].z);
DVKVertexSkin& skin = skinInfoMap[i];
if (mesh->isSkin) {
v.boneIDs = glm::vec4(skin.indices[0], skin.indices[1], skin.indices[2], skin.indices[3]);//默认0
v.weights = glm::vec4(skin.weights[0], skin.weights[1], skin.weights[2], skin.weights[3]);//默认1, 0, 0, 0
}
else {
v.boneIDs = glm::vec4(.0f);
v.weights = glm::vec4(1.0f, .0f, .0f, .0f);
}
vertices.push_back(v);
}
}
void DVKModel::LoadIndices(std::vector<uint32_t>& indices, const aiMesh* aiMesh, const aiScene* aiScene){
for (int32_t i = 0; i < (int32_t)aiMesh->mNumFaces; ++i){
indices.push_back(aiMesh->mFaces[i].mIndices[0]);
indices.push_back(aiMesh->mFaces[i].mIndices[1]);
indices.push_back(aiMesh->mFaces[i].mIndices[2]);
}
}
void DVKModel::LoadPrimitives(std::vector<DVKVertex>& vertices, std::vector<uint32_t>& indices, DVKMesh* mesh, const aiMesh* aiMesh, const aiScene* aiScene){
std::unordered_map<uint32_t, uint32_t> indicesMap;
DVKPrimitive* primitive = new DVKPrimitive();
primitive->vertices = vertices;
primitive->indices = indices;
createBuffer(device, indices.size() * sizeof(uint32_t), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitive->indexBuffer);
bufferData(device, indices.size() * sizeof(uint32_t), indices.data(), primitive->indexBuffer.memory);
createBuffer(device, vertices.size() * sizeof(DVKVertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, primitive->vertexBuffer);
bufferData(device, vertices.size() * sizeof(DVKVertex), vertices.data(), primitive->vertexBuffer.memory);
mesh->primitives.push_back(primitive);
}
DVKMesh* DVKModel::LoadMesh(const aiMesh* aiMesh, const aiScene* aiScene){
DVKMesh* mesh = new DVKMesh();
// load material
aiMaterial* material = aiScene->mMaterials[aiMesh->mMaterialIndex];
// load bones
std::unordered_map<uint32_t, DVKVertexSkin> skinInfoMap;
if (aiMesh->mNumBones > 0 && loadSkin){
LoadSkin(skinInfoMap, mesh, aiMesh, aiScene);
}
// load vertex data
std::vector<DVKVertex> vertices;
glm::vec3 mmin(FLT_MAX, FLT_MAX, FLT_MAX);
glm::vec3 mmax(-FLT_MAX, -FLT_MAX, -FLT_MAX);
LoadVertexDatas(skinInfoMap, vertices, mmax, mmin, mesh, aiMesh, aiScene);
#else
Vector3 mmin(MAX_FLT, MAX_FLT, MAX_FLT);
Vector3 mmax(-MAX_FLT, -MAX_FLT, -MAX_FLT);
LoadVertexDatas(skinInfoMap, vertices, mmax, mmin, mesh, aiMesh, aiScene);
// load indices
std::vector<uint32_t> indices;
LoadIndices(indices, aiMesh, aiScene);
// load primitives
LoadPrimitives(vertices, indices, mesh, aiMesh, aiScene);
mesh->bounding.min = mmin;
mesh->bounding.max = mmax;
mesh->bounding.UpdateCorners();
return mesh;
}
DVKNode* DVKModel::LoadNode(const aiNode* aiNode, const aiScene* aiScene){
DVKNode* vkNode = new DVKNode();
vkNode->name = aiNode->mName.C_Str();
if (rootNode == nullptr){
rootNode = vkNode;
}
// mesh
if (aiNode->mNumMeshes > 0){
for (uint32_t i = 0; i < aiNode->mNumMeshes; ++i){
DVKMesh* vkMesh = LoadMesh(aiScene->mMeshes[aiNode->mMeshes[i]], aiScene);
vkMesh->linkNode = vkNode;
vkNode->meshes.push_back(vkMesh);
meshes.push_back(vkMesh);
}
}
// nodes map
nodesMap.insert(std::make_pair(vkNode->name, vkNode));
linearNodes.push_back(vkNode);
// bones parent
int32_t boneParentIndex = -1;{
auto it = bonesMap.find(vkNode->name);
if (it != bonesMap.end()){
boneParentIndex = it->second->index;
}
}
// children node
for (int32_t i = 0; i < (int32_t)aiNode->mNumChildren; ++i){
DVKNode* childNode = LoadNode(aiNode->mChildren[i], aiScene);
childNode->parent = vkNode;
vkNode->children.push_back(childNode);
// bones relationship
{
auto it = bonesMap.find(childNode->name);
if (it != bonesMap.end()){
it->second->parent = boneParentIndex;
}
}
}
return vkNode;
}
void DVKModel::LoadAnim(const aiScene* aiScene) {
for (int32_t i = 0; i < (int32_t)aiScene->mNumAnimations; ++i) {
aiAnimation* aianimation = aiScene->mAnimations[i];
float timeTick = aianimation->mTicksPerSecond != 0 ? (float)aianimation->mTicksPerSecond : 25.0f
animations.push_back(DVKAnimation());
DVKAnimation& dvkAnimation = animations.back();
for (int32_t j = 0; j < (int32_t)aianimation->mNumChannels; ++j){
aiNodeAnim* nodeAnim = aianimation->mChannels[j];
std::string nodeName = nodeAnim->mNodeName.C_Str();
dvkAnimation.clips.insert(std::make_pair(nodeName, DVKAnimationClip()));
DVKAnimationClip& animClip = dvkAnimation.clips[nodeName];
animClip.nodeName = nodeName;
animClip.duration = 0.0f;
// position
for (int32_t index = 0; index < (int32_t)nodeAnim->mNumPositionKeys; ++index){
aiVectorKey& aikey = nodeAnim->mPositionKeys[index];
animClip.positions.keys.push_back((float)aikey.mTime / timeTick);
animClip.positions.values.push_back(glm::vec3(aikey.mValue.x, aikey.mValue.y, aikey.mValue.z));
animClip.duration = glm::max((float)aikey.mTime / timeTick, animClip.duration);
}
// scale
for (int32_t index = 0; index < (int32_t)nodeAnim->mNumScalingKeys; ++index){
aiVectorKey& aikey = nodeAnim->mScalingKeys[index];
animClip.scales.keys.push_back((float)aikey.mTime / timeTick);
animClip.scales.values.push_back(glm::vec3(aikey.mValue.x, aikey.mValue.y, aikey.mValue.z));
animClip.duration = glm::max((float)aikey.mTime / timeTick, animClip.duration);
}
// rotation
for (int32_t index = 0; index < (int32_t)nodeAnim->mNumRotationKeys; ++index){
aiQuatKey& aikey = nodeAnim->mRotationKeys[index];
animClip.rotations.keys.push_back((float)aikey.mTime / timeTick);
animClip.rotations.values.push_back(glm::quat(aikey.mValue.w, aikey.mValue.x, aikey.mValue.y, aikey.mValue.z));
animClip.duration = glm::max((float)aikey.mTime / timeTick, animClip.duration);
}
dvkAnimation.duration = glm::max(animClip.duration, dvkAnimation.duration);
}
}
}
void AppendScale(glm::mat4& m, const glm::vec3& scale) {
glm::mat4 matrix;
matrix = glm::mat4(1.0f);
matrix[0][0] = scale.x;
matrix[1][1] = scale.y;
matrix[2][2] = scale.z;
m = matrix * m;
}
void DVKModel::GotoAnimation(float time) {
if (animIndex == -1) {
return;
}
DVKAnimation& animation = animations[animIndex];
animation.time = glm::clamp(time, 0.0f, animation.duration);
// update nodes animation
for (auto it = animation.clips.begin(); it != animation.clips.end(); ++it){
DVKAnimationClip& clip = it->second;
DVKNode* node = nodesMap[clip.nodeName];
float alpha = 0.0f;
// rotation
glm::quat prevRot(1, 0, 0, 0);
glm::quat nextRot(1, 0, 0, 0);
clip.rotations.GetValue(animation.time, prevRot, nextRot, alpha);
glm::quat retRot = glm::lerp(prevRot, nextRot, alpha);
// position
glm::vec3 prevPos(0, 0, 0);
glm::vec3 nextPos(0, 0, 0);
clip.positions.GetValue(animation.time, prevPos, nextPos, alpha);
glm::vec3 retPos = prevPos + alpha * (nextPos - prevPos);//MMath::Lerp(prevPos, nextPos, alpha);
// scale
glm::vec3 prevScale(1, 1, 1);
glm::vec3 nextScale(1, 1, 1);
clip.scales.GetValue(animation.time, prevScale, nextScale, alpha);
glm::vec3 retScale = prevScale + alpha * (nextScale - prevScale);
node->localMatrix = glm::mat4(1.0f);
AppendScale(node->localMatrix, retScale);
node->localMatrix *= glm::mat4_cast(retRot);
node->localMatrix[3][0] += retPos.x;
node->localMatrix[3][1] += retPos.y;
node->localMatrix[3][2] += retPos.z;
}
// update bones
for (int32_t i = 0; i < bones.size(); ++i){
DVKBone* bone = bones[i];
DVKNode* node = nodesMap[bone->name];
// 注意行列矩阵的区别
bone->finalTransform = bone->inverseBindPose;
bone->finalTransform = node->GetGlobalMatrix() * bone->finalTransform;
}
}
我没有详细查看您的代码,但略读了一下,您似乎没有使用 aiNode::mTransformation
,您必须使用它才能获得正确的转换 w.r.t.骨骼的父骨骼。 ASSIMP 的文档对此参数的描述如下:
/** The transformation relative to the node's parent. */
C_STRUCT aiMatrix4x4 mTransformation;
并且在将 ASSIMP 的矩阵(行优先)转换为通常用于 OpenGL 或 Vulkan 的矩阵(通常是列优先)时必须小心。以下代码在这种情况下进行必要的转换:
/** Convert from a row-major ASSIMP matrix to a column-major GLM matrix */
glm::mat4 to_mat4(const aiMatrix4x4& aAssimpMat)
{
return glm::transpose(*reinterpret_cast<const glm::mat4*>(&aAssimpMat[0][0]));
}