lovr/src/data/model.c

575 lines
20 KiB
C
Raw Normal View History

2017-12-10 20:36:22 +00:00
#include "data/model.h"
2018-01-30 05:44:32 +00:00
#include "data/texture.h"
2017-10-21 07:19:05 +00:00
#include "filesystem/filesystem.h"
2017-10-22 00:35:50 +00:00
#include "filesystem/file.h"
#include "math/math.h"
2017-01-21 03:55:54 +00:00
#include "math/mat4.h"
2017-11-02 06:10:21 +00:00
#include "math/quat.h"
2017-11-02 02:27:58 +00:00
#include "math/vec3.h"
2017-12-01 05:17:54 +00:00
#include <float.h>
#include <limits.h>
2016-10-29 06:40:31 +00:00
#include <stdlib.h>
2017-10-22 00:35:50 +00:00
#include <stdio.h>
2017-10-21 07:19:05 +00:00
#include <assimp/cfileio.h>
2016-10-04 04:54:27 +00:00
#include <assimp/cimport.h>
#include <assimp/config.h>
2017-09-20 06:49:10 +00:00
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/matrix4x4.h>
#include <assimp/vector3.h>
2016-10-04 04:54:27 +00:00
#include <assimp/postprocess.h>
2016-10-29 22:18:10 +00:00
2018-01-30 05:44:32 +00:00
static void normalizePath(char* path, char* dst, size_t size) {
char* slash = path;
while ((slash = strchr(path, '\\')) != NULL) { *slash++ = '/'; }
if (path[0] == '/') {
strncpy(dst, path, size);
return;
}
memset(dst, 0, size);
while (*path != '\0') {
if (*path == '/') {
path++;
continue;
}
if (*path == '.') {
if (path[1] == '\0' || path[1] == '/') {
path++;
continue;
}
if (path[1] == '.' && (path[2] == '\0' || path[2] == '/')) {
path += 2;
while ((--dst)[-1] != '/');
continue;
}
}
while (*path != '\0' && *path != '/') {
*dst++ = *path++;
}
*dst++ = '/';
}
*--dst = '\0';
}
static void assimpSumChildren(struct aiNode* assimpNode, int* totalChildren) {
(*totalChildren)++;
for (unsigned int i = 0; i < assimpNode->mNumChildren; i++) {
assimpSumChildren(assimpNode->mChildren[i], totalChildren);
}
}
static void assimpNodeTraversal(ModelData* modelData, struct aiNode* assimpNode, int* nodeId) {
int currentIndex = *nodeId;
ModelNode* node = &modelData->nodes[currentIndex];
node->name = strdup(assimpNode->mName.data);
map_set(&modelData->nodeMap, node->name, currentIndex);
// Transform
struct aiMatrix4x4 m = assimpNode->mTransformation;
aiTransposeMatrix4(&m);
mat4_set(node->transform, (float*) &m);
if (node->parent == -1) {
mat4_set(node->globalTransform, node->transform);
} else {
mat4_set(node->globalTransform, modelData->nodes[node->parent].globalTransform);
mat4_multiply(node->globalTransform, node->transform);
}
// Primitives
vec_init(&node->primitives);
vec_pusharr(&node->primitives, assimpNode->mMeshes, assimpNode->mNumMeshes);
// Children
vec_init(&node->children);
for (unsigned int n = 0; n < assimpNode->mNumChildren; n++) {
(*nodeId)++;
vec_push(&node->children, *nodeId);
ModelNode* child = &modelData->nodes[*nodeId];
child->parent = currentIndex;
assimpNodeTraversal(modelData, assimpNode->mChildren[n], nodeId);
}
}
2018-01-30 05:44:32 +00:00
static Color readMaterialColor(struct aiMaterial* assimpMaterial, const char* key, unsigned int type, unsigned int index) {
struct aiColor4D assimpColor;
if (aiGetMaterialColor(assimpMaterial, key, type, index, &assimpColor) == aiReturn_SUCCESS) {
Color color;
color.r = assimpColor.r;
color.g = assimpColor.g;
color.b = assimpColor.b;
color.a = assimpColor.a;
return color;
} else {
return (Color) { 1, 1, 1, 1 };
2017-10-22 00:35:50 +00:00
}
2018-01-30 05:44:32 +00:00
}
2017-10-22 00:35:50 +00:00
2018-01-30 05:44:32 +00:00
static int readMaterialTexture(struct aiMaterial* assimpMaterial, enum aiTextureType type, ModelData* modelData, map_int_t* textureCache, const char* dirname) {
struct aiString str;
if (aiGetMaterialTexture(assimpMaterial, type, 0, &str, NULL, NULL, NULL, NULL, NULL, NULL) == aiReturn_SUCCESS) {
char* path = str.data;
2017-10-22 00:35:50 +00:00
2018-01-30 05:44:32 +00:00
int* cachedTexture = map_get(textureCache, path);
if (cachedTexture) {
return *cachedTexture;
2017-10-22 00:35:50 +00:00
}
2018-01-30 05:44:32 +00:00
int textureIndex = modelData->textures.length;
char fullPath[LOVR_PATH_MAX];
char normalizedPath[LOVR_PATH_MAX];
strncpy(fullPath, dirname, LOVR_PATH_MAX);
char* lastSlash = strrchr(fullPath, '/');
if (lastSlash) lastSlash[1] = '\0';
strncat(fullPath, path, LOVR_PATH_MAX);
normalizePath(fullPath, normalizedPath, LOVR_PATH_MAX);
size_t size;
void* data = lovrFilesystemRead(normalizedPath, &size);
if (data) {
Blob* blob = lovrBlobCreate(data, size, path);
vec_push(&modelData->textures, lovrTextureDataFromBlob(blob));
} else {
vec_push(&modelData->textures, NULL);
2017-10-22 00:35:50 +00:00
}
2018-01-30 05:44:32 +00:00
map_set(textureCache, path, textureIndex);
return textureIndex;
} else {
int textureIndex = modelData->textures.length;
vec_push(&modelData->textures, NULL);
return textureIndex;
}
2017-10-22 00:35:50 +00:00
}
2017-10-21 07:19:05 +00:00
// Blob IO (to avoid reading data twice)
2017-11-25 10:26:45 +00:00
static size_t assimpBlobRead(struct aiFile* assimpFile, char* buffer, size_t size, size_t count) {
2017-10-22 00:35:50 +00:00
Blob* blob = (Blob*) assimpFile->UserData;
2017-10-21 07:19:05 +00:00
char* data = blob->data;
2017-10-22 00:35:50 +00:00
size_t bytes = MIN(count * size * sizeof(char), blob->size - blob->seek);
memcpy(buffer, data + blob->seek, bytes);
2017-11-30 03:06:04 +00:00
blob->seek += bytes;
return bytes / size;
2017-10-21 07:19:05 +00:00
}
2017-10-22 00:35:50 +00:00
static size_t assimpBlobGetSize(struct aiFile* assimpFile) {
Blob* blob = (Blob*) assimpFile->UserData;
2017-10-21 07:19:05 +00:00
return blob->size;
}
2017-11-25 10:26:45 +00:00
static aiReturn assimpBlobSeek(struct aiFile* assimpFile, size_t position, enum aiOrigin origin) {
2017-10-22 00:35:50 +00:00
Blob* blob = (Blob*) assimpFile->UserData;
switch (origin) {
case aiOrigin_SET: blob->seek = position; break;
case aiOrigin_CUR: blob->seek += position; break;
case aiOrigin_END: blob->seek = blob->size - position; break;
default: return aiReturn_FAILURE;
}
return blob->seek < blob->size ? aiReturn_SUCCESS : aiReturn_FAILURE;
2017-10-21 07:19:05 +00:00
}
2017-11-25 10:26:45 +00:00
static size_t assimpBlobTell(struct aiFile* assimpFile) {
2017-10-22 00:35:50 +00:00
Blob* blob = (Blob*) assimpFile->UserData;
return blob->seek;
2017-10-21 07:19:05 +00:00
}
// File IO (for reading referenced materials/textures)
2017-11-25 10:26:45 +00:00
static size_t assimpFileRead(struct aiFile* assimpFile, char* buffer, size_t size, size_t count) {
2017-10-22 00:35:50 +00:00
File* file = (File*) assimpFile->UserData;
unsigned long bytes = lovrFileRead(file, buffer, size * count);
2017-11-30 03:06:04 +00:00
return bytes / size;
2017-10-21 07:19:05 +00:00
}
2017-10-22 00:35:50 +00:00
static size_t assimpFileGetSize(struct aiFile* assimpFile) {
File* file = (File*) assimpFile->UserData;
return lovrFileGetSize(file);
2017-10-21 07:19:05 +00:00
}
2017-11-25 10:26:45 +00:00
static aiReturn assimpFileSeek(struct aiFile* assimpFile, size_t position, enum aiOrigin origin) {
2017-10-22 00:35:50 +00:00
File* file = (File*) assimpFile->UserData;
return lovrFileSeek(file, position) ? aiReturn_FAILURE : aiReturn_SUCCESS;
2017-10-21 07:19:05 +00:00
}
2017-10-22 00:35:50 +00:00
static unsigned long assimpFileTell(struct aiFile* assimpFile) {
File* file = (File*) assimpFile->UserData;
return lovrFileTell(file);
2017-10-21 07:19:05 +00:00
}
static struct aiFile* assimpFileOpen(struct aiFileIO* io, const char* path, const char* mode) {
2017-10-22 00:35:50 +00:00
struct aiFile* assimpFile = malloc(sizeof(struct aiFile));
2017-10-21 07:19:05 +00:00
Blob* blob = (Blob*) io->UserData;
if (!strcmp(blob->name, path)) {
2017-11-30 03:06:04 +00:00
blob->seek = 0;
2017-10-22 00:35:50 +00:00
assimpFile->ReadProc = assimpBlobRead;
assimpFile->FileSizeProc = assimpBlobGetSize;
assimpFile->SeekProc = assimpBlobSeek;
assimpFile->TellProc = assimpBlobTell;
assimpFile->UserData = (void*) blob;
2017-10-21 07:19:05 +00:00
} else {
char tempPath[LOVR_PATH_MAX];
2017-10-22 00:35:50 +00:00
char normalizedPath[LOVR_PATH_MAX];
strncpy(tempPath, path, LOVR_PATH_MAX);
normalizePath(tempPath, normalizedPath, LOVR_PATH_MAX);
2017-10-22 00:35:50 +00:00
File* file = lovrFileCreate(normalizedPath);
2017-10-22 00:35:50 +00:00
if (lovrFileOpen(file, OPEN_READ)) {
return NULL;
}
assimpFile->ReadProc = assimpFileRead;
assimpFile->FileSizeProc = assimpFileGetSize;
assimpFile->SeekProc = assimpFileSeek;
assimpFile->TellProc = assimpFileTell;
assimpFile->UserData = (void*) file;
2017-10-21 07:19:05 +00:00
}
2017-10-22 00:35:50 +00:00
return assimpFile;
2017-10-21 07:19:05 +00:00
}
2017-10-22 00:35:50 +00:00
static void assimpFileClose(struct aiFileIO* io, struct aiFile* assimpFile) {
2017-10-21 07:19:05 +00:00
void* blob = io->UserData;
2017-10-22 00:35:50 +00:00
if (assimpFile->UserData != blob) {
File* file = (File*) assimpFile->UserData;
lovrFileClose(file);
lovrRelease(&file->ref);
2017-10-21 07:19:05 +00:00
}
2017-10-22 00:35:50 +00:00
free(assimpFile);
2017-10-21 07:19:05 +00:00
}
2017-04-02 12:55:21 +00:00
ModelData* lovrModelDataCreate(Blob* blob) {
2018-01-23 02:24:39 +00:00
ModelData* modelData = lovrAlloc(sizeof(ModelData), lovrModelDataDestroy);
2016-11-08 22:44:03 +00:00
if (!modelData) return NULL;
2017-10-21 07:19:05 +00:00
struct aiFileIO assimpIO;
assimpIO.OpenProc = assimpFileOpen;
assimpIO.CloseProc = assimpFileClose;
assimpIO.UserData = (void*) blob;
struct aiPropertyStore* propertyStore = aiCreatePropertyStore();
aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
2017-11-25 20:29:40 +00:00
aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBBC_MAX_BONES, 48);
2017-12-10 03:11:53 +00:00
unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_OptimizeGraph | aiProcess_SplitByBoneCount;
2017-10-21 07:19:05 +00:00
const struct aiScene* scene = aiImportFileExWithProperties(blob->name, flags, &assimpIO, propertyStore);
aiReleasePropertyStore(propertyStore);
2017-10-22 00:35:50 +00:00
if (!scene) {
lovrThrow("Unable to load model from '%s': %s\n", blob->name, aiGetErrorString());
}
modelData->nodeCount = 0;
modelData->indexCount = 0;
2018-01-27 02:58:36 +00:00
2018-02-11 01:27:29 +00:00
uint32_t vertexCount = 0;
2018-01-27 02:58:36 +00:00
bool hasNormals = false;
bool hasUVs = false;
bool hasVertexColors = false;
bool isSkinned = false;
2016-11-27 02:58:58 +00:00
for (unsigned int m = 0; m < scene->mNumMeshes; m++) {
struct aiMesh* assimpMesh = scene->mMeshes[m];
2018-02-11 01:27:29 +00:00
vertexCount += assimpMesh->mNumVertices;
modelData->indexCount += assimpMesh->mNumFaces * 3;
2018-01-27 02:58:36 +00:00
hasNormals |= assimpMesh->mNormals != NULL;
hasUVs |= assimpMesh->mTextureCoords[0] != NULL;
hasVertexColors |= assimpMesh->mColors[0] != NULL;
isSkinned |= assimpMesh->mNumBones > 0;
}
2016-10-29 22:18:10 +00:00
2018-02-11 01:27:29 +00:00
VertexFormat format;
vertexFormatInit(&format);
vertexFormatAppend(&format, "lovrPosition", ATTR_FLOAT, 3);
2018-01-27 02:58:36 +00:00
2018-02-11 01:27:29 +00:00
if (hasNormals) vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3);
if (hasUVs) vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2);
if (hasVertexColors) vertexFormatAppend(&format, "lovrVertexColor", ATTR_BYTE, 4);
size_t boneByteOffset = format.stride;
if (isSkinned) vertexFormatAppend(&format, "lovrBones", ATTR_INT, 4);
if (isSkinned) vertexFormatAppend(&format, "lovrBoneWeights", ATTR_FLOAT, 4);
2018-01-27 02:58:36 +00:00
// Allocate
2018-02-11 01:27:29 +00:00
modelData->vertexData = lovrVertexDataCreate(vertexCount, &format, true);
modelData->indexSize = vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t);
modelData->indices.raw = malloc(modelData->indexCount * modelData->indexSize);
modelData->primitiveCount = scene->mNumMeshes;
modelData->primitives = malloc(modelData->primitiveCount * sizeof(ModelPrimitive));
2017-11-02 06:10:21 +00:00
2017-10-24 02:24:23 +00:00
// Load vertices
2018-02-11 01:27:29 +00:00
IndexPointer indices = modelData->indices;
2017-11-02 06:10:21 +00:00
uint32_t vertex = 0;
uint32_t index = 0;
2016-11-12 09:19:47 +00:00
for (unsigned int m = 0; m < scene->mNumMeshes; m++) {
2016-10-29 22:18:10 +00:00
struct aiMesh* assimpMesh = scene->mMeshes[m];
ModelPrimitive* primitive = &modelData->primitives[m];
primitive->material = assimpMesh->mMaterialIndex;
primitive->drawStart = index;
primitive->drawCount = 0;
2017-11-02 06:10:21 +00:00
uint32_t baseVertex = vertex;
2016-10-29 22:18:10 +00:00
// Indices
2016-11-12 09:19:47 +00:00
for (unsigned int f = 0; f < assimpMesh->mNumFaces; f++) {
2016-10-29 22:18:10 +00:00
struct aiFace assimpFace = assimpMesh->mFaces[f];
lovrAssert(assimpFace.mNumIndices == 3, "Only triangular faces are supported");
2016-10-31 20:54:32 +00:00
primitive->drawCount += assimpFace.mNumIndices;
2016-10-31 20:54:32 +00:00
2017-11-02 02:27:58 +00:00
if (modelData->indexSize == sizeof(uint16_t)) {
for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) {
2017-11-02 06:10:21 +00:00
indices.shorts[index++] = baseVertex + assimpFace.mIndices[i];
}
2017-11-02 02:27:58 +00:00
} else {
for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) {
2017-11-02 06:10:21 +00:00
indices.ints[index++] = baseVertex + assimpFace.mIndices[i];
}
2016-10-29 22:18:10 +00:00
}
}
2017-11-02 06:10:21 +00:00
// Vertices
2016-11-12 09:19:47 +00:00
for (unsigned int v = 0; v < assimpMesh->mNumVertices; v++) {
2018-02-11 01:27:29 +00:00
VertexPointer vertices = modelData->vertexData->data;
vertices.bytes += vertex * modelData->vertexData->format.stride;
2017-11-02 06:10:21 +00:00
2017-10-24 02:24:23 +00:00
*vertices.floats++ = assimpMesh->mVertices[v].x;
*vertices.floats++ = assimpMesh->mVertices[v].y;
*vertices.floats++ = assimpMesh->mVertices[v].z;
2018-01-27 02:58:36 +00:00
if (hasNormals) {
if (assimpMesh->mNormals) {
2017-10-24 02:24:23 +00:00
*vertices.floats++ = assimpMesh->mNormals[v].x;
*vertices.floats++ = assimpMesh->mNormals[v].y;
*vertices.floats++ = assimpMesh->mNormals[v].z;
} else {
2017-10-24 02:24:23 +00:00
*vertices.floats++ = 0;
*vertices.floats++ = 0;
*vertices.floats++ = 0;
}
}
2016-11-23 08:02:19 +00:00
2018-01-27 02:58:36 +00:00
if (hasUVs) {
if (assimpMesh->mTextureCoords[0]) {
2017-10-24 02:24:23 +00:00
*vertices.floats++ = assimpMesh->mTextureCoords[0][v].x;
*vertices.floats++ = assimpMesh->mTextureCoords[0][v].y;
} else {
2017-10-24 02:24:23 +00:00
*vertices.floats++ = 0;
*vertices.floats++ = 0;
}
2016-11-23 08:02:19 +00:00
}
2017-10-24 02:24:23 +00:00
2018-01-27 02:58:36 +00:00
if (hasVertexColors) {
2017-10-24 02:24:23 +00:00
if (assimpMesh->mColors[0]) {
*vertices.bytes++ = assimpMesh->mColors[0][v].r * 255;
*vertices.bytes++ = assimpMesh->mColors[0][v].g * 255;
*vertices.bytes++ = assimpMesh->mColors[0][v].b * 255;
*vertices.bytes++ = assimpMesh->mColors[0][v].a * 255;
} else {
*vertices.bytes++ = 255;
*vertices.bytes++ = 255;
*vertices.bytes++ = 255;
*vertices.bytes++ = 255;
}
}
vertex++;
2016-11-23 08:02:19 +00:00
}
2017-11-02 06:10:21 +00:00
// Bones
primitive->boneCount = assimpMesh->mNumBones;
map_init(&primitive->boneMap);
2017-11-02 06:10:21 +00:00
for (unsigned int b = 0; b < assimpMesh->mNumBones; b++) {
struct aiBone* assimpBone = assimpMesh->mBones[b];
Bone* bone = &primitive->bones[b];
2017-11-02 06:10:21 +00:00
bone->name = strdup(assimpBone->mName.data);
aiTransposeMatrix4(&assimpBone->mOffsetMatrix);
mat4_set(bone->offset, (float*) &assimpBone->mOffsetMatrix);
map_set(&primitive->boneMap, bone->name, b);
2017-11-02 06:10:21 +00:00
for (unsigned int w = 0; w < assimpBone->mNumWeights; w++) {
uint32_t vertexIndex = baseVertex + assimpBone->mWeights[w].mVertexId;
float weight = assimpBone->mWeights[w].mWeight;
2018-02-11 01:27:29 +00:00
VertexPointer vertices = modelData->vertexData->data;
vertices.bytes += vertexIndex * modelData->vertexData->format.stride;
uint32_t* bones = (uint32_t*) (vertices.bytes + boneByteOffset);
2017-11-02 06:10:21 +00:00
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] = b;
2017-11-02 06:10:21 +00:00
weights[boneSlot] = weight;
}
}
2016-10-09 04:52:58 +00:00
}
2016-10-29 22:18:10 +00:00
2017-10-21 22:20:16 +00:00
// Materials
2018-01-30 05:44:32 +00:00
map_int_t textureCache;
map_init(&textureCache);
vec_init(&modelData->textures);
2017-10-21 22:20:16 +00:00
modelData->materialCount = scene->mNumMaterials;
2018-01-30 05:44:32 +00:00
modelData->materials = malloc(modelData->materialCount * sizeof(ModelMaterial));
2017-10-21 22:20:16 +00:00
for (unsigned int m = 0; m < scene->mNumMaterials; m++) {
2018-01-30 05:44:32 +00:00
ModelMaterial* material = &modelData->materials[m];
struct aiMaterial* assimpMaterial = scene->mMaterials[m];
2017-10-21 22:20:16 +00:00
2018-01-30 05:44:32 +00:00
material->diffuseColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_DIFFUSE);
material->diffuseTexture = readMaterialTexture(assimpMaterial, aiTextureType_DIFFUSE, modelData, &textureCache, blob->name);
2017-10-21 22:20:16 +00:00
}
2018-01-30 05:44:32 +00:00
map_deinit(&textureCache);
2017-10-21 22:20:16 +00:00
2016-10-29 22:18:10 +00:00
// Nodes
modelData->nodeCount = 0;
assimpSumChildren(scene->mRootNode, &modelData->nodeCount);
modelData->nodes = malloc(modelData->nodeCount * sizeof(ModelNode));
modelData->nodes[0].parent = -1;
map_init(&modelData->nodeMap);
int nodeIndex = 0;
assimpNodeTraversal(modelData, scene->mRootNode, &nodeIndex);
2016-10-29 22:18:10 +00:00
2017-11-02 06:10:21 +00:00
// Animations
2018-01-30 04:30:13 +00:00
modelData->animationCount = scene->mNumAnimations;
modelData->animations = malloc(modelData->animationCount * sizeof(Animation));
for (int i = 0; i < modelData->animationCount; i++) {
2017-11-02 06:10:21 +00:00
struct aiAnimation* assimpAnimation = scene->mAnimations[i];
float ticksPerSecond = assimpAnimation->mTicksPerSecond;
2017-11-02 06:10:21 +00:00
2018-01-30 04:30:13 +00:00
Animation* animation = &modelData->animations[i];
animation->name = strdup(assimpAnimation->mName.data);
animation->duration = assimpAnimation->mDuration / ticksPerSecond;
animation->channelCount = assimpAnimation->mNumChannels;
map_init(&animation->channels);
2017-11-03 06:47:13 +00:00
2018-01-30 04:30:13 +00:00
for (int j = 0; j < animation->channelCount; j++) {
2017-11-02 06:10:21 +00:00
struct aiNodeAnim* assimpChannel = assimpAnimation->mChannels[j];
AnimationChannel channel;
channel.node = strdup(assimpChannel->mNodeName.data);
2017-11-02 06:10:21 +00:00
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 / ticksPerSecond;
2017-11-02 06:10:21 +00:00
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 / ticksPerSecond;
2017-11-02 06:10:21 +00:00
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 / ticksPerSecond;
2017-11-02 06:10:21 +00:00
vec3_set(keyframe.data, scale.x, scale.y, scale.z);
vec_push(&channel.scaleKeyframes, keyframe);
}
2018-01-30 04:30:13 +00:00
map_set(&animation->channels, channel.node, channel);
2017-11-02 06:10:21 +00:00
}
}
2016-10-09 04:52:58 +00:00
aiReleaseImport(scene);
2016-10-04 04:54:27 +00:00
return modelData;
}
2018-01-23 02:24:39 +00:00
void lovrModelDataDestroy(const Ref* ref) {
ModelData* modelData = containerof(ref, ModelData);
2017-10-21 22:20:16 +00:00
for (int i = 0; i < modelData->nodeCount; i++) {
vec_deinit(&modelData->nodes[i].children);
vec_deinit(&modelData->nodes[i].primitives);
2017-02-19 09:54:58 +00:00
}
for (int i = 0; i < modelData->primitiveCount; i++) {
map_deinit(&modelData->primitives[i].boneMap);
}
2018-01-30 04:30:13 +00:00
for (int i = 0; i < modelData->animationCount; i++) {
Animation* animation = &modelData->animations[i];
const char* key;
map_iter_t iter = map_iter(&animation->channels);
while ((key = map_next(&animation->channels, &iter)) != NULL) {
AnimationChannel* channel = map_get(&animation->channels, key);
vec_deinit(&channel->positionKeyframes);
vec_deinit(&channel->rotationKeyframes);
vec_deinit(&channel->scaleKeyframes);
}
map_deinit(&animation->channels);
2018-01-06 05:09:16 +00:00
}
2017-11-02 06:10:21 +00:00
2018-01-30 05:44:32 +00:00
for (int i = 0; i < modelData->textures.length; i++) {
TextureData* textureData = modelData->textures.data[i];
lovrRelease(&textureData->ref);
2017-10-21 22:20:16 +00:00
}
2018-01-30 05:44:32 +00:00
vec_deinit(&modelData->textures);
map_deinit(&modelData->nodeMap);
2018-02-11 01:27:29 +00:00
lovrRelease(&modelData->vertexData->ref);
free(modelData->nodes);
free(modelData->primitives);
2018-01-30 04:30:13 +00:00
free(modelData->animations);
2017-10-21 22:20:16 +00:00
free(modelData->materials);
2018-02-11 01:27:29 +00:00
free(modelData->indices.raw);
2017-02-19 09:54:58 +00:00
free(modelData);
}
2017-11-02 02:27:58 +00:00
2017-12-01 05:17:54 +00:00
static void aabbIterator(ModelData* modelData, ModelNode* node, float aabb[6], mat4 transform) {
2017-11-02 02:27:58 +00:00
mat4_multiply(transform, node->transform);
for (int i = 0; i < node->primitives.length; i++) {
ModelPrimitive* primitive = &modelData->primitives[node->primitives.data[i]];
2017-12-01 05:17:54 +00:00
for (int j = 0; j < primitive->drawCount; j++) {
float vertex[3];
uint32_t index;
if (modelData->indexSize == sizeof(uint16_t)) {
index = modelData->indices.shorts[primitive->drawStart + j];
} else {
index = modelData->indices.ints[primitive->drawStart + j];
}
2018-02-11 01:27:29 +00:00
vec3_init(vertex, (float*) (modelData->vertexData->data.bytes + index * modelData->vertexData->format.stride));
2017-12-01 05:17:54 +00:00
mat4_transform(transform, vertex);
aabb[0] = MIN(aabb[0], vertex[0]);
aabb[1] = MAX(aabb[1], vertex[0]);
aabb[2] = MIN(aabb[2], vertex[1]);
aabb[3] = MAX(aabb[3], vertex[1]);
aabb[4] = MIN(aabb[4], vertex[2]);
aabb[5] = MAX(aabb[5], vertex[2]);
2017-11-02 02:27:58 +00:00
}
}
for (int i = 0; i < node->children.length; i++) {
ModelNode* child = &modelData->nodes[node->children.data[i]];
aabbIterator(modelData, child, aabb, transform);
}
}
void lovrModelDataGetAABB(ModelData* modelData, float aabb[6]) {
float transform[16];
mat4_identity(transform);
2017-12-01 05:17:54 +00:00
aabb[0] = FLT_MAX;
aabb[1] = -FLT_MAX;
aabb[2] = FLT_MAX;
aabb[3] = -FLT_MAX;
aabb[4] = FLT_MAX;
aabb[5] = -FLT_MAX;
2017-11-02 02:27:58 +00:00
aabbIterator(modelData, &modelData->nodes[0], aabb, transform);
}