diff --git a/src/loaders/model.c b/src/loaders/model.c index 9f3a30bf..0f1a8125 100644 --- a/src/loaders/model.c +++ b/src/loaders/model.c @@ -3,6 +3,7 @@ #include "filesystem/file.h" #include "math/math.h" #include "math/mat4.h" +#include "math/quat.h" #include "math/vec3.h" #include #include @@ -149,6 +150,7 @@ static void assimpSumChildren(struct aiNode* assimpNode, int* totalChildren) { static void assimpNodeTraversal(ModelData* modelData, struct aiNode* assimpNode, int* nodeId) { int currentIndex = *nodeId; ModelNode* node = &modelData->nodes[currentIndex]; + node->name = strndup(assimpNode->mName.data, assimpNode->mName.length); // Transform struct aiMatrix4x4 m = assimpNode->mTransformation; @@ -195,6 +197,7 @@ ModelData* lovrModelDataCreate(Blob* blob) { modelData->hasNormals = false; modelData->hasUVs = false; modelData->hasVertexColors = false; + modelData->hasBones = false; for (unsigned int m = 0; m < scene->mNumMeshes; m++) { struct aiMesh* assimpMesh = scene->mMeshes[m]; @@ -203,6 +206,7 @@ ModelData* lovrModelDataCreate(Blob* blob) { modelData->hasNormals |= assimpMesh->mNormals != NULL; modelData->hasUVs |= assimpMesh->mTextureCoords[0] != NULL; modelData->hasVertexColors |= assimpMesh->mColors[0] != NULL; + modelData->hasBones |= assimpMesh->mNumBones > 0; } // Allocate @@ -213,19 +217,25 @@ ModelData* lovrModelDataCreate(Blob* blob) { modelData->stride += (modelData->hasNormals ? 3 : 0) * sizeof(float); modelData->stride += (modelData->hasUVs ? 2 : 0) * sizeof(float); modelData->stride += (modelData->hasVertexColors ? 4 : 0) * sizeof(uint8_t); + modelData->boneOffset = modelData->stride; + modelData->stride += (modelData->hasBones ? 4 : 0) * sizeof(uint8_t); + modelData->stride += (modelData->hasBones ? 4 : 0) * sizeof(float); modelData->vertices.data = malloc(modelData->stride * modelData->vertexCount); modelData->indices.data = malloc(modelData->indexCount * modelData->indexSize); + vec_init(&modelData->bones); + map_init(&modelData->boneMap); + // Load vertices - ModelVertices vertices = modelData->vertices; ModelIndices indices = modelData->indices; - int vertex = 0; - int index = 0; + uint32_t vertex = 0; + uint32_t index = 0; for (unsigned int m = 0; m < scene->mNumMeshes; m++) { struct aiMesh* assimpMesh = scene->mMeshes[m]; modelData->primitives[m].material = assimpMesh->mMaterialIndex; modelData->primitives[m].drawStart = index; modelData->primitives[m].drawCount = 0; + uint32_t baseVertex = vertex; // Indices for (unsigned int f = 0; f < assimpMesh->mNumFaces; f++) { @@ -236,16 +246,20 @@ ModelData* lovrModelDataCreate(Blob* blob) { if (modelData->indexSize == sizeof(uint16_t)) { for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) { - indices.shorts[index++] = vertex + assimpFace.mIndices[i]; + indices.shorts[index++] = baseVertex + assimpFace.mIndices[i]; } } else { for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) { - indices.ints[index++] = vertex + assimpFace.mIndices[i]; + indices.ints[index++] = baseVertex + assimpFace.mIndices[i]; } } } + // Vertices for (unsigned int v = 0; v < assimpMesh->mNumVertices; v++) { + ModelVertices vertices = modelData->vertices; + vertices.bytes += vertex * modelData->stride; + *vertices.floats++ = assimpMesh->mVertices[v].x; *vertices.floats++ = assimpMesh->mVertices[v].y; *vertices.floats++ = assimpMesh->mVertices[v].z; @@ -288,6 +302,41 @@ ModelData* lovrModelDataCreate(Blob* blob) { vertex++; } + + // Bones + for (unsigned int b = 0; b < assimpMesh->mNumBones; b++) { + struct aiBone* assimpBone = assimpMesh->mBones[b]; + const char* boneName = assimpBone->mName.data; + + if (!map_get(&modelData->boneMap, boneName)) { + Bone bone; + bone.name = strndup(boneName, assimpBone->mName.length); + aiTransposeMatrix4(&assimpBone->mOffsetMatrix); + mat4_set(bone.offset, (float*) &assimpBone->mOffsetMatrix); + map_set(&modelData->boneMap, bone.name, modelData->bones.length); + vec_push(&modelData->bones, bone); + } + + uint32_t boneIndex = *map_get(&modelData->boneMap, boneName); + + for (unsigned int w = 0; w < assimpBone->mNumWeights; w++) { + uint32_t vertexIndex = baseVertex + assimpBone->mWeights[w].mVertexId; + float weight = assimpBone->mWeights[w].mWeight; + ModelVertices vertices = modelData->vertices; + vertices.bytes += vertexIndex * modelData->stride; + uint32_t* bones = (uint32_t*) (vertices.bytes + modelData->boneOffset); + float* weights = (float*) (bones + MAX_BONES_PER_VERTEX); + + int boneSlot = 0; + while (weights[boneSlot] > 0) { + boneSlot++; + lovrAssert(boneSlot < MAX_BONES_PER_VERTEX, "Too many bones for vertex %d", vertexIndex); + } + + bones[boneSlot] = boneIndex; + weights[boneSlot] = weight; + } + } } // Materials @@ -330,6 +379,56 @@ ModelData* lovrModelDataCreate(Blob* blob) { int nodeIndex = 0; assimpNodeTraversal(modelData, scene->mRootNode, &nodeIndex); + // Animations + modelData->animationCount = scene->mNumAnimations; + modelData->animations = malloc(modelData->animationCount * sizeof(AnimationData*)); + for (int i = 0; i < modelData->animationCount; i++) { + struct aiAnimation* assimpAnimation = scene->mAnimations[i]; + AnimationData* animationData = lovrAnimationDataCreate(assimpAnimation->mName.data); + animationData->channelCount = assimpAnimation->mNumChannels; + + for (int j = 0; j < animationData->channelCount; j++) { + struct aiNodeAnim* assimpChannel = assimpAnimation->mChannels[j]; + AnimationChannel channel; + + channel.node = strndup(assimpChannel->mNodeName.data, assimpChannel->mNodeName.length); + vec_init(&channel.positionKeyframes); + vec_init(&channel.rotationKeyframes); + vec_init(&channel.scaleKeyframes); + + for (unsigned int k = 0; k < assimpChannel->mNumPositionKeys; k++) { + struct aiVectorKey assimpKeyframe = assimpChannel->mPositionKeys[k]; + struct aiVector3D position = assimpKeyframe.mValue; + Keyframe keyframe; + keyframe.time = assimpKeyframe.mTime; + vec3_set(keyframe.data, position.x, position.y, position.z); + vec_push(&channel.positionKeyframes, keyframe); + } + + for (unsigned int k = 0; k < assimpChannel->mNumRotationKeys; k++) { + struct aiQuatKey assimpKeyframe = assimpChannel->mRotationKeys[k]; + struct aiQuaternion quaternion = assimpKeyframe.mValue; + Keyframe keyframe; + keyframe.time = assimpKeyframe.mTime; + quat_set(keyframe.data, quaternion.x, quaternion.y, quaternion.z, quaternion.w); + vec_push(&channel.rotationKeyframes, keyframe); + } + + for (unsigned int k = 0; k < assimpChannel->mNumScalingKeys; k++) { + struct aiVectorKey assimpKeyframe = assimpChannel->mScalingKeys[k]; + struct aiVector3D scale = assimpKeyframe.mValue; + Keyframe keyframe; + keyframe.time = assimpKeyframe.mTime; + vec3_set(keyframe.data, scale.x, scale.y, scale.z); + vec_push(&channel.scaleKeyframes, keyframe); + } + + map_set(&animationData->channels, channel.node, channel); + } + + modelData->animations[i] = animationData; + } + aiReleaseImport(scene); return modelData; } @@ -340,6 +439,10 @@ void lovrModelDataDestroy(ModelData* modelData) { vec_deinit(&modelData->nodes[i].primitives); } + for (int i = 0; i < modelData->animationCount; i++) { + lovrAnimationDataDestroy(modelData->animations[i]); + } + for (int i = 0; i < modelData->materialCount; i++) { lovrMaterialDataDestroy(&modelData->materials[i]); } diff --git a/src/loaders/model.h b/src/loaders/model.h index 1a1f2ab1..63fcc7d2 100644 --- a/src/loaders/model.h +++ b/src/loaders/model.h @@ -1,10 +1,13 @@ #include "filesystem/blob.h" +#include "loaders/animation.h" #include "loaders/material.h" #include "util.h" #include "lib/vec/vec.h" #pragma once +#define MAX_BONES_PER_VERTEX 4 + typedef union { void* data; uint8_t* bytes; @@ -25,20 +28,32 @@ typedef struct { } ModelPrimitive; typedef struct ModelNode { + const char* name; float transform[16]; int parent; vec_uint_t children; vec_uint_t primitives; } ModelNode; +typedef struct { + const char* name; + float offset[16]; +} Bone; + +typedef vec_t(Bone) vec_bone_t; + typedef struct { ModelNode* nodes; ModelPrimitive* primitives; + vec_bone_t bones; + map_int_t boneMap; + AnimationData** animations; MaterialData* materials; ModelVertices vertices; ModelIndices indices; int nodeCount; int primitiveCount; + int animationCount; int materialCount; int vertexCount; int indexCount; @@ -46,6 +61,8 @@ typedef struct { bool hasNormals; bool hasUVs; bool hasVertexColors; + bool hasBones; + size_t boneOffset; size_t stride; } ModelData;