From 6857b01ac0f8a2eb8b4244be9081d4d1e0ebe85f Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 23 Jan 2019 11:15:01 -0800 Subject: [PATCH] Start OBJ parser; Optimize glTF parsing; --- CMakeLists.txt | 2 + src/data/modelData.c | 902 ++------------------------------------ src/data/modelData.h | 19 +- src/data/modelData_gltf.c | 858 ++++++++++++++++++++++++++++++++++++ src/data/modelData_obj.c | 165 +++++++ src/headset/openvr.c | 30 +- src/lib/jsmn/jsmn.h | 1 + 7 files changed, 1092 insertions(+), 885 deletions(-) create mode 100644 src/data/modelData_gltf.c create mode 100644 src/data/modelData_obj.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 39675ec7..69d4640f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -380,6 +380,8 @@ if(LOVR_ENABLE_DATA) src/data/audioStream.c src/data/blob.c src/data/modelData.c + src/data/modelData_gltf.c + src/data/modelData_obj.c src/data/rasterizer.c src/data/soundData.c src/data/textureData.c diff --git a/src/data/modelData.c b/src/data/modelData.c index c42c4faa..cac5a504 100644 --- a/src/data/modelData.c +++ b/src/data/modelData.c @@ -1,866 +1,14 @@ #include "data/modelData.h" -#include "lib/math.h" -#include "lib/jsmn/jsmn.h" -#include -#include -#include - -#define MAGIC_glTF 0x46546c67 -#define MAGIC_JSON 0x4e4f534a -#define MAGIC_BIN 0x004e4942 - -#define STR_EQ(k, s) !strncmp(k.data, s, k.length) -#define NOM_VALUE(j, t) nomValue(j, t, 1, 0) -#define NOM_INT(j, t) strtol(j + (t++)->start, NULL, 10) -#define NOM_STR(j, t) (gltfString) { (char* )j + t->start, t->end - t->start }; t++ -#define NOM_BOOL(j, t) (*(j + (t++)->start) == 't') -#define NOM_FLOAT(j, t) strtof(j + (t++)->start, NULL) - -typedef struct { - char* data; - size_t length; -} gltfString; - -typedef struct { - uint32_t magic; - uint32_t version; - uint32_t length; -} gltfHeader; - -typedef struct { - uint32_t length; - uint32_t type; -} gltfChunkHeader; - -typedef struct { - int input; - int output; - SmoothMode smoothing; -} gltfAnimationSampler; - -typedef struct { - uint32_t primitiveIndex; - uint32_t primitiveCount; -} gltfMesh; - -typedef struct { - TextureFilter filter; - TextureWrap wrap; -} gltfSampler; - -typedef struct { - uint32_t node; - uint32_t nodeCount; -} gltfScene; - -static int nomValue(const char* data, jsmntok_t* token, int count, int sum) { - if (count == 0) { return sum; } - switch (token->type) { - case JSMN_OBJECT: return nomValue(data, token + 1, count - 1 + 2 * token->size, sum + 1); - case JSMN_ARRAY: return nomValue(data, token + 1, count - 1 + token->size, sum + 1); - default: return nomValue(data, token + 1, count - 1, sum + 1); - } -} - -// Kinda like total += sum(map(arr, obj => #obj[key])) -static jsmntok_t* aggregate(const char* json, jsmntok_t* token, const char* target, int* total) { - for (int i = (token++)->size; i > 0; i--) { - if (token->size > 0) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, target)) { - *total += token->size; - } - token += NOM_VALUE(json, token); - } - } - } - return token; -} - -static jsmntok_t* parseTextureInfo(const char* json, jsmntok_t* token, int* dest) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "index")) { - *dest = NOM_INT(json, token); - } else if (STR_EQ(key, "texCoord")) { - lovrAssert(NOM_INT(json, token) == 0, "Only one set of texture coordinates is supported"); - } else { - token += NOM_VALUE(json, token); - } - } - return token; -} ModelData* lovrModelDataInit(ModelData* model, Blob* source, ModelDataIO io) { - uint8_t* data = source->data; - gltfHeader* header = (gltfHeader*) data; - bool glb = header->magic == MAGIC_glTF; - const char *json, *binData; - size_t jsonLength, binLength; - ptrdiff_t binOffset; - - char basePath[1024]; - strncpy(basePath, source->name, 1023); - char* slash = strrchr(basePath, '/'); - if (slash) *slash = 0; - - if (glb) { - gltfChunkHeader* jsonHeader = (gltfChunkHeader*) &header[1]; - lovrAssert(jsonHeader->type == MAGIC_JSON, "Invalid JSON header"); - - json = (char*) &jsonHeader[1]; - jsonLength = jsonHeader->length; - - gltfChunkHeader* binHeader = (gltfChunkHeader*) &json[jsonLength]; - lovrAssert(binHeader->type == MAGIC_BIN, "Invalid BIN header"); - - binData = (char*) &binHeader[1]; - binLength = binHeader->length; - binOffset = (char*) binData - (char*) source->data; - } else { - json = (char*) data; - jsonLength = source->size; - binData = NULL; - binLength = 0; - binOffset = 0; + if (lovrModelDataInitGltf(model, source, io)) { + return model; + } else if (lovrModelDataInitObj(model, source, io)) { + return model; } - jsmn_parser parser; - jsmn_init(&parser); - int tokenCount = jsmn_parse(&parser, json, jsonLength, NULL, 0); - jsmntok_t* tokens = malloc(tokenCount * sizeof(jsmntok_t)); - jsmn_init(&parser); - tokenCount = jsmn_parse(&parser, json, jsonLength, tokens, tokenCount); - lovrAssert(tokenCount >= 0, "Invalid JSON"); - lovrAssert(tokens[0].type == JSMN_OBJECT, "No root object"); - - // Preparse - struct { - size_t totalSize; - jsmntok_t* animations; - jsmntok_t* attributes; - jsmntok_t* buffers; - jsmntok_t* bufferViews; - jsmntok_t* images; - jsmntok_t* textures; - jsmntok_t* materials; - jsmntok_t* meshes; - jsmntok_t* nodes; - jsmntok_t* scenes; - jsmntok_t* skins; - int animationChannelCount; - int childCount; - int jointCount; - int charCount; - int sceneCount; - } info = { 0 }; - - gltfAnimationSampler* animationSamplers = NULL; - gltfMesh* meshes = NULL; - gltfSampler* samplers = NULL; - gltfScene* scenes = NULL; - int rootScene = 0; - - for (jsmntok_t* token = tokens + 1; token < tokens + tokenCount;) { - gltfString key = NOM_STR(json, token); - - if (STR_EQ(key, "accessors")) { - info.attributes = token; - model->attributeCount = token->size; - info.totalSize += token->size * sizeof(ModelAttribute); - token += NOM_VALUE(json, token); - - } else if (STR_EQ(key, "animations")){ - info.animations = token; - model->animationCount = token->size; - info.totalSize += token->size * sizeof(ModelAnimation); - size_t samplerCount = 0; - jsmntok_t* t = token; - for (int i = (t++)->size; i > 0; i--) { - if (t->size > 0) { - for (int k = (t++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, t); - if (STR_EQ(key, "channels")) { info.animationChannelCount += t->size; } - else if (STR_EQ(key, "samplers")) { samplerCount += t->size; } - else if (STR_EQ(key, "name")) { - info.charCount += t->end - t->start + 1; - info.totalSize += t->end - t->start + 1; - } - t += NOM_VALUE(json, t); - } - } - } - - info.totalSize += info.animationChannelCount * sizeof(ModelAnimationChannel); - animationSamplers = malloc(samplerCount * sizeof(gltfAnimationSampler)); - gltfAnimationSampler* sampler = animationSamplers; - for (int i = (token++)->size; i > 0; i--) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "samplers")) { - for (int j = (token++)->size; j > 0; j--, sampler++) { - sampler->input = -1; - sampler->output = -1; - sampler->smoothing = SMOOTH_LINEAR; - for (int kk = (token++)->size; kk > 0; kk--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "input")) { sampler->input = NOM_INT(json, token); } - else if (STR_EQ(key, "output")) { sampler->output = NOM_INT(json, token); } - else if (STR_EQ(key, "interpolation")) { - gltfString smoothing = NOM_STR(json, token); - if (STR_EQ(smoothing, "LINEAR")) { sampler->smoothing = SMOOTH_LINEAR; } - else if (STR_EQ(smoothing, "STEP")) { sampler->smoothing = SMOOTH_STEP; } - else if (STR_EQ(smoothing, "CUBICSPLINE")) { sampler->smoothing = SMOOTH_CUBIC; } - else { lovrThrow("Unknown animation sampler interpolation"); } - } else { - token += NOM_VALUE(json, token); - } - } - } - } else { - token += NOM_VALUE(json, token); - } - } - } - - } else if (STR_EQ(key, "buffers")) { - info.buffers = token; - model->blobCount = token->size; - info.totalSize += token->size * sizeof(Blob*); - token += NOM_VALUE(json, token); - - } else if (STR_EQ(key, "bufferViews")) { - info.bufferViews = token; - model->bufferCount = token->size; - info.totalSize += token->size * sizeof(ModelBuffer); - token += NOM_VALUE(json, token); - - } else if (STR_EQ(key, "images")) { - info.images = token; - model->imageCount = token->size; - info.totalSize += token->size * sizeof(TextureData*); - token += NOM_VALUE(json, token); - - } else if (STR_EQ(key, "samplers")) { - samplers = malloc(token->size * sizeof(gltfSampler)); - gltfSampler* sampler = samplers; - for (int i = (token++)->size; i > 0; i--, sampler++) { - sampler->filter.mode = FILTER_TRILINEAR; - sampler->wrap.s = sampler->wrap.t = sampler->wrap.r = WRAP_REPEAT; - int min = -1; - int mag = -1; - - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "minFilter")) { min = NOM_INT(json, token); } - else if (STR_EQ(key, "magFilter")) { mag = NOM_INT(json, token); } - else if (STR_EQ(key, "wrapS")) { - switch (NOM_INT(json, token)) { - case 33071: sampler->wrap.s = WRAP_CLAMP; break; - case 33648: sampler->wrap.s = WRAP_MIRRORED_REPEAT; break; - case 10497: sampler->wrap.s = WRAP_REPEAT; break; - default: lovrThrow("Unknown sampler wrapS mode for sampler %d", i); - } - } else if (STR_EQ(key, "wrapT")) { - switch (NOM_INT(json, token)) { - case 33071: sampler->wrap.t = WRAP_CLAMP; break; - case 33648: sampler->wrap.t = WRAP_MIRRORED_REPEAT; break; - case 10497: sampler->wrap.t = WRAP_REPEAT; break; - default: lovrThrow("Unknown sampler wrapT mode for sampler %d", i); - } - } else { - token += NOM_VALUE(json, token); - } - } - - if (min == 9728 || min == 9984 || min == 9986 || mag == 9728) { - sampler->filter.mode = FILTER_NEAREST; - } else { - switch (min) { - case 9729: sampler->filter.mode = FILTER_BILINEAR; break; - case 9985: sampler->filter.mode = FILTER_BILINEAR; break; - case 9987: sampler->filter.mode = FILTER_TRILINEAR; break; - } - } - } - - } else if (STR_EQ(key, "textures")) { - info.textures = token; - model->textureCount = token->size; - info.totalSize += token->size * sizeof(ModelTexture); - token += NOM_VALUE(json, token); - - } else if (STR_EQ(key, "materials")) { - info.materials = token; - model->materialCount = token->size; - info.totalSize += token->size * sizeof(ModelMaterial); - token += NOM_VALUE(json, token); - - } else if (STR_EQ(key, "meshes")) { - info.meshes = token; - meshes = malloc(token->size * sizeof(gltfMesh)); - gltfMesh* mesh = meshes; - model->primitiveCount = 0; - for (int i = (token++)->size; i > 0; i--, mesh++) { - mesh->primitiveCount = 0; - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "primitives")) { - mesh->primitiveIndex = model->primitiveCount; - mesh->primitiveCount += token->size; - model->primitiveCount += token->size; - } - token += NOM_VALUE(json, token); - } - } - info.totalSize += model->primitiveCount * sizeof(ModelPrimitive); - - } else if (STR_EQ(key, "nodes")) { - info.nodes = token; - model->nodeCount += token->size; - info.totalSize += token->size * sizeof(ModelNode); - token = aggregate(json, token, "children", &info.childCount); - info.totalSize += info.childCount * sizeof(uint32_t); - - } else if (STR_EQ(key, "scene")) { - rootScene = NOM_INT(json, token); - - } else if (STR_EQ(key, "scenes")) { - info.scenes = token; - info.sceneCount = token->size; - scenes = malloc(info.sceneCount * sizeof(gltfScene)); - gltfScene* scene = scenes; - for (int i = (token++)->size; i > 0; i--, scene++) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "nodes")) { - scene->nodeCount = token->size; - jsmntok_t* t = token + 1; - scene->node = NOM_INT(json, t); - } - token += NOM_VALUE(json, token); - } - } - - } else if (STR_EQ(key, "skins")) { - info.skins = token; - model->skinCount = token->size; - info.totalSize += token->size * sizeof(ModelSkin); - token = aggregate(json, token, "joints", &info.jointCount); - info.totalSize += info.jointCount * sizeof(uint32_t); - - } else { - token += NOM_VALUE(json, token); - } - } - - // Make space for fake root node if the root scene has multiple root nodes - if (info.sceneCount > 0 && scenes[rootScene].nodeCount > 1) { - info.childCount += model->nodeCount; - info.totalSize += sizeof(ModelNode) + model->nodeCount * sizeof(uint32_t); - model->nodeCount++; - } - - // Allocate - size_t offset = 0; - int childIndex = 0; - model->data = calloc(1, info.totalSize); - model->animations = (ModelAnimation*) (model->data + offset), offset += model->animationCount * sizeof(ModelAnimation); - model->attributes = (ModelAttribute*) (model->data + offset), offset += model->attributeCount * sizeof(ModelAttribute); - model->blobs = (Blob**) (model->data + offset), offset += model->blobCount * sizeof(Blob*); - model->buffers = (ModelBuffer*) (model->data + offset), offset += model->bufferCount * sizeof(ModelBuffer); - model->images = (TextureData**) (model->data + offset), offset += model->imageCount * sizeof(TextureData*); - model->textures = (ModelTexture*) (model->data + offset), offset += model->textureCount * sizeof(ModelTexture); - model->materials = (ModelMaterial*) (model->data + offset), offset += model->materialCount * sizeof(ModelMaterial); - model->primitives = (ModelPrimitive*) (model->data + offset), offset += model->primitiveCount * sizeof(ModelPrimitive); - model->nodes = (ModelNode*) (model->data + offset), offset += model->nodeCount * sizeof(ModelNode); - model->skins = (ModelSkin*) (model->data + offset), offset += model->skinCount * sizeof(ModelSkin); - ModelAnimationChannel* channels = (ModelAnimationChannel*) (model->data + offset); offset += info.animationChannelCount * sizeof(ModelAnimationChannel); - uint32_t* nodeChildren = (uint32_t*) (model->data + offset); offset += info.childCount * sizeof(uint32_t); - uint32_t* skinJoints = (uint32_t*) (model->data + offset); offset += info.jointCount * sizeof(uint32_t); - char* chars = (char*) (model->data + offset); offset += info.charCount * sizeof(char); - - // Blobs - if (info.buffers) { - jsmntok_t* token = info.buffers; - Blob** blob = model->blobs; - for (int i = (token++)->size; i > 0; i--, blob++) { - gltfString uri = { 0 }; - size_t size = 0; - - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "byteLength")) { size = NOM_INT(json, token); } - else if (STR_EQ(key, "uri")) { uri = NOM_STR(json, token); } - else { token += NOM_VALUE(json, token); } - } - - if (uri.data) { - lovrAssert(strncmp("data:", uri.data, strlen("data:")), "Base64 URIs aren't supported yet");; - size_t bytesRead; - char filename[1024]; - lovrAssert(uri.length < 1024, "Buffer filename is too long"); - snprintf(filename, 1023, "%s/%.*s", basePath, (int) uri.length, uri.data); - *blob = lovrBlobCreate(io.read(filename, &bytesRead), size, NULL); - lovrAssert((*blob)->data && bytesRead == size, "Unable to read %s", filename); - } else { - lovrAssert(glb, "Buffer is missing URI"); - lovrRetain(source); - *blob = source; - } - } - } - - // Buffers - if (info.bufferViews) { - jsmntok_t* token = info.bufferViews; - ModelBuffer* buffer = model->buffers; - for (int i = (token++)->size; i > 0; i--, buffer++) { - size_t offset = 0; - - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "buffer")) { buffer->data = model->blobs[NOM_INT(json, token)]->data; } - else if (STR_EQ(key, "byteOffset")) { offset = NOM_INT(json, token); } - else if (STR_EQ(key, "byteLength")) { buffer->size = NOM_INT(json, token); } - else if (STR_EQ(key, "byteStride")) { buffer->stride = NOM_INT(json, token); } - else { token += NOM_VALUE(json, token); } - } - - if (buffer->data && buffer->data == source->data && glb) { - offset += binOffset; - } - - buffer->data = (char*) buffer->data + offset; - } - } - - // Attributes - if (info.attributes) { - jsmntok_t* token = info.attributes; - ModelAttribute* attribute = model->attributes; - for (int i = (token++)->size; i > 0; i--, attribute++) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "bufferView")) { attribute->buffer = NOM_INT(json, token); } - else if (STR_EQ(key, "count")) { attribute->count = NOM_INT(json, token); } - else if (STR_EQ(key, "byteOffset")) { attribute->offset = NOM_INT(json, token); } - else if (STR_EQ(key, "normalized")) { attribute->normalized = NOM_BOOL(json, token); } - else if (STR_EQ(key, "componentType")) { - switch (NOM_INT(json, token)) { - case 5120: attribute->type = I8; break; - case 5121: attribute->type = U8; break; - case 5122: attribute->type = I16; break; - case 5123: attribute->type = U16; break; - case 5125: attribute->type = U32; break; - case 5126: attribute->type = F32; break; - default: break; - } - } else if (STR_EQ(key, "type")) { - gltfString type = NOM_STR(json, token); - if (STR_EQ(type, "SCALAR")) { - attribute->components = 1; - } else if (type.length == 4) { - attribute->components = type.data[3] - '0'; - attribute->matrix = type.data[0] == 'M'; - } - } else if (STR_EQ(key, "min") && token->size <= 4) { - int count = (token++)->size; - attribute->hasMin = true; - for (int j = 0; j < count; j++) { - attribute->min[j] = NOM_FLOAT(json, token); - } - } else if (STR_EQ(key, "max") && token->size <= 4) { - int count = (token++)->size; - attribute->hasMax = true; - for (int j = 0; j < count; j++) { - attribute->max[j] = NOM_FLOAT(json, token); - } - } else { - token += NOM_VALUE(json, token); - } - } - } - } - - // Animations - if (info.animations) { - int channelIndex = 0; - int baseSampler = 0; - jsmntok_t* token = info.animations; - ModelAnimation* animation = model->animations; - for (int i = (token++)->size; i > 0; i--, animation++) { - int samplerCount = 0; - animation->name = NULL; - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "channels")) { - animation->channelCount = (token++)->size; - animation->channels = channels + channelIndex; - channelIndex += animation->channelCount; - for (int j = 0; j < animation->channelCount; j++) { - ModelAnimationChannel* channel = &animation->channels[j]; - ModelAttribute* times = NULL; - ModelAttribute* data = NULL; - - for (int kk = (token++)->size; kk > 0; kk--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "sampler")) { - gltfAnimationSampler* sampler = animationSamplers + baseSampler + NOM_INT(json, token); - times = &model->attributes[sampler->input]; - data = &model->attributes[sampler->output]; - channel->smoothing = sampler->smoothing; - channel->keyframeCount = times->count; - } else if (STR_EQ(key, "target")) { - for (int kkk = (token++)->size; kkk > 0; kkk--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "node")) { channel->nodeIndex = NOM_INT(json, token); } - else if (STR_EQ(key, "path")) { - gltfString property = NOM_STR(json, token); - if (STR_EQ(property, "translation")) { channel->property = PROP_TRANSLATION; } - else if (STR_EQ(property, "rotation")) { channel->property = PROP_ROTATION; } - else if (STR_EQ(property, "scale")) { channel->property = PROP_SCALE; } - else { lovrThrow("Unknown animation channel property"); } - } else { - token += NOM_VALUE(json, token); - } - } - } else { - token += NOM_VALUE(json, token); - } - } - - lovrAssert(times, "Missing keyframe times"); - lovrAssert(data, "Missing keyframe data"); - - ModelBuffer* buffer; - buffer = &model->buffers[times->buffer]; - lovrAssert(times->type == F32 && (buffer->stride == 0 || buffer->stride == sizeof(float)), "Keyframe times must be tightly-packed floats"); - channel->times = (float*) (buffer->data + times->offset); - - buffer = &model->buffers[data->buffer]; - uint8_t components = data->components; - lovrAssert(data->type == F32 && (buffer->stride == 0 || buffer->stride == sizeof(float) * components), "Keyframe data must be tightly-packed floats"); - channel->data = (float*) (buffer->data + data->offset); - - animation->duration = MAX(animation->duration, channel->times[channel->keyframeCount - 1]); - } - } else if (STR_EQ(key, "samplers")) { - samplerCount = token->size; - token += NOM_VALUE(json, token); - } else if (STR_EQ(key, "name")) { - gltfString name = NOM_STR(json, token); - memcpy(chars, name.data, name.length); - chars[name.length] = '\0'; - animation->name = chars; - chars += name.length; - } else { - token += NOM_VALUE(json, token); - } - } - baseSampler += samplerCount; - } - } - - // Images - if (info.images) { - jsmntok_t* token = info.images; - TextureData** image = model->images; - for (int i = (token++)->size; i > 0; i--, image++) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "bufferView")) { - ModelBuffer* buffer = &model->buffers[NOM_INT(json, token)]; - Blob* blob = lovrBlobCreate(buffer->data, buffer->size, NULL); - *image = lovrTextureDataCreateFromBlob(blob, false); - blob->data = NULL; // FIXME - lovrRelease(blob); - } else if (STR_EQ(key, "uri")) { - size_t size = 0; - char filename[1024]; - gltfString uri = NOM_STR(json, token); - lovrAssert(strncmp("data:", uri.data, strlen("data:")), "Base64 URIs aren't supported yet");; - snprintf(filename, 1024, "%s/%.*s%c", basePath, (int) uri.length, uri.data, 0); - void* data = io.read(filename, &size); - lovrAssert(data && size > 0, "Unable to read image from '%s'", filename); - Blob* blob = lovrBlobCreate(data, size, NULL); - *image = lovrTextureDataCreateFromBlob(blob, false); - lovrRelease(blob); - } else { - token += NOM_VALUE(json, token); - } - } - } - } - - // Textures - if (info.textures) { - jsmntok_t* token = info.textures; - ModelTexture* texture = model->textures; - for (int i = (token++)->size; i > 0; i--, texture++) { - texture->filter.mode = FILTER_TRILINEAR; - texture->wrap.s = texture->wrap.t = WRAP_REPEAT; - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "source")) { - texture->imageIndex = NOM_INT(json, token); - } else if (STR_EQ(key, "sampler")) { - gltfSampler* sampler = &samplers[NOM_INT(json, token)]; - texture->filter = sampler->filter; - texture->wrap = sampler->wrap; - } - else { token += NOM_VALUE(json, token); } - } - } - } - - // Materials - if (info.materials) { - jsmntok_t* token = info.materials; - ModelMaterial* material = model->materials; - for (int i = (token++)->size; i > 0; i--, material++) { - material->scalars[SCALAR_METALNESS] = 1.f; - material->scalars[SCALAR_ROUGHNESS] = 1.f; - material->colors[COLOR_DIFFUSE] = (Color) { 1.f, 1.f, 1.f, 1.f }; - material->colors[COLOR_EMISSIVE] = (Color) { 1.f, 1.f, 1.f, 1.f }; - - for (int j = 0; j < MAX_MATERIAL_TEXTURES; j++) { - material->textures[j] = -1; - } - - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "pbrMetallicRoughness")) { - for (int j = (token++)->size; j > 0; j--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "baseColorFactor")) { - token++; // Enter array - material->colors[COLOR_DIFFUSE].r = NOM_FLOAT(json, token); - material->colors[COLOR_DIFFUSE].g = NOM_FLOAT(json, token); - material->colors[COLOR_DIFFUSE].b = NOM_FLOAT(json, token); - material->colors[COLOR_DIFFUSE].a = NOM_FLOAT(json, token); - } else if (STR_EQ(key, "baseColorTexture")) { - token = parseTextureInfo(json, token, &material->textures[TEXTURE_DIFFUSE]); - } else if (STR_EQ(key, "metallicFactor")) { - material->scalars[SCALAR_METALNESS] = NOM_FLOAT(json, token); - } else if (STR_EQ(key, "roughnessFactor")) { - material->scalars[SCALAR_ROUGHNESS] = NOM_FLOAT(json, token); - } else if (STR_EQ(key, "metallicRoughnessTexture")) { - token = parseTextureInfo(json, token, &material->textures[TEXTURE_METALNESS]); - material->textures[TEXTURE_ROUGHNESS] = material->textures[TEXTURE_METALNESS]; - } else { - token += NOM_VALUE(json, token); - } - } - } else if (STR_EQ(key, "normalTexture")) { - token = parseTextureInfo(json, token, &material->textures[TEXTURE_NORMAL]); - } else if (STR_EQ(key, "occlusionTexture")) { - token = parseTextureInfo(json, token, &material->textures[TEXTURE_OCCLUSION]); - } else if (STR_EQ(key, "emissiveTexture")) { - token = parseTextureInfo(json, token, &material->textures[TEXTURE_EMISSIVE]); - } else if (STR_EQ(key, "emissiveFactor")) { - token++; // Enter array - material->colors[COLOR_EMISSIVE].r = NOM_FLOAT(json, token); - material->colors[COLOR_EMISSIVE].g = NOM_FLOAT(json, token); - material->colors[COLOR_EMISSIVE].b = NOM_FLOAT(json, token); - } else { - token += NOM_VALUE(json, token); - } - } - } - } - - // Primitives - if (info.meshes) { - int primitiveIndex = 0; - jsmntok_t* token = info.meshes; - ModelPrimitive* primitive = model->primitives; - for (int i = (token++)->size; i > 0; i--) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "primitives")) { - for (uint32_t j = (token++)->size; j > 0; j--, primitive++) { - primitive->mode = DRAW_TRIANGLES; - primitive->material = -1; - - for (int kk = (token++)->size; kk > 0; kk--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "material")) { - primitive->material = NOM_INT(json, token); - } else if (STR_EQ(key, "indices")) { - primitive->indices = &model->attributes[NOM_INT(json, token)]; - lovrAssert(primitive->indices->type != U8, "Unsigned byte indices are not supported (must be unsigned shorts or unsigned ints)"); - } else if (STR_EQ(key, "mode")) { - switch (NOM_INT(json, token)) { - case 0: primitive->mode = DRAW_POINTS; break; - case 1: primitive->mode = DRAW_LINES; break; - case 2: primitive->mode = DRAW_LINE_LOOP; break; - case 3: primitive->mode = DRAW_LINE_STRIP; break; - case 4: primitive->mode = DRAW_TRIANGLES; break; - case 5: primitive->mode = DRAW_TRIANGLE_STRIP; break; - case 6: primitive->mode = DRAW_TRIANGLE_FAN; break; - default: lovrThrow("Unknown primitive mode"); - } - } else if (STR_EQ(key, "attributes")) { - int attributeCount = (token++)->size; - for (int a = 0; a < attributeCount; a++) { - DefaultAttribute attributeType = -1; - gltfString name = NOM_STR(json, token); - int attributeIndex = NOM_INT(json, token); - if (STR_EQ(name, "POSITION")) { attributeType = ATTR_POSITION; } - else if (STR_EQ(name, "NORMAL")) { attributeType = ATTR_NORMAL; } - else if (STR_EQ(name, "TEXCOORD_0")) { attributeType = ATTR_TEXCOORD; } - else if (STR_EQ(name, "COLOR_0")) { attributeType = ATTR_COLOR; } - else if (STR_EQ(name, "TANGENT")) { attributeType = ATTR_TANGENT; } - else if (STR_EQ(name, "JOINTS_0")) { attributeType = ATTR_BONES; } - else if (STR_EQ(name, "WEIGHTS_0")) { attributeType = ATTR_WEIGHTS; } - if (attributeType >= 0) { - primitive->attributes[attributeType] = &model->attributes[attributeIndex]; - } - } - } else { - token += NOM_VALUE(json, token); - } - } - } - } else { - token += NOM_VALUE(json, token); - } - } - } - } - - // Nodes - if (info.nodes) { - jsmntok_t* token = info.nodes; - ModelNode* node = model->nodes; - for (int i = (token++)->size; i > 0; i--, node++) { - float translation[3] = { 0, 0, 0 }; - float rotation[4] = { 0, 0, 0, 0 }; - float scale[3] = { 1, 1, 1 }; - bool matrix = false; - node->primitiveCount = 0; - node->skin = -1; - - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "mesh")) { - gltfMesh* mesh = &meshes[NOM_INT(json, token)]; - node->primitiveIndex = mesh->primitiveIndex; - node->primitiveCount = mesh->primitiveCount; - } else if (STR_EQ(key, "skin")) { - node->skin = NOM_INT(json, token); - } else if (STR_EQ(key, "children")) { - node->children = &nodeChildren[childIndex]; - node->childCount = (token++)->size; - for (uint32_t j = 0; j < node->childCount; j++) { - nodeChildren[childIndex++] = NOM_INT(json, token); - } - } else if (STR_EQ(key, "matrix")) { - lovrAssert((token++)->size == 16, "Node matrix needs 16 elements"); - matrix = true; - for (int j = 0; j < 16; j++) { - node->transform[j] = NOM_FLOAT(json, token); - } - } else if (STR_EQ(key, "translation")) { - lovrAssert((token++)->size == 3, "Node translation needs 3 elements"); - translation[0] = NOM_FLOAT(json, token); - translation[1] = NOM_FLOAT(json, token); - translation[2] = NOM_FLOAT(json, token); - } else if (STR_EQ(key, "rotation")) { - lovrAssert((token++)->size == 4, "Node rotation needs 4 elements"); - rotation[0] = NOM_FLOAT(json, token); - rotation[1] = NOM_FLOAT(json, token); - rotation[2] = NOM_FLOAT(json, token); - rotation[3] = NOM_FLOAT(json, token); - } else if (STR_EQ(key, "scale")) { - lovrAssert((token++)->size == 3, "Node scale needs 3 elements"); - scale[0] = NOM_FLOAT(json, token); - scale[1] = NOM_FLOAT(json, token); - scale[2] = NOM_FLOAT(json, token); - } else { - token += NOM_VALUE(json, token); - } - } - - // Fix it in post - if (!matrix) { - mat4_identity(node->transform); - mat4_translate(node->transform, translation[0], translation[1], translation[2]); - mat4_rotateQuat(node->transform, rotation); - mat4_scale(node->transform, scale[0], scale[1], scale[2]); - } - } - } - - // Skins - if (info.skins) { - int jointIndex = 0; - jsmntok_t* token = info.skins; - ModelSkin* skin = model->skins; - for (int i = (token++)->size; i > 0; i--, skin++) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "inverseBindMatrices")) { - ModelAttribute* attribute = &model->attributes[NOM_INT(json, token)]; - ModelBuffer* buffer = &model->buffers[attribute->buffer]; - skin->inverseBindMatrices = (float*) ((uint8_t*) buffer->data + attribute->offset); - } else if (STR_EQ(key, "skeleton")) { - skin->skeleton = NOM_INT(json, token); - } else if (STR_EQ(key, "joints")) { - skin->joints = &skinJoints[jointIndex]; - skin->jointCount = (token++)->size; - for (uint32_t j = 0; j < skin->jointCount; j++) { - skinJoints[jointIndex++] = NOM_INT(json, token); - } - } else { - token += NOM_VALUE(json, token); - } - } - } - } - - // Scenes - if (info.sceneCount == 0) { - model->rootNode = 0; - } else if (scenes[rootScene].nodeCount > 1) { - model->rootNode = model->nodeCount - 1; - ModelNode* lastNode = &model->nodes[model->rootNode]; - lastNode->childCount = scenes[rootScene].nodeCount; - lastNode->children = &nodeChildren[childIndex]; - mat4_identity(lastNode->transform); - lastNode->primitiveCount = 0; - lastNode->skin = -1; - - jsmntok_t* token = info.scenes; - int sceneCount = (token++)->size; - for (int i = 0; i < sceneCount; i++) { - if (i == rootScene) { - for (int k = (token++)->size; k > 0; k--) { - gltfString key = NOM_STR(json, token); - if (STR_EQ(key, "nodes")) { - for (int j = (token++)->size; j > 0; j--) { - lastNode->children[lastNode->childCount - j] = NOM_INT(json, token); - } - } else { - token += NOM_VALUE(json, token); - } - } - } else { - token += NOM_VALUE(json, token); - } - } - } else { - model->rootNode = scenes[rootScene].node; - } - - free(animationSamplers); - free(meshes); - free(samplers); - free(scenes); - free(tokens); - return model; + lovrThrow("Unable to load model from '%s'", source->name); + return NULL; } void lovrModelDataDestroy(void* ref) { @@ -873,3 +21,41 @@ void lovrModelDataDestroy(void* ref) { } free(model->data); } + +// Note: this code is a scary optimization +void lovrModelDataAllocate(ModelData* model) { + size_t totalSize = 0; + size_t sizes[14]; + totalSize += sizes[0] = model->blobCount * sizeof(Blob*); + totalSize += sizes[1] = model->imageCount * sizeof(TextureData*); + totalSize += sizes[2] = model->animationCount * sizeof(ModelAnimation); + totalSize += sizes[3] = model->attributeCount * sizeof(ModelAttribute); + totalSize += sizes[4] = model->bufferCount * sizeof(ModelBuffer); + totalSize += sizes[5] = model->textureCount * sizeof(ModelTexture); + totalSize += sizes[6] = model->materialCount * sizeof(ModelMaterial); + totalSize += sizes[7] = model->primitiveCount * sizeof(ModelPrimitive); + totalSize += sizes[8] = model->nodeCount * sizeof(ModelNode); + totalSize += sizes[9] = model->skinCount * sizeof(ModelSkin); + totalSize += sizes[10] = model->channelCount * sizeof(ModelAnimationChannel); + totalSize += sizes[11] = model->childCount * sizeof(uint32_t); + totalSize += sizes[12] = model->jointCount * sizeof(uint32_t); + totalSize += sizes[13] = model->charCount * sizeof(char); + + size_t offset = 0; + char* p = model->data = calloc(1, totalSize); + lovrAssert(model->data, "Out of memory"); + model->blobs = (Blob**) (p + offset), offset += sizes[0]; + model->images = (TextureData**) (p + offset), offset += sizes[1]; + model->animations = (ModelAnimation*) (p + offset), offset += sizes[2]; + model->attributes = (ModelAttribute*) (p + offset), offset += sizes[3]; + model->buffers = (ModelBuffer*) (p + offset), offset += sizes[4]; + model->textures = (ModelTexture*) (p + offset), offset += sizes[5]; + model->materials = (ModelMaterial*) (p + offset), offset += sizes[6]; + model->primitives = (ModelPrimitive*) (p + offset), offset += sizes[7]; + model->nodes = (ModelNode*) (p + offset), offset += sizes[8]; + model->skins = (ModelSkin*) (p + offset), offset += sizes[9]; + model->channels = (ModelAnimationChannel*) (p + offset), offset += sizes[10]; + model->children = (uint32_t*) (p + offset), offset += sizes[11]; + model->joints = (uint32_t*) (p + offset), offset += sizes[12]; + model->chars = (char*) (p + offset), offset += sizes[13]; +} diff --git a/src/data/modelData.h b/src/data/modelData.h index a46e195d..02186fb9 100644 --- a/src/data/modelData.h +++ b/src/data/modelData.h @@ -97,8 +97,8 @@ typedef struct { } ModelBuffer; typedef struct { - uint32_t buffer; size_t offset; + uint32_t buffer; uint32_t count; AttributeType type; unsigned int components : 3; @@ -139,9 +139,9 @@ typedef struct { } ModelMaterial; typedef struct { - DrawMode mode; ModelAttribute* attributes[MAX_DEFAULT_ATTRIBUTES]; ModelAttribute* indices; + DrawMode mode; int material; } ModelPrimitive; @@ -157,13 +157,12 @@ typedef struct { typedef struct { uint32_t* joints; uint32_t jointCount; - int skeleton; float* inverseBindMatrices; } ModelSkin; typedef struct { Ref ref; - uint8_t* data; + void* data; Blob** blobs; TextureData** images; ModelAnimation* animations; @@ -185,6 +184,15 @@ typedef struct { int nodeCount; int skinCount; int rootNode; + + ModelAnimationChannel* channels; + uint32_t* children; + uint32_t* joints; + char* chars; + int channelCount; + int childCount; + int jointCount; + int charCount; } ModelData; typedef struct { @@ -192,5 +200,8 @@ typedef struct { } ModelDataIO; ModelData* lovrModelDataInit(ModelData* model, Blob* blob, ModelDataIO io); +ModelData* lovrModelDataInitGltf(ModelData* model, Blob* blob, ModelDataIO io); +ModelData* lovrModelDataInitObj(ModelData* model, Blob* blob, ModelDataIO io); #define lovrModelDataCreate(...) lovrModelDataInit(lovrAlloc(ModelData), __VA_ARGS__) void lovrModelDataDestroy(void* ref); +void lovrModelDataAllocate(ModelData* model); diff --git a/src/data/modelData_gltf.c b/src/data/modelData_gltf.c new file mode 100644 index 00000000..054c98ef --- /dev/null +++ b/src/data/modelData_gltf.c @@ -0,0 +1,858 @@ +#include "data/modelData.h" +#include "lib/math.h" +#include "lib/jsmn/jsmn.h" +#include +#include +#include +#include + +#define MAX_STACK_TOKENS 1024 + +#define MAGIC_glTF 0x46546c67 +#define MAGIC_JSON 0x4e4f534a +#define MAGIC_BIN 0x004e4942 + +#define STR_EQ(k, s) !strncmp(k.data, s, k.length) +#define NOM_VALUE(j, t) nomValue(j, t, 1, 0) +#define NOM_INT(j, t) nomInt(j + (t++)->start) +#define NOM_STR(j, t) (gltfString) { (char* )j + t->start, t->end - t->start }; t++ +#define NOM_BOOL(j, t) (*(j + (t++)->start) == 't') +#define NOM_FLOAT(j, t) atof(j + (t++)->start) + +typedef struct { + char* data; + size_t length; +} gltfString; + +typedef struct { + uint32_t magic; + uint32_t version; + uint32_t length; +} gltfHeader; + +typedef struct { + uint32_t length; + uint32_t type; +} gltfChunkHeader; + +typedef struct { + int input; + int output; + SmoothMode smoothing; +} gltfAnimationSampler; + +typedef struct { + uint32_t primitiveIndex; + uint32_t primitiveCount; +} gltfMesh; + +typedef struct { + TextureFilter filter; + TextureWrap wrap; +} gltfSampler; + +typedef struct { + uint32_t node; + uint32_t nodeCount; +} gltfScene; + +static int nomInt(const char* s) { + int n = 0; + bool negative = (*s == '-'); + s += negative; + while (isdigit(*s)) { n = 10 * n + (*s++ - '0'); } + return negative ? -n : n; +} + +static int nomValue(const char* data, jsmntok_t* token, int count, int sum) { + if (count == 0) { return sum; } + switch (token->type) { + case JSMN_OBJECT: return nomValue(data, token + 1, count - 1 + 2 * token->size, sum + 1); + case JSMN_ARRAY: return nomValue(data, token + 1, count - 1 + token->size, sum + 1); + default: return nomValue(data, token + 1, count - 1, sum + 1); + } +} + +// Kinda like total += sum(map(arr, obj => #obj[key])) +static jsmntok_t* aggregate(const char* json, jsmntok_t* token, const char* target, int* total) { + for (int i = (token++)->size; i > 0; i--) { + if (token->size > 0) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, target)) { + *total += token->size; + } + token += NOM_VALUE(json, token); + } + } + } + return token; +} + +static jsmntok_t* parseTextureInfo(const char* json, jsmntok_t* token, int* dest) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "index")) { + *dest = NOM_INT(json, token); + } else if (STR_EQ(key, "texCoord")) { + lovrAssert(NOM_INT(json, token) == 0, "Only one set of texture coordinates is supported"); + } else { + token += NOM_VALUE(json, token); + } + } + return token; +} + +ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO io) { + uint8_t* data = source->data; + gltfHeader* header = (gltfHeader*) data; + bool glb = header->magic == MAGIC_glTF; + const char *json, *binData; + size_t jsonLength, binLength; + ptrdiff_t binOffset; + + char basePath[1024]; + strncpy(basePath, source->name, 1023); + char* slash = strrchr(basePath, '/'); + if (slash) *slash = 0; + + if (glb) { + gltfChunkHeader* jsonHeader = (gltfChunkHeader*) &header[1]; + lovrAssert(jsonHeader->type == MAGIC_JSON, "Invalid JSON header"); + + json = (char*) &jsonHeader[1]; + jsonLength = jsonHeader->length; + + gltfChunkHeader* binHeader = (gltfChunkHeader*) &json[jsonLength]; + lovrAssert(binHeader->type == MAGIC_BIN, "Invalid BIN header"); + + binData = (char*) &binHeader[1]; + binLength = binHeader->length; + binOffset = (char*) binData - (char*) source->data; + } else { + json = (char*) data; + jsonLength = source->size; + binData = NULL; + binLength = 0; + binOffset = 0; + } + + // Parse JSON + jsmn_parser parser; + jsmn_init(&parser); + + jsmntok_t stackTokens[MAX_STACK_TOKENS]; + jsmntok_t* heapTokens = NULL; + jsmntok_t* tokens = &stackTokens[0]; + int tokenCount = 0; + + if ((tokenCount = jsmn_parse(&parser, json, jsonLength, stackTokens, MAX_STACK_TOKENS)) == JSMN_ERROR_NOMEM) { + size_t capacity = MAX_STACK_TOKENS; + + do { + capacity *= 2; + lovrAssert(heapTokens = realloc(heapTokens, capacity), "Out of memory"); + tokenCount = jsmn_parse(&parser, json, jsonLength, heapTokens, capacity); + } while (tokenCount == JSMN_ERROR_NOMEM); + + tokens = heapTokens; + } + + if (tokenCount <= 0 || tokens[0].type != JSMN_OBJECT) { + free(heapTokens); + return NULL; + } + + // Prepass: Basically we iterate over the tokens once and figure out how much memory we need and + // record the locations of tokens that we'll use later to fill in the memory once it's allocated. + + struct { + jsmntok_t* animations; + jsmntok_t* attributes; + jsmntok_t* buffers; + jsmntok_t* bufferViews; + jsmntok_t* images; + jsmntok_t* textures; + jsmntok_t* materials; + jsmntok_t* meshes; + jsmntok_t* nodes; + jsmntok_t* scenes; + jsmntok_t* skins; + int sceneCount; + } info = { 0 }; + + gltfAnimationSampler* animationSamplers = NULL; + gltfMesh* meshes = NULL; + gltfSampler* samplers = NULL; + gltfScene* scenes = NULL; + int rootScene = 0; + + for (jsmntok_t* token = tokens + 1; token < tokens + tokenCount;) { + gltfString key = NOM_STR(json, token); + + if (STR_EQ(key, "accessors")) { + info.attributes = token; + model->attributeCount = token->size; + token += NOM_VALUE(json, token); + + } else if (STR_EQ(key, "animations")){ + info.animations = token; + model->animationCount = token->size; + size_t samplerCount = 0; + jsmntok_t* t = token; + for (int i = (t++)->size; i > 0; i--) { + if (t->size > 0) { + for (int k = (t++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, t); + if (STR_EQ(key, "channels")) { model->channelCount += t->size; } + else if (STR_EQ(key, "samplers")) { samplerCount += t->size; } + else if (STR_EQ(key, "name")) { model->charCount += t->end - t->start + 1; } + t += NOM_VALUE(json, t); + } + } + } + + animationSamplers = malloc(samplerCount * sizeof(gltfAnimationSampler)); + lovrAssert(animationSamplers, "Out of memory"); + gltfAnimationSampler* sampler = animationSamplers; + for (int i = (token++)->size; i > 0; i--) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "samplers")) { + for (int j = (token++)->size; j > 0; j--, sampler++) { + sampler->input = -1; + sampler->output = -1; + sampler->smoothing = SMOOTH_LINEAR; + for (int kk = (token++)->size; kk > 0; kk--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "input")) { sampler->input = NOM_INT(json, token); } + else if (STR_EQ(key, "output")) { sampler->output = NOM_INT(json, token); } + else if (STR_EQ(key, "interpolation")) { + gltfString smoothing = NOM_STR(json, token); + if (STR_EQ(smoothing, "LINEAR")) { sampler->smoothing = SMOOTH_LINEAR; } + else if (STR_EQ(smoothing, "STEP")) { sampler->smoothing = SMOOTH_STEP; } + else if (STR_EQ(smoothing, "CUBICSPLINE")) { sampler->smoothing = SMOOTH_CUBIC; } + else { lovrThrow("Unknown animation sampler interpolation"); } + } else { + token += NOM_VALUE(json, token); + } + } + } + } else { + token += NOM_VALUE(json, token); + } + } + } + + } else if (STR_EQ(key, "buffers")) { + info.buffers = token; + model->blobCount = token->size; + token += NOM_VALUE(json, token); + + } else if (STR_EQ(key, "bufferViews")) { + info.bufferViews = token; + model->bufferCount = token->size; + token += NOM_VALUE(json, token); + + } else if (STR_EQ(key, "images")) { + info.images = token; + model->imageCount = token->size; + token += NOM_VALUE(json, token); + + } else if (STR_EQ(key, "samplers")) { + samplers = malloc(token->size * sizeof(gltfSampler)); + lovrAssert(samplers, "Out of memory"); + gltfSampler* sampler = samplers; + for (int i = (token++)->size; i > 0; i--, sampler++) { + sampler->filter.mode = FILTER_TRILINEAR; + sampler->wrap.s = sampler->wrap.t = sampler->wrap.r = WRAP_REPEAT; + int min = -1; + int mag = -1; + + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "minFilter")) { min = NOM_INT(json, token); } + else if (STR_EQ(key, "magFilter")) { mag = NOM_INT(json, token); } + else if (STR_EQ(key, "wrapS")) { + switch (NOM_INT(json, token)) { + case 33071: sampler->wrap.s = WRAP_CLAMP; break; + case 33648: sampler->wrap.s = WRAP_MIRRORED_REPEAT; break; + case 10497: sampler->wrap.s = WRAP_REPEAT; break; + default: lovrThrow("Unknown sampler wrapS mode for sampler %d", i); + } + } else if (STR_EQ(key, "wrapT")) { + switch (NOM_INT(json, token)) { + case 33071: sampler->wrap.t = WRAP_CLAMP; break; + case 33648: sampler->wrap.t = WRAP_MIRRORED_REPEAT; break; + case 10497: sampler->wrap.t = WRAP_REPEAT; break; + default: lovrThrow("Unknown sampler wrapT mode for sampler %d", i); + } + } else { + token += NOM_VALUE(json, token); + } + } + + if (min == 9728 || min == 9984 || min == 9986 || mag == 9728) { + sampler->filter.mode = FILTER_NEAREST; + } else { + switch (min) { + case 9729: sampler->filter.mode = FILTER_BILINEAR; break; + case 9985: sampler->filter.mode = FILTER_BILINEAR; break; + case 9987: sampler->filter.mode = FILTER_TRILINEAR; break; + } + } + } + + } else if (STR_EQ(key, "textures")) { + info.textures = token; + model->textureCount = token->size; + token += NOM_VALUE(json, token); + + } else if (STR_EQ(key, "materials")) { + info.materials = token; + model->materialCount = token->size; + token += NOM_VALUE(json, token); + + } else if (STR_EQ(key, "meshes")) { + info.meshes = token; + meshes = malloc(token->size * sizeof(gltfMesh)); + lovrAssert(meshes, "Out of memory"); + gltfMesh* mesh = meshes; + model->primitiveCount = 0; + for (int i = (token++)->size; i > 0; i--, mesh++) { + mesh->primitiveCount = 0; + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "primitives")) { + mesh->primitiveIndex = model->primitiveCount; + mesh->primitiveCount += token->size; + model->primitiveCount += token->size; + } + token += NOM_VALUE(json, token); + } + } + + } else if (STR_EQ(key, "nodes")) { + info.nodes = token; + model->nodeCount = token->size; + token = aggregate(json, token, "children", &model->childCount); + + } else if (STR_EQ(key, "scene")) { + rootScene = NOM_INT(json, token); + + } else if (STR_EQ(key, "scenes")) { + info.scenes = token; + info.sceneCount = token->size; + scenes = malloc(info.sceneCount * sizeof(gltfScene)); + lovrAssert(scenes, "Out of memory"); + gltfScene* scene = scenes; + for (int i = (token++)->size; i > 0; i--, scene++) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "nodes")) { + scene->nodeCount = token->size; + jsmntok_t* t = token + 1; + scene->node = NOM_INT(json, t); + } + token += NOM_VALUE(json, token); + } + } + + } else if (STR_EQ(key, "skins")) { + info.skins = token; + model->skinCount = token->size; + token = aggregate(json, token, "joints", &model->jointCount); + + } else { + token += NOM_VALUE(json, token); + } + } + + // We only support a single root node, so if there is more than one root node in the scene then + // we create a fake "super root" node and add all the scene's root nodes as its children. + if (info.sceneCount > 0 && scenes[rootScene].nodeCount > 1) { + model->childCount += model->nodeCount; + model->nodeCount++; + } + + // Allocate memory, then revisit all of the tokens that were recorded during the prepass and write + // their data into this memory. + lovrModelDataAllocate(model); + + // Blobs + if (model->blobCount > 0) { + jsmntok_t* token = info.buffers; + Blob** blob = model->blobs; + for (int i = (token++)->size; i > 0; i--, blob++) { + gltfString uri = { 0 }; + size_t size = 0; + + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "byteLength")) { size = NOM_INT(json, token); } + else if (STR_EQ(key, "uri")) { uri = NOM_STR(json, token); } + else { token += NOM_VALUE(json, token); } + } + + if (uri.data) { + lovrAssert(strncmp("data:", uri.data, strlen("data:")), "Base64 URIs aren't supported yet");; + size_t bytesRead; + char filename[1024]; + lovrAssert(uri.length < 1024, "Buffer filename is too long"); + snprintf(filename, 1023, "%s/%.*s", basePath, (int) uri.length, uri.data); + *blob = lovrBlobCreate(io.read(filename, &bytesRead), size, NULL); + lovrAssert((*blob)->data && bytesRead == size, "Unable to read %s", filename); + } else { + lovrAssert(glb, "Buffer is missing URI"); + lovrRetain(source); + *blob = source; + } + } + } + + // Buffers + if (model->bufferCount > 0) { + jsmntok_t* token = info.bufferViews; + ModelBuffer* buffer = model->buffers; + for (int i = (token++)->size; i > 0; i--, buffer++) { + size_t offset = 0; + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "buffer")) { buffer->data = model->blobs[NOM_INT(json, token)]->data; } + else if (STR_EQ(key, "byteOffset")) { offset = NOM_INT(json, token); } + else if (STR_EQ(key, "byteLength")) { buffer->size = NOM_INT(json, token); } + else if (STR_EQ(key, "byteStride")) { buffer->stride = NOM_INT(json, token); } + else { token += NOM_VALUE(json, token); } + } + + // If this is the glb binary data, increment the offset to account for the file header + if (buffer->data && buffer->data == source->data && glb) { + offset += binOffset; + } + + buffer->data = (char*) buffer->data + offset; + } + } + + // Attributes + if (model->attributeCount > 0) { + jsmntok_t* token = info.attributes; + ModelAttribute* attribute = model->attributes; + for (int i = (token++)->size; i > 0; i--, attribute++) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "bufferView")) { attribute->buffer = NOM_INT(json, token); } + else if (STR_EQ(key, "count")) { attribute->count = NOM_INT(json, token); } + else if (STR_EQ(key, "byteOffset")) { attribute->offset = NOM_INT(json, token); } + else if (STR_EQ(key, "normalized")) { attribute->normalized = NOM_BOOL(json, token); } + else if (STR_EQ(key, "componentType")) { + switch (NOM_INT(json, token)) { + case 5120: attribute->type = I8; break; + case 5121: attribute->type = U8; break; + case 5122: attribute->type = I16; break; + case 5123: attribute->type = U16; break; + case 5125: attribute->type = U32; break; + case 5126: attribute->type = F32; break; + default: break; + } + } else if (STR_EQ(key, "type")) { + gltfString type = NOM_STR(json, token); + if (STR_EQ(type, "SCALAR")) { + attribute->components = 1; + } else if (type.length == 4) { + attribute->components = type.data[3] - '0'; + attribute->matrix = type.data[0] == 'M'; + } + } else if (STR_EQ(key, "min") && token->size <= 4) { + int count = (token++)->size; + attribute->hasMin = true; + for (int j = 0; j < count; j++) { + attribute->min[j] = NOM_FLOAT(json, token); + } + } else if (STR_EQ(key, "max") && token->size <= 4) { + int count = (token++)->size; + attribute->hasMax = true; + for (int j = 0; j < count; j++) { + attribute->max[j] = NOM_FLOAT(json, token); + } + } else { + token += NOM_VALUE(json, token); + } + } + } + } + + // Animations + if (model->animationCount > 0) { + int channelIndex = 0; + int baseSampler = 0; + jsmntok_t* token = info.animations; + ModelAnimation* animation = model->animations; + for (int i = (token++)->size; i > 0; i--, animation++) { + int samplerCount = 0; + animation->name = NULL; + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "channels")) { + animation->channelCount = (token++)->size; + animation->channels = model->channels + channelIndex; + channelIndex += animation->channelCount; + for (int j = 0; j < animation->channelCount; j++) { + ModelAnimationChannel* channel = &animation->channels[j]; + ModelAttribute* times = NULL; + ModelAttribute* data = NULL; + + for (int kk = (token++)->size; kk > 0; kk--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "sampler")) { + gltfAnimationSampler* sampler = animationSamplers + baseSampler + NOM_INT(json, token); + times = &model->attributes[sampler->input]; + data = &model->attributes[sampler->output]; + channel->smoothing = sampler->smoothing; + channel->keyframeCount = times->count; + } else if (STR_EQ(key, "target")) { + for (int kkk = (token++)->size; kkk > 0; kkk--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "node")) { channel->nodeIndex = NOM_INT(json, token); } + else if (STR_EQ(key, "path")) { + gltfString property = NOM_STR(json, token); + if (STR_EQ(property, "translation")) { channel->property = PROP_TRANSLATION; } + else if (STR_EQ(property, "rotation")) { channel->property = PROP_ROTATION; } + else if (STR_EQ(property, "scale")) { channel->property = PROP_SCALE; } + else { lovrThrow("Unknown animation channel property"); } + } else { + token += NOM_VALUE(json, token); + } + } + } else { + token += NOM_VALUE(json, token); + } + } + + lovrAssert(times, "Missing keyframe times"); + lovrAssert(data, "Missing keyframe data"); + + ModelBuffer* buffer; + buffer = &model->buffers[times->buffer]; + lovrAssert(times->type == F32 && (buffer->stride == 0 || buffer->stride == sizeof(float)), "Keyframe times must be tightly-packed floats"); + channel->times = (float*) (buffer->data + times->offset); + + buffer = &model->buffers[data->buffer]; + uint8_t components = data->components; + lovrAssert(data->type == F32 && (buffer->stride == 0 || buffer->stride == sizeof(float) * components), "Keyframe data must be tightly-packed floats"); + channel->data = (float*) (buffer->data + data->offset); + + animation->duration = MAX(animation->duration, channel->times[channel->keyframeCount - 1]); + } + } else if (STR_EQ(key, "samplers")) { + samplerCount = token->size; + token += NOM_VALUE(json, token); + } else if (STR_EQ(key, "name")) { + gltfString name = NOM_STR(json, token); + memcpy(model->chars, name.data, name.length); + model->chars[name.length] = '\0'; + animation->name = model->chars; + model->chars += name.length; + } else { + token += NOM_VALUE(json, token); + } + } + baseSampler += samplerCount; + } + } + + // Images + if (model->imageCount > 0) { + jsmntok_t* token = info.images; + TextureData** image = model->images; + for (int i = (token++)->size; i > 0; i--, image++) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "bufferView")) { + ModelBuffer* buffer = &model->buffers[NOM_INT(json, token)]; + Blob* blob = lovrBlobCreate(buffer->data, buffer->size, NULL); + *image = lovrTextureDataCreateFromBlob(blob, false); + blob->data = NULL; // FIXME + lovrRelease(blob); + } else if (STR_EQ(key, "uri")) { + size_t size = 0; + char filename[1024]; + gltfString uri = NOM_STR(json, token); + lovrAssert(strncmp("data:", uri.data, strlen("data:")), "Base64 URIs aren't supported yet"); + snprintf(filename, 1024, "%s/%.*s%c", basePath, (int) uri.length, uri.data, 0); + void* data = io.read(filename, &size); + lovrAssert(data && size > 0, "Unable to read image from '%s'", filename); + Blob* blob = lovrBlobCreate(data, size, NULL); + *image = lovrTextureDataCreateFromBlob(blob, false); + lovrRelease(blob); + } else { + token += NOM_VALUE(json, token); + } + } + } + } + + // Textures + if (model->textureCount > 0) { + jsmntok_t* token = info.textures; + ModelTexture* texture = model->textures; + for (int i = (token++)->size; i > 0; i--, texture++) { + texture->filter.mode = FILTER_TRILINEAR; + texture->wrap.s = texture->wrap.t = WRAP_REPEAT; + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "source")) { + texture->imageIndex = NOM_INT(json, token); + } else if (STR_EQ(key, "sampler")) { + gltfSampler* sampler = &samplers[NOM_INT(json, token)]; + texture->filter = sampler->filter; + texture->wrap = sampler->wrap; + } + else { token += NOM_VALUE(json, token); } + } + } + } + + // Materials + if (model->materialCount > 0) { + jsmntok_t* token = info.materials; + ModelMaterial* material = model->materials; + for (int i = (token++)->size; i > 0; i--, material++) { + material->scalars[SCALAR_METALNESS] = 1.f; + material->scalars[SCALAR_ROUGHNESS] = 1.f; + material->colors[COLOR_DIFFUSE] = (Color) { 1.f, 1.f, 1.f, 1.f }; + material->colors[COLOR_EMISSIVE] = (Color) { 0.f, 0.f, 0.f, 0.f }; + memset(material->textures, 0xff, MAX_MATERIAL_TEXTURES * sizeof(int)); + + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "pbrMetallicRoughness")) { + for (int j = (token++)->size; j > 0; j--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "baseColorFactor")) { + token++; // Enter array + material->colors[COLOR_DIFFUSE].r = NOM_FLOAT(json, token); + material->colors[COLOR_DIFFUSE].g = NOM_FLOAT(json, token); + material->colors[COLOR_DIFFUSE].b = NOM_FLOAT(json, token); + material->colors[COLOR_DIFFUSE].a = NOM_FLOAT(json, token); + } else if (STR_EQ(key, "baseColorTexture")) { + token = parseTextureInfo(json, token, &material->textures[TEXTURE_DIFFUSE]); + } else if (STR_EQ(key, "metallicFactor")) { + material->scalars[SCALAR_METALNESS] = NOM_FLOAT(json, token); + } else if (STR_EQ(key, "roughnessFactor")) { + material->scalars[SCALAR_ROUGHNESS] = NOM_FLOAT(json, token); + } else if (STR_EQ(key, "metallicRoughnessTexture")) { + token = parseTextureInfo(json, token, &material->textures[TEXTURE_METALNESS]); + material->textures[TEXTURE_ROUGHNESS] = material->textures[TEXTURE_METALNESS]; + } else { + token += NOM_VALUE(json, token); + } + } + } else if (STR_EQ(key, "normalTexture")) { + token = parseTextureInfo(json, token, &material->textures[TEXTURE_NORMAL]); + } else if (STR_EQ(key, "occlusionTexture")) { + token = parseTextureInfo(json, token, &material->textures[TEXTURE_OCCLUSION]); + } else if (STR_EQ(key, "emissiveTexture")) { + token = parseTextureInfo(json, token, &material->textures[TEXTURE_EMISSIVE]); + } else if (STR_EQ(key, "emissiveFactor")) { + token++; // Enter array + material->colors[COLOR_EMISSIVE].r = NOM_FLOAT(json, token); + material->colors[COLOR_EMISSIVE].g = NOM_FLOAT(json, token); + material->colors[COLOR_EMISSIVE].b = NOM_FLOAT(json, token); + } else { + token += NOM_VALUE(json, token); + } + } + } + } + + // Primitives + if (model->primitiveCount > 0) { + int primitiveIndex = 0; + jsmntok_t* token = info.meshes; + ModelPrimitive* primitive = model->primitives; + for (int i = (token++)->size; i > 0; i--) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "primitives")) { + for (uint32_t j = (token++)->size; j > 0; j--, primitive++) { + primitive->mode = DRAW_TRIANGLES; + primitive->material = -1; + + for (int kk = (token++)->size; kk > 0; kk--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "material")) { + primitive->material = NOM_INT(json, token); + } else if (STR_EQ(key, "indices")) { + primitive->indices = &model->attributes[NOM_INT(json, token)]; + lovrAssert(primitive->indices->type != U8, "Unsigned byte indices are not supported (must be unsigned shorts or unsigned ints)"); + } else if (STR_EQ(key, "mode")) { + switch (NOM_INT(json, token)) { + case 0: primitive->mode = DRAW_POINTS; break; + case 1: primitive->mode = DRAW_LINES; break; + case 2: primitive->mode = DRAW_LINE_LOOP; break; + case 3: primitive->mode = DRAW_LINE_STRIP; break; + case 4: primitive->mode = DRAW_TRIANGLES; break; + case 5: primitive->mode = DRAW_TRIANGLE_STRIP; break; + case 6: primitive->mode = DRAW_TRIANGLE_FAN; break; + default: lovrThrow("Unknown primitive mode"); + } + } else if (STR_EQ(key, "attributes")) { + int attributeCount = (token++)->size; + for (int a = 0; a < attributeCount; a++) { + DefaultAttribute attributeType = -1; + gltfString name = NOM_STR(json, token); + int attributeIndex = NOM_INT(json, token); + if (STR_EQ(name, "POSITION")) { attributeType = ATTR_POSITION; } + else if (STR_EQ(name, "NORMAL")) { attributeType = ATTR_NORMAL; } + else if (STR_EQ(name, "TEXCOORD_0")) { attributeType = ATTR_TEXCOORD; } + else if (STR_EQ(name, "COLOR_0")) { attributeType = ATTR_COLOR; } + else if (STR_EQ(name, "TANGENT")) { attributeType = ATTR_TANGENT; } + else if (STR_EQ(name, "JOINTS_0")) { attributeType = ATTR_BONES; } + else if (STR_EQ(name, "WEIGHTS_0")) { attributeType = ATTR_WEIGHTS; } + if (attributeType >= 0) { + primitive->attributes[attributeType] = &model->attributes[attributeIndex]; + } + } + } else { + token += NOM_VALUE(json, token); + } + } + } + } else { + token += NOM_VALUE(json, token); + } + } + } + } + + // Nodes + int childIndex = 0; + if (model->nodeCount > 0) { + jsmntok_t* token = info.nodes; + ModelNode* node = model->nodes; + for (int i = (token++)->size; i > 0; i--, node++) { + float translation[3] = { 0, 0, 0 }; + float rotation[4] = { 0, 0, 0, 0 }; + float scale[3] = { 1, 1, 1 }; + bool matrix = false; + node->primitiveCount = 0; + node->skin = -1; + + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "mesh")) { + gltfMesh* mesh = &meshes[NOM_INT(json, token)]; + node->primitiveIndex = mesh->primitiveIndex; + node->primitiveCount = mesh->primitiveCount; + } else if (STR_EQ(key, "skin")) { + node->skin = NOM_INT(json, token); + } else if (STR_EQ(key, "children")) { + node->children = &model->children[childIndex]; + node->childCount = (token++)->size; + for (uint32_t j = 0; j < node->childCount; j++) { + model->children[childIndex++] = NOM_INT(json, token); + } + } else if (STR_EQ(key, "matrix")) { + lovrAssert((token++)->size == 16, "Node matrix needs 16 elements"); + matrix = true; + for (int j = 0; j < 16; j++) { + node->transform[j] = NOM_FLOAT(json, token); + } + } else if (STR_EQ(key, "translation")) { + lovrAssert((token++)->size == 3, "Node translation needs 3 elements"); + translation[0] = NOM_FLOAT(json, token); + translation[1] = NOM_FLOAT(json, token); + translation[2] = NOM_FLOAT(json, token); + } else if (STR_EQ(key, "rotation")) { + lovrAssert((token++)->size == 4, "Node rotation needs 4 elements"); + rotation[0] = NOM_FLOAT(json, token); + rotation[1] = NOM_FLOAT(json, token); + rotation[2] = NOM_FLOAT(json, token); + rotation[3] = NOM_FLOAT(json, token); + } else if (STR_EQ(key, "scale")) { + lovrAssert((token++)->size == 3, "Node scale needs 3 elements"); + scale[0] = NOM_FLOAT(json, token); + scale[1] = NOM_FLOAT(json, token); + scale[2] = NOM_FLOAT(json, token); + } else { + token += NOM_VALUE(json, token); + } + } + + // Fix it in post + if (!matrix) { + mat4_identity(node->transform); + mat4_translate(node->transform, translation[0], translation[1], translation[2]); + mat4_rotateQuat(node->transform, rotation); + mat4_scale(node->transform, scale[0], scale[1], scale[2]); + } + } + } + + // Skins + if (model->skinCount > 0) { + int jointIndex = 0; + jsmntok_t* token = info.skins; + ModelSkin* skin = model->skins; + for (int i = (token++)->size; i > 0; i--, skin++) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "inverseBindMatrices")) { + ModelAttribute* attribute = &model->attributes[NOM_INT(json, token)]; + ModelBuffer* buffer = &model->buffers[attribute->buffer]; + skin->inverseBindMatrices = (float*) ((uint8_t*) buffer->data + attribute->offset); + } else if (STR_EQ(key, "joints")) { + skin->joints = &model->joints[jointIndex]; + skin->jointCount = (token++)->size; + for (uint32_t j = 0; j < skin->jointCount; j++) { + model->joints[jointIndex++] = NOM_INT(json, token); + } + } else { + token += NOM_VALUE(json, token); + } + } + } + } + + // Scenes + if (info.sceneCount == 0) { + model->rootNode = 0; + } else if (scenes[rootScene].nodeCount > 1) { + model->rootNode = model->nodeCount - 1; + ModelNode* lastNode = &model->nodes[model->rootNode]; + lastNode->childCount = scenes[rootScene].nodeCount; + lastNode->children = &model->children[childIndex]; + mat4_identity(lastNode->transform); + lastNode->primitiveCount = 0; + lastNode->skin = -1; + + jsmntok_t* token = info.scenes; + int sceneCount = (token++)->size; + for (int i = 0; i < sceneCount; i++) { + if (i == rootScene) { + for (int k = (token++)->size; k > 0; k--) { + gltfString key = NOM_STR(json, token); + if (STR_EQ(key, "nodes")) { + for (int j = (token++)->size; j > 0; j--) { + lastNode->children[lastNode->childCount - j] = NOM_INT(json, token); + } + } else { + token += NOM_VALUE(json, token); + } + } + } else { + token += NOM_VALUE(json, token); + } + } + } else { + model->rootNode = scenes[rootScene].node; + } + + free(animationSamplers); + free(meshes); + free(samplers); + free(scenes); + free(heapTokens); + return model; +} diff --git a/src/data/modelData_obj.c b/src/data/modelData_obj.c new file mode 100644 index 00000000..ce8a10b0 --- /dev/null +++ b/src/data/modelData_obj.c @@ -0,0 +1,165 @@ +#include "data/modelData.h" +#include "lib/math.h" +#include + +#define STARTS_WITH(a, b) !strncmp(a, b, strlen(b)) + +ModelData* lovrModelDataInitObj(ModelData* model, Blob* source, ModelDataIO io) { + char* data = (char*) source->data; + size_t length = source->size; + + vec_float_t vertexBuffer; + vec_int_t indexBuffer; + map_int_t vertexMap; + vec_float_t vertices; + vec_float_t normals; + vec_float_t uvs; + + vec_init(&vertexBuffer); + vec_init(&indexBuffer); + map_init(&vertexMap); + vec_init(&vertices); + vec_init(&normals); + vec_init(&uvs); + + while (length > 0) { + int lineLength = 0; + + if (STARTS_WITH(data, "v ")) { + float x, y, z; + int count = sscanf(data + 2, "%f %f %f\n%n", &x, &y, &z, &lineLength); + lovrAssert(count == 3, "Bad OBJ: Expected 3 coordinates for vertex position"); + vec_pusharr(&vertices, ((float[3]) { x, y, z }), 3); + } else if (STARTS_WITH(data, "vn ")) { + float x, y, z; + int count = sscanf(data + 3, "%f %f %f\n%n", &x, &y, &z, &lineLength); + lovrAssert(count == 3, "Bad OBJ: Expected 3 coordinates for vertex normal"); + vec_pusharr(&normals, ((float[3]) { x, y, z }), 3); + } else if (STARTS_WITH(data, "vt ")) { + float u, v; + int count = sscanf(data + 3, "%f %f\n%n", &u, &v, &lineLength); + lovrAssert(count == 2, "Bad OBJ: Expected 2 coordinates for texture coordinate"); + vec_pusharr(&uvs, ((float[2]) { u, v }), 2); + } else if (STARTS_WITH(data, "f ")) { + char* s = data + 2; + for (int i = 0; i < 3; i++) { + char terminator = i == 2 ? '\n' : ' '; + char* space = strchr(s, terminator); + if (space) { + *space = '\0'; // I'll be back + int* index = map_get(&vertexMap, s); + if (index) { + vec_push(&indexBuffer, *index); + } else { + int v, vt, vn; + int newIndex = vertexBuffer.length / 8; + vec_push(&indexBuffer, newIndex); + map_set(&vertexMap, s, newIndex); + + // Can be improved + if (sscanf(s, "%d/%d/%d", &v, &vt, &vn) == 3) { + vec_pusharr(&vertexBuffer, vertices.data + 3 * (v - 1), 3); + vec_pusharr(&vertexBuffer, normals.data + 3 * (vn - 1), 3); + vec_pusharr(&vertexBuffer, uvs.data + 2 * (vt - 1), 2); + } else if (sscanf(s, "%d//%d", &v, &vn) == 2) { + vec_pusharr(&vertexBuffer, vertices.data + 3 * (v - 1), 3); + vec_pusharr(&vertexBuffer, normals.data + 3 * (vn - 1), 3); + vec_pusharr(&vertexBuffer, ((float[2]) { 0 }), 2); + } else if (sscanf(s, "%d", &v) == 1) { + vec_pusharr(&vertexBuffer, vertices.data + 3 * (v - 1), 3); + vec_pusharr(&vertexBuffer, ((float[5]) { 0 }), 5); + } else { + lovrThrow("Bad OBJ: Unknown face format"); + } + } + *space = terminator; + s = space + 1; + } + } + lineLength = s - data; + } else { + char* newline = memchr(data, '\n', length); + lineLength = newline - data + 1; + } + + data += lineLength; + length -= lineLength; + } + + model->blobCount = 2; + model->bufferCount = 2; + model->attributeCount = 4; + model->primitiveCount = 1; + model->nodeCount = 1; + lovrModelDataAllocate(model); + + model->blobs[0] = lovrBlobCreate(vertexBuffer.data, vertexBuffer.length * sizeof(float), "obj vertex data"); + model->blobs[1] = lovrBlobCreate(indexBuffer.data, indexBuffer.length * sizeof(int), "obj index data"); + + model->buffers[0] = (ModelBuffer) { + .data = model->blobs[0]->data, + .size = model->blobs[0]->size, + .stride = 8 * sizeof(float) + }; + + model->buffers[1] = (ModelBuffer) { + .data = model->blobs[1]->data, + .size = model->blobs[1]->size, + .stride = sizeof(int) + }; + + model->attributes[0] = (ModelAttribute) { + .buffer = 0, + .offset = 0, + .count = vertexBuffer.length / 8, + .type = F32, + .components = 3 + }; + + model->attributes[1] = (ModelAttribute) { + .buffer = 0, + .offset = 3 * sizeof(float), + .count = vertexBuffer.length / 8, + .type = F32, + .components = 3 + }; + + model->attributes[2] = (ModelAttribute) { + .buffer = 0, + .offset = 6 * sizeof(float), + .count = vertexBuffer.length / 8, + .type = F32, + .components = 2 + }; + + model->attributes[3] = (ModelAttribute) { + .buffer = 1, + .offset = 0, + .count = indexBuffer.length, + .type = U32, + .components = 1 + }; + + model->primitives[0] = (ModelPrimitive) { + .mode = DRAW_TRIANGLES, + .attributes = { + [ATTR_POSITION] = &model->attributes[0], + [ATTR_NORMAL] = &model->attributes[1], + [ATTR_TEXCOORD] = &model->attributes[2] + }, + .indices = &model->attributes[3], + .material = -1 + }; + + model->nodes[0] = (ModelNode) { + .transform = MAT4_IDENTITY, + .primitiveIndex = 0, + .primitiveCount = 1 + }; + + map_deinit(&vertexMap); + vec_deinit(&vertices); + vec_deinit(&normals); + vec_deinit(&uvs); + return model; +} diff --git a/src/headset/openvr.c b/src/headset/openvr.c index 7e1c48a3..92685679 100644 --- a/src/headset/openvr.c +++ b/src/headset/openvr.c @@ -403,9 +403,14 @@ static ModelData* openvrControllerNewModelData(Controller* controller) { ModelData* model = lovrAlloc(ModelData); size_t vertexSize = sizeof(RenderModel_Vertex_t); - // Buffers model->bufferCount = 2; - model->buffers = calloc(model->bufferCount, sizeof(ModelBuffer)); + model->attributeCount = 4; + model->imageCount = 1; + model->textureCount = 1; + model->materialCount = 1; + model->primitiveCount = 1; + model->nodeCount = 1; + lovrModelDataAllocate(model); model->buffers[0] = (ModelBuffer) { .data = (char*) vrModel->rVertexData, @@ -419,10 +424,6 @@ static ModelData* openvrControllerNewModelData(Controller* controller) { .stride = sizeof(uint16_t) }; - // Attributes - model->attributeCount = 4; - model->attributes = calloc(model->attributeCount, sizeof(ModelAttribute)); - model->attributes[0] = (ModelAttribute) { .buffer = 0, .offset = offsetof(RenderModel_Vertex_t, vPosition), @@ -455,32 +456,20 @@ static ModelData* openvrControllerNewModelData(Controller* controller) { .components = 1 }; - // Images RenderModel_TextureMap_t* vrTexture = state.deviceTextures[id]; - model->imageCount = 1; - model->images = calloc(model->imageCount, sizeof(TextureData**)); model->images[0] = lovrTextureDataCreate(vrTexture->unWidth, vrTexture->unHeight, 0, FORMAT_RGBA); memcpy(model->images[0]->blob.data, vrTexture->rubTextureMapData, vrTexture->unWidth * vrTexture->unHeight * 4); - // Textures - model->textureCount = 1; - model->textures = calloc(model->textureCount, sizeof(ModelTexture)); model->textures[0] = (ModelTexture) { .imageIndex = 0, .filter = lovrGraphicsGetDefaultFilter() }; - // Material - model->materialCount = 1; - model->materials = calloc(model->materialCount, sizeof(ModelMaterial)); model->materials[0] = (ModelMaterial) { .colors[COLOR_DIFFUSE] = { 1.f, 1.f, 1.f, 1.f }, .textures[TEXTURE_DIFFUSE] = 0 }; - // Primitives - model->primitiveCount = 1; - model->primitives = calloc(model->primitiveCount, sizeof(ModelPrimitive)); model->primitives[0] = (ModelPrimitive) { .mode = DRAW_TRIANGLES, .attributes = { @@ -492,17 +481,12 @@ static ModelData* openvrControllerNewModelData(Controller* controller) { .material = 0 }; - // Nodes - model->nodeCount = 1; - model->nodes = calloc(model->nodeCount, sizeof(ModelNode)); model->nodes[0] = (ModelNode) { .transform = MAT4_IDENTITY, .primitiveIndex = 0, .primitiveCount = 1 }; - model->rootNode = 0; - return model; } diff --git a/src/lib/jsmn/jsmn.h b/src/lib/jsmn/jsmn.h index 7625b783..11c0c751 100644 --- a/src/lib/jsmn/jsmn.h +++ b/src/lib/jsmn/jsmn.h @@ -2,6 +2,7 @@ #define __JSMN_H_ #define JSMN_PARENT_LINKS +#define JSMN_STRICT #include