2018-02-11 23:22:04 +00:00
|
|
|
#include "data/modelData.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"
|
2018-11-15 16:03:51 +00:00
|
|
|
#include "lib/math.h"
|
2017-12-01 05:17:54 +00:00
|
|
|
#include <float.h>
|
2017-10-22 14:04:52 +00:00
|
|
|
#include <limits.h>
|
2016-10-29 06:40:31 +00:00
|
|
|
#include <stdlib.h>
|
2019-01-25 01:39:27 +00:00
|
|
|
#include <stdio.h>
|
2018-10-02 04:07:29 +00:00
|
|
|
#include <string.h>
|
2018-09-21 22:31:07 +00:00
|
|
|
|
|
|
|
#ifdef LOVR_USE_ASSIMP
|
2017-10-21 07:19:05 +00:00
|
|
|
#include <assimp/cfileio.h>
|
2016-10-04 04:54:27 +00:00
|
|
|
#include <assimp/cimport.h>
|
2017-10-15 01:01:00 +00:00
|
|
|
#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';
|
|
|
|
}
|
|
|
|
|
2017-11-21 05:19:46 +00:00
|
|
|
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);
|
2018-02-11 21:03:52 +00:00
|
|
|
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);
|
|
|
|
}
|
2017-11-21 05:19:46 +00:00
|
|
|
|
|
|
|
// 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-09-21 22:31:07 +00:00
|
|
|
static void aabbIterator(ModelData* modelData, ModelNode* node, float aabb[6]) {
|
|
|
|
for (int i = 0; i < node->primitives.length; i++) {
|
|
|
|
ModelPrimitive* primitive = &modelData->primitives[node->primitives.data[i]];
|
|
|
|
for (int j = 0; j < primitive->drawCount; j++) {
|
|
|
|
uint32_t index;
|
|
|
|
if (modelData->indexSize == sizeof(uint16_t)) {
|
|
|
|
index = modelData->indices.shorts[primitive->drawStart + j];
|
|
|
|
} else {
|
|
|
|
index = modelData->indices.ints[primitive->drawStart + j];
|
|
|
|
}
|
|
|
|
float vertex[3];
|
|
|
|
VertexPointer vertices = { .raw = modelData->vertexData->blob.data };
|
|
|
|
vec3_init(vertex, (float*) (vertices.bytes + index * modelData->vertexData->format.stride));
|
|
|
|
mat4_transform(node->globalTransform, &vertex[0], &vertex[1], &vertex[2]);
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < node->children.length; i++) {
|
|
|
|
ModelNode* child = &modelData->nodes[node->children.data[i]];
|
|
|
|
aabbIterator(modelData, child, aabb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 03:16:40 +00:00
|
|
|
static float readMaterialScalar(struct aiMaterial* assimpMaterial, const char* key, unsigned int type, unsigned int index) {
|
|
|
|
float scalar;
|
|
|
|
if (aiGetMaterialFloatArray(assimpMaterial, key, type, index, &scalar, NULL) == aiReturn_SUCCESS) {
|
|
|
|
return scalar;
|
|
|
|
} else {
|
|
|
|
return 1.f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-11 09:07:09 +00:00
|
|
|
static Color readMaterialColor(struct aiMaterial* assimpMaterial, const char* key, unsigned int type, unsigned int index, Color fallback) {
|
2018-01-30 05:44:32 +00:00
|
|
|
struct aiColor4D assimpColor;
|
|
|
|
if (aiGetMaterialColor(assimpMaterial, key, type, index, &assimpColor) == aiReturn_SUCCESS) {
|
2018-10-11 09:07:09 +00:00
|
|
|
return (Color) { .r = assimpColor.r, .g = assimpColor.g, .b = assimpColor.b, .a = assimpColor.a };
|
2018-01-30 05:44:32 +00:00
|
|
|
} else {
|
2018-10-11 09:07:09 +00:00
|
|
|
return fallback;
|
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-02-16 03:59:31 +00:00
|
|
|
static int readMaterialTexture(struct aiMaterial* assimpMaterial, enum aiTextureType type, ModelData* modelData, map_int_t* textureCache, const char* dirname) {
|
2018-01-30 05:44:32 +00:00
|
|
|
struct aiString str;
|
2017-10-22 00:35:50 +00:00
|
|
|
|
2018-02-14 05:24:18 +00:00
|
|
|
if (aiGetMaterialTexture(assimpMaterial, type, 0, &str, NULL, NULL, NULL, NULL, NULL, NULL) != aiReturn_SUCCESS) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-01-30 05:44:32 +00:00
|
|
|
|
2018-02-14 05:24:18 +00:00
|
|
|
char* path = str.data;
|
2018-01-30 05:44:32 +00:00
|
|
|
|
2018-02-16 03:59:31 +00:00
|
|
|
int* cachedTexture = map_get(textureCache, path);
|
2018-02-14 05:24:18 +00:00
|
|
|
if (cachedTexture) {
|
|
|
|
return *cachedTexture;
|
|
|
|
}
|
2017-10-22 00:35:50 +00:00
|
|
|
|
2018-02-14 05:24:18 +00:00
|
|
|
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';
|
2018-04-05 06:42:15 +00:00
|
|
|
else fullPath[0] = '\0';
|
2018-09-27 06:16:18 +00:00
|
|
|
strncat(fullPath, path, LOVR_PATH_MAX - 1);
|
2018-02-14 05:24:18 +00:00
|
|
|
normalizePath(fullPath, normalizedPath, LOVR_PATH_MAX);
|
|
|
|
|
|
|
|
size_t size;
|
|
|
|
void* data = lovrFilesystemRead(normalizedPath, &size);
|
|
|
|
if (!data) {
|
|
|
|
return 0;
|
2018-01-30 05:44:32 +00:00
|
|
|
}
|
2018-02-14 05:24:18 +00:00
|
|
|
|
|
|
|
Blob* blob = lovrBlobCreate(data, size, path);
|
2018-09-04 03:59:12 +00:00
|
|
|
TextureData* textureData = lovrTextureDataCreateFromBlob(blob, true);
|
2018-08-02 10:04:13 +00:00
|
|
|
lovrRelease(blob);
|
2018-02-14 05:24:18 +00:00
|
|
|
int textureIndex = modelData->textures.length;
|
|
|
|
vec_push(&modelData->textures, textureData);
|
|
|
|
map_set(textureCache, path, textureIndex);
|
|
|
|
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;
|
2017-11-25 11:34:55 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-04-26 05:12:49 +00:00
|
|
|
static size_t assimpFileTell(struct aiFile* assimpFile) {
|
2017-10-22 00:35:50 +00:00
|
|
|
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 {
|
2017-11-25 21:42:55 +00:00
|
|
|
char tempPath[LOVR_PATH_MAX];
|
2017-10-22 00:35:50 +00:00
|
|
|
char normalizedPath[LOVR_PATH_MAX];
|
2017-11-25 21:42:55 +00:00
|
|
|
strncpy(tempPath, path, LOVR_PATH_MAX);
|
|
|
|
normalizePath(tempPath, normalizedPath, LOVR_PATH_MAX);
|
2017-10-22 00:35:50 +00:00
|
|
|
|
2017-11-25 21:42:55 +00:00
|
|
|
File* file = lovrFileCreate(normalizedPath);
|
2017-10-22 00:35:50 +00:00
|
|
|
if (lovrFileOpen(file, OPEN_READ)) {
|
2018-08-02 10:04:13 +00:00
|
|
|
lovrRelease(file);
|
2017-10-22 00:35:50 +00:00
|
|
|
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);
|
2018-02-26 08:59:03 +00:00
|
|
|
lovrRelease(file);
|
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
|
|
|
}
|
|
|
|
|
2018-12-19 08:15:08 +00:00
|
|
|
ModelData* lovrModelDataInit(ModelData* modelData, Blob* blob) {
|
2017-10-21 07:19:05 +00:00
|
|
|
struct aiFileIO assimpIO;
|
|
|
|
assimpIO.OpenProc = assimpFileOpen;
|
|
|
|
assimpIO.CloseProc = assimpFileClose;
|
|
|
|
assimpIO.UserData = (void*) blob;
|
|
|
|
|
2017-10-15 01:01:00 +00:00
|
|
|
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);
|
2017-10-15 01:01:00 +00:00
|
|
|
aiReleasePropertyStore(propertyStore);
|
2018-12-19 08:15:08 +00:00
|
|
|
lovrAssert(scene, "Unable to load model from '%s': %s", blob->name, aiGetErrorString());
|
2017-10-22 00:35:50 +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;
|
2018-02-26 10:54:35 +00:00
|
|
|
bool hasTangents = false;
|
2018-01-27 02:58:36 +00:00
|
|
|
bool isSkinned = false;
|
2016-11-27 02:58:58 +00:00
|
|
|
|
2017-10-15 01:01:00 +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;
|
2017-10-15 01:01:00 +00:00
|
|
|
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;
|
2018-02-26 10:54:35 +00:00
|
|
|
hasTangents |= assimpMesh->mTangents != NULL;
|
2018-01-27 02:58:36 +00:00
|
|
|
isSkinned |= assimpMesh->mNumBones > 0;
|
2017-10-15 01:01:00 +00:00
|
|
|
}
|
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);
|
2018-02-26 10:54:35 +00:00
|
|
|
if (hasTangents) vertexFormatAppend(&format, "lovrTangent", ATTR_FLOAT, 3);
|
2018-02-11 01:27:29 +00:00
|
|
|
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
|
|
|
|
2017-10-15 01:01:00 +00:00
|
|
|
// Allocate
|
2018-03-21 21:48:46 +00:00
|
|
|
modelData->vertexData = lovrVertexDataCreate(vertexCount, &format);
|
2018-02-11 01:27:29 +00:00
|
|
|
modelData->indexSize = vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t);
|
|
|
|
modelData->indices.raw = malloc(modelData->indexCount * modelData->indexSize);
|
2017-10-15 01:01:00 +00:00
|
|
|
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];
|
2017-11-25 19:59:27 +00:00
|
|
|
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
|
|
|
|
2017-10-15 01:01:00 +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];
|
2017-10-15 01:01:00 +00:00
|
|
|
lovrAssert(assimpFace.mNumIndices == 3, "Only triangular faces are supported");
|
2016-10-31 20:54:32 +00:00
|
|
|
|
2017-11-25 19:59:27 +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)) {
|
2017-10-22 14:04:52 +00:00
|
|
|
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-10-22 14:04:52 +00:00
|
|
|
}
|
2017-11-02 02:27:58 +00:00
|
|
|
} else {
|
2017-10-22 14:04:52 +00:00
|
|
|
for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) {
|
2017-11-02 06:10:21 +00:00
|
|
|
indices.ints[index++] = baseVertex + assimpFace.mIndices[i];
|
2017-10-22 14:04:52 +00:00
|
|
|
}
|
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-03-11 05:28:22 +00:00
|
|
|
VertexPointer vertices = { .raw = modelData->vertexData->blob.data };
|
2018-02-11 01:27:29 +00:00
|
|
|
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;
|
2017-10-15 01:01:00 +00:00
|
|
|
|
2018-01-27 02:58:36 +00:00
|
|
|
if (hasNormals) {
|
2017-10-15 01:01:00 +00:00
|
|
|
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;
|
2017-10-15 01:01:00 +00:00
|
|
|
} else {
|
2017-10-24 02:24:23 +00:00
|
|
|
*vertices.floats++ = 0;
|
|
|
|
*vertices.floats++ = 0;
|
|
|
|
*vertices.floats++ = 0;
|
2017-10-15 01:01:00 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-23 08:02:19 +00:00
|
|
|
|
2018-01-27 02:58:36 +00:00
|
|
|
if (hasUVs) {
|
2017-10-15 01:01:00 +00:00
|
|
|
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;
|
2017-10-15 01:01:00 +00:00
|
|
|
} else {
|
2017-10-24 02:24:23 +00:00
|
|
|
*vertices.floats++ = 0;
|
|
|
|
*vertices.floats++ = 0;
|
2017-10-15 01:01:00 +00:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-26 10:54:35 +00:00
|
|
|
if (hasTangents) {
|
|
|
|
if (assimpMesh->mTangents) {
|
|
|
|
*vertices.floats++ = assimpMesh->mTangents[v].x;
|
|
|
|
*vertices.floats++ = assimpMesh->mTangents[v].y;
|
|
|
|
*vertices.floats++ = assimpMesh->mTangents[v].z;
|
|
|
|
} else {
|
|
|
|
*vertices.floats++ = 0;
|
|
|
|
*vertices.floats++ = 0;
|
|
|
|
*vertices.floats++ = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-24 02:24:23 +00:00
|
|
|
vertex++;
|
2016-11-23 08:02:19 +00:00
|
|
|
}
|
2017-11-02 06:10:21 +00:00
|
|
|
|
|
|
|
// Bones
|
2017-11-25 19:59:27 +00:00
|
|
|
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];
|
2017-11-25 19:59:27 +00:00
|
|
|
Bone* bone = &primitive->bones[b];
|
2017-11-02 06:10:21 +00:00
|
|
|
|
2017-11-25 19:59:27 +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-03-11 05:28:22 +00:00
|
|
|
VertexPointer vertices = { .raw = modelData->vertexData->blob.data };
|
2018-02-11 01:27:29 +00:00
|
|
|
vertices.bytes += vertexIndex * modelData->vertexData->format.stride;
|
2017-11-25 19:59:27 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-11-25 19:59:27 +00:00
|
|
|
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);
|
2018-02-14 05:24:18 +00:00
|
|
|
vec_push(&modelData->textures, NULL);
|
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-10-11 09:07:09 +00:00
|
|
|
material->diffuseColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_DIFFUSE, (Color) { 1., 1., 1., 1. });
|
|
|
|
material->emissiveColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_EMISSIVE, (Color) { 0., 0., 0., 0. });
|
2018-01-30 05:44:32 +00:00
|
|
|
material->diffuseTexture = readMaterialTexture(assimpMaterial, aiTextureType_DIFFUSE, modelData, &textureCache, blob->name);
|
2018-02-12 03:16:40 +00:00
|
|
|
material->emissiveTexture = readMaterialTexture(assimpMaterial, aiTextureType_EMISSIVE, modelData, &textureCache, blob->name);
|
|
|
|
material->metalnessTexture = readMaterialTexture(assimpMaterial, aiTextureType_UNKNOWN, modelData, &textureCache, blob->name);
|
|
|
|
material->roughnessTexture = material->metalnessTexture;
|
|
|
|
material->occlusionTexture = readMaterialTexture(assimpMaterial, aiTextureType_LIGHTMAP, modelData, &textureCache, blob->name);
|
|
|
|
material->normalTexture = readMaterialTexture(assimpMaterial, aiTextureType_NORMALS, modelData, &textureCache, blob->name);
|
|
|
|
material->metalness = readMaterialScalar(assimpMaterial, "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0);
|
|
|
|
material->roughness = readMaterialScalar(assimpMaterial, "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0);
|
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
|
2017-10-15 01:01:00 +00:00
|
|
|
modelData->nodeCount = 0;
|
|
|
|
assimpSumChildren(scene->mRootNode, &modelData->nodeCount);
|
|
|
|
modelData->nodes = malloc(modelData->nodeCount * sizeof(ModelNode));
|
|
|
|
modelData->nodes[0].parent = -1;
|
2017-11-21 05:19:46 +00:00
|
|
|
map_init(&modelData->nodeMap);
|
2017-10-15 01:01:00 +00:00
|
|
|
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];
|
2017-11-07 04:25:08 +00:00
|
|
|
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;
|
|
|
|
|
2017-11-03 02:01:31 +00:00
|
|
|
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;
|
2018-07-04 21:46:52 +00:00
|
|
|
vec_push(&channel.positionKeyframes, ((Keyframe) {
|
|
|
|
.time = assimpKeyframe.mTime / ticksPerSecond,
|
|
|
|
.data = { position.x, position.y, position.z }
|
|
|
|
}));
|
2017-11-02 06:10:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned int k = 0; k < assimpChannel->mNumRotationKeys; k++) {
|
|
|
|
struct aiQuatKey assimpKeyframe = assimpChannel->mRotationKeys[k];
|
|
|
|
struct aiQuaternion quaternion = assimpKeyframe.mValue;
|
2018-07-04 21:46:52 +00:00
|
|
|
vec_push(&channel.rotationKeyframes, ((Keyframe) {
|
|
|
|
.time = assimpKeyframe.mTime / ticksPerSecond,
|
|
|
|
.data = { quaternion.x, quaternion.y, quaternion.z, quaternion.w }
|
|
|
|
}));
|
2017-11-02 06:10:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned int k = 0; k < assimpChannel->mNumScalingKeys; k++) {
|
|
|
|
struct aiVectorKey assimpKeyframe = assimpChannel->mScalingKeys[k];
|
|
|
|
struct aiVector3D scale = assimpKeyframe.mValue;
|
2018-07-04 21:46:52 +00:00
|
|
|
vec_push(&channel.scaleKeyframes, ((Keyframe) {
|
|
|
|
.time = assimpKeyframe.mTime / ticksPerSecond,
|
|
|
|
.data = { scale.x, scale.y, scale.z }
|
|
|
|
}));
|
2017-11-02 06:10:21 +00:00
|
|
|
}
|
|
|
|
|
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-09-21 22:31:07 +00:00
|
|
|
#else
|
|
|
|
static void aabbIterator(ModelData* modelData, ModelNode* node, float aabb[6]) {}
|
2018-12-19 08:15:08 +00:00
|
|
|
ModelData* lovrModelDataInit(ModelData* modelData, Blob* blob) {
|
2018-09-21 22:31:07 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
2016-10-04 04:54:27 +00:00
|
|
|
|
2018-02-26 08:59:03 +00:00
|
|
|
void lovrModelDataDestroy(void* ref) {
|
|
|
|
ModelData* modelData = ref;
|
2018-01-23 02:24:39 +00:00
|
|
|
|
2017-10-21 22:20:16 +00:00
|
|
|
for (int i = 0; i < modelData->nodeCount; i++) {
|
2017-10-15 01:01:00 +00:00
|
|
|
vec_deinit(&modelData->nodes[i].children);
|
|
|
|
vec_deinit(&modelData->nodes[i].primitives);
|
2018-03-24 02:31:32 +00:00
|
|
|
free((char*) modelData->nodes[i].name);
|
2017-02-19 09:54:58 +00:00
|
|
|
}
|
|
|
|
|
2017-11-25 19:59:27 +00:00
|
|
|
for (int i = 0; i < modelData->primitiveCount; i++) {
|
2018-03-24 02:31:32 +00:00
|
|
|
ModelPrimitive* primitive = &modelData->primitives[i];
|
|
|
|
for (int j = 0; j < primitive->boneCount; j++) {
|
|
|
|
free((char*) primitive->bones[j].name);
|
|
|
|
}
|
|
|
|
map_deinit(&primitive->boneMap);
|
2017-11-25 19:59:27 +00:00
|
|
|
}
|
|
|
|
|
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-03-24 02:31:32 +00:00
|
|
|
free((char*) animation->name);
|
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++) {
|
2018-02-26 08:59:03 +00:00
|
|
|
lovrRelease(modelData->textures.data[i]);
|
2017-10-21 22:20:16 +00:00
|
|
|
}
|
|
|
|
|
2018-01-30 05:44:32 +00:00
|
|
|
vec_deinit(&modelData->textures);
|
2017-11-21 05:19:46 +00:00
|
|
|
map_deinit(&modelData->nodeMap);
|
|
|
|
|
2018-02-26 08:59:03 +00:00
|
|
|
lovrRelease(modelData->vertexData);
|
2018-02-11 01:27:29 +00:00
|
|
|
|
2017-10-15 01:01:00 +00:00
|
|
|
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
|
|
|
}
|
2017-11-02 02:27:58 +00:00
|
|
|
|
|
|
|
void lovrModelDataGetAABB(ModelData* modelData, float aabb[6]) {
|
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;
|
2018-03-02 04:51:57 +00:00
|
|
|
aabbIterator(modelData, &modelData->nodes[0], aabb);
|
2017-11-02 02:27:58 +00:00
|
|
|
}
|