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>
|
2017-10-22 14:04:52 +00:00
|
|
|
#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>
|
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-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;
|
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
|
|
|
}
|
|
|
|
|
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 {
|
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)) {
|
|
|
|
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;
|
|
|
|
|
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);
|
|
|
|
|
2017-10-22 00:35:50 +00:00
|
|
|
if (!scene) {
|
|
|
|
lovrThrow("Unable to load model from '%s': %s\n", blob->name, aiGetErrorString());
|
|
|
|
}
|
|
|
|
|
2017-10-15 01:01:00 +00:00
|
|
|
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
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
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-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);
|
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-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;
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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-02-11 01:27:29 +00:00
|
|
|
VertexPointer vertices = modelData->vertexData->data;
|
|
|
|
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);
|
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
|
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;
|
|
|
|
Keyframe keyframe;
|
2017-11-07 04:25:08 +00:00
|
|
|
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;
|
2017-11-07 04:25:08 +00:00
|
|
|
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;
|
2017-11-07 04:25:08 +00:00
|
|
|
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++) {
|
2017-10-15 01:01:00 +00:00
|
|
|
vec_deinit(&modelData->nodes[i].children);
|
|
|
|
vec_deinit(&modelData->nodes[i].primitives);
|
2017-02-19 09:54:58 +00:00
|
|
|
}
|
|
|
|
|
2017-11-25 19:59:27 +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);
|
2017-11-21 05:19:46 +00:00
|
|
|
map_deinit(&modelData->nodeMap);
|
|
|
|
|
2018-02-11 01:27:29 +00:00
|
|
|
lovrRelease(&modelData->vertexData->ref);
|
|
|
|
|
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
|
|
|
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);
|
|
|
|
}
|