From 6b323e3476ed090fa64c7e1c4801d2d32b68fc0f Mon Sep 17 00:00:00 2001 From: bjorn Date: Tue, 25 Sep 2018 17:10:09 -0700 Subject: [PATCH] Start parsing glTF; --- CMakeLists.txt | 1 + src/api/data.c | 7 +- src/api/data.h | 10 +- src/api/graphics.c | 45 +- src/api/types/mesh.c | 10 +- src/api/types/model.c | 69 --- src/api/types/modelData.c | 254 ---------- src/api/types/vertexData.c | 68 +-- src/data/modelData.c | 930 +++++++++++++++---------------------- src/data/modelData.h | 132 ++++-- src/data/vertexData.c | 18 +- src/data/vertexData.h | 19 +- src/graphics/animator.c | 10 +- src/graphics/graphics.c | 10 +- src/graphics/mesh.h | 13 +- src/graphics/model.c | 261 ++--------- src/graphics/model.h | 26 +- src/graphics/opengl.c | 34 +- src/headset/openvr.c | 73 +-- src/lib/jsmn/LICENSE | 19 + src/lib/jsmn/jsmn.c | 313 +++++++++++++ src/lib/jsmn/jsmn.h | 76 +++ src/resources/shaders.c | 10 + src/resources/shaders.h | 1 + 24 files changed, 1075 insertions(+), 1334 deletions(-) create mode 100644 src/lib/jsmn/LICENSE create mode 100644 src/lib/jsmn/jsmn.c create mode 100644 src/lib/jsmn/jsmn.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e1a8728a..39675ec7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -396,6 +396,7 @@ if(LOVR_ENABLE_DATA) src/lib/stb/stb_image_write.c src/lib/stb/stb_truetype.c src/lib/stb/stb_vorbis.c + src/lib/jsmn/jsmn.c ) if (LOVR_USE_ASSIMP) diff --git a/src/api/data.c b/src/api/data.c index fd03e8a4..a88f3bd3 100644 --- a/src/api/data.c +++ b/src/api/data.c @@ -6,6 +6,7 @@ #include "data/soundData.h" #include "data/textureData.h" #include "data/vertexData.h" +#include "filesystem/filesystem.h" static int l_lovrDataNewBlob(lua_State* L) { size_t size; @@ -44,9 +45,11 @@ static int l_lovrDataNewAudioStream(lua_State* L) { return 1; } +static ModelDataIO modelDataIO = { lovrFilesystemRead }; + static int l_lovrDataNewModelData(lua_State* L) { Blob* blob = luax_readblob(L, 1, "Model"); - ModelData* modelData = lovrModelDataCreate(blob); + ModelData* modelData = lovrModelDataCreate(blob, modelDataIO); luax_pushobject(L, modelData); lovrRelease(blob); lovrRelease(modelData); @@ -147,7 +150,7 @@ static int l_lovrDataNewVertexData(lua_State* L) { VertexData* vertexData = lovrVertexDataCreate(count, hasFormat ? &format : NULL); if (dataIndex) { - luax_loadvertices(L, dataIndex, &vertexData->format, (VertexPointer) { .raw = vertexData->blob.data }); + luax_loadvertices(L, dataIndex, &vertexData->format, (AttributePointer) { .raw = vertexData->blob.data }); } luax_pushobject(L, vertexData); lovrRelease(vertexData); diff --git a/src/api/data.h b/src/api/data.h index 9fb142e9..2adf82ce 100644 --- a/src/api/data.h +++ b/src/api/data.h @@ -2,11 +2,11 @@ #include "data/vertexData.h" #include -int luax_loadvertices(lua_State* L, int index, VertexFormat* format, VertexPointer vertices); +int luax_loadvertices(lua_State* L, int index, VertexFormat* format, AttributePointer vertices); bool luax_checkvertexformat(lua_State* L, int index, VertexFormat* format); int luax_pushvertexformat(lua_State* L, VertexFormat* format); -int luax_pushvertexattribute(lua_State* L, VertexPointer* vertex, Attribute attribute); -int luax_pushvertex(lua_State* L, VertexPointer* vertex, VertexFormat* format); -void luax_setvertexattribute(lua_State* L, int index, VertexPointer* vertex, Attribute attribute); -void luax_setvertex(lua_State* L, int index, VertexPointer* vertex, VertexFormat* format); +int luax_pushvertexattribute(lua_State* L, AttributePointer* vertex, Attribute attribute); +int luax_pushvertex(lua_State* L, AttributePointer* vertex, VertexFormat* format); +void luax_setvertexattribute(lua_State* L, int index, AttributePointer* vertex, Attribute attribute); +void luax_setvertex(lua_State* L, int index, AttributePointer* vertex, VertexFormat* format); Blob* luax_readblob(lua_State* L, int index, const char* debug); diff --git a/src/api/graphics.c b/src/api/graphics.c index 900a34f0..8c695002 100644 --- a/src/api/graphics.c +++ b/src/api/graphics.c @@ -26,9 +26,13 @@ const char* ArcModes[] = { }; const char* AttributeTypes[] = { - [ATTR_FLOAT] = "float", - [ATTR_BYTE] = "byte", - [ATTR_INT] = "int", + [I8] = "i8", + [U8] = "u8", + [I16] = "i16", + [U16] = "u16", + [I32] = "i32", + [U32] = "u32", + [F32] = "f32", NULL }; @@ -899,7 +903,7 @@ static int l_lovrGraphicsCompute(lua_State* L) { static int l_lovrGraphicsNewAnimator(lua_State* L) { Model* model = luax_checktype(L, 1, Model); - Animator* animator = lovrAnimatorCreate(model->modelData); + Animator* animator = lovrAnimatorCreate(model->data); luax_pushobject(L, animator); lovrRelease(animator); return 1; @@ -1154,9 +1158,9 @@ static int l_lovrGraphicsNewMesh(lua_State* L) { } if (!hasFormat) { - vertexFormatAppend(&format, "lovrPosition", ATTR_FLOAT, 3); - vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3); - vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2); + vertexFormatAppend(&format, "lovrPosition", F32, 3); + vertexFormatAppend(&format, "lovrNormal", F32, 3); + vertexFormatAppend(&format, "lovrTexCoord", F32, 2); } DrawMode mode = luaL_checkoption(L, drawModeIndex, "fan", DrawModes); @@ -1168,7 +1172,7 @@ static int l_lovrGraphicsNewMesh(lua_State* L) { lovrMeshAttachAttribute(mesh, "lovrDrawID", &(MeshAttribute) { .buffer = lovrGraphicsGetIdentityBuffer(), - .type = ATTR_BYTE, + .type = U8, .components = 1, .divisor = 1, .integer = true, @@ -1176,8 +1180,8 @@ static int l_lovrGraphicsNewMesh(lua_State* L) { }); if (dataIndex) { - VertexPointer vertices = { .raw = lovrBufferMap(vertexBuffer, 0) }; - luax_loadvertices(L, dataIndex, &format, vertices); + AttributePointer vertices = { .raw = lovrBufferMap(vertexBuffer, 0) }; + luax_loadvertices(L, dataIndex, lovrMeshGetVertexFormat(mesh), vertices); } else if (vertexData) { void* vertices = lovrBufferMap(vertexBuffer, 0); memcpy(vertices, vertexData->blob.data, vertexData->count * vertexData->format.stride); @@ -1196,30 +1200,13 @@ static int l_lovrGraphicsNewModel(lua_State* L) { if (!modelData) { Blob* blob = luax_readblob(L, 1, "Model"); - modelData = lovrModelDataCreate(blob); + static ModelDataIO io = { lovrFilesystemRead }; + modelData = lovrModelDataCreate(blob, io); lovrRelease(blob); } Model* model = lovrModelCreate(modelData); - - if (lua_gettop(L) >= 2) { - if (lua_type(L, 2) == LUA_TSTRING) { - Blob* blob = luax_readblob(L, 2, "Texture"); - TextureData* textureData = lovrTextureDataCreateFromBlob(blob, true); - Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, true, 0); - Material* material = lovrMaterialCreate(); - lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture); - lovrModelSetMaterial(model, material); - lovrRelease(blob); - lovrRelease(texture); - lovrRelease(material); - } else { - lovrModelSetMaterial(model, luax_checktype(L, 2, Material)); - } - } - luax_pushobject(L, model); - lovrRelease(modelData); lovrRelease(model); return 1; } diff --git a/src/api/types/mesh.c b/src/api/types/mesh.c index d42f5cfe..24bda127 100644 --- a/src/api/types/mesh.c +++ b/src/api/types/mesh.c @@ -127,7 +127,7 @@ int l_lovrMeshGetVertex(lua_State* L) { Buffer* buffer = lovrMeshGetVertexBuffer(mesh); lovrAssert(lovrBufferIsReadable(buffer), "Mesh:getVertex can only be used if the Mesh was created with the readable flag"); VertexFormat* format = lovrMeshGetVertexFormat(mesh); - VertexPointer vertex = { .raw = lovrBufferMap(buffer, index * format->stride) }; + AttributePointer vertex = { .raw = lovrBufferMap(buffer, index * format->stride) }; return luax_pushvertex(L, &vertex, format); } @@ -137,7 +137,7 @@ int l_lovrMeshSetVertex(lua_State* L) { lovrAssert(index >= 0 && index < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex index: %d", index + 1); Buffer* buffer = lovrMeshGetVertexBuffer(mesh); VertexFormat* format = lovrMeshGetVertexFormat(mesh); - VertexPointer vertex = { .raw = lovrBufferMap(buffer, index * format->stride) }; + AttributePointer vertex = { .raw = lovrBufferMap(buffer, index * format->stride) }; luax_setvertex(L, 3, &vertex, format); lovrBufferMarkRange(buffer, index * format->stride, (index + 1) * format->stride); return 0; @@ -153,7 +153,7 @@ int l_lovrMeshGetVertexAttribute(lua_State* L) { lovrAssert(vertexIndex >= 0 && vertexIndex < lovrMeshGetVertexCount(mesh), "Invalid mesh vertex: %d", vertexIndex + 1); lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1); Attribute attribute = format->attributes[attributeIndex]; - VertexPointer vertex = { .raw = lovrBufferMap(buffer, vertexIndex * format->stride + attribute.offset) }; + AttributePointer vertex = { .raw = lovrBufferMap(buffer, vertexIndex * format->stride + attribute.offset) }; return luax_pushvertexattribute(L, &vertex, attribute); } @@ -166,7 +166,7 @@ int l_lovrMeshSetVertexAttribute(lua_State* L) { lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid mesh attribute: %d", attributeIndex + 1); Attribute attribute = format->attributes[attributeIndex]; Buffer* buffer = lovrMeshGetVertexBuffer(mesh); - VertexPointer vertex = { .raw = lovrBufferMap(buffer, vertexIndex * format->stride + attribute.offset) }; + AttributePointer vertex = { .raw = lovrBufferMap(buffer, vertexIndex * format->stride + attribute.offset) }; luax_setvertexattribute(L, 4, &vertex, attribute); lovrBufferMarkRange(buffer, vertexIndex * format->stride + attribute.offset, vertexIndex * format->stride + attribute.offset + attribute.size); return 0; @@ -194,7 +194,7 @@ int l_lovrMeshSetVertices(lua_State* L) { lovrAssert(count <= sourceSize, "Cannot set %d vertices on Mesh: source only has %d vertices", count, sourceSize); Buffer* buffer = lovrMeshGetVertexBuffer(mesh); - VertexPointer vertices = { .raw = lovrBufferMap(buffer, start * format->stride) }; + AttributePointer vertices = { .raw = lovrBufferMap(buffer, start * format->stride) }; if (vertexData) { memcpy(vertices.raw, vertexData->blob.data, count * format->stride); diff --git a/src/api/types/model.c b/src/api/types/model.c index fcec6342..b58c9f45 100644 --- a/src/api/types/model.c +++ b/src/api/types/model.c @@ -11,76 +11,7 @@ int l_lovrModelDraw(lua_State* L) { return 0; } -int l_lovrModelGetAABB(lua_State* L) { - Model* model = luax_checktype(L, 1, Model); - const float* aabb = lovrModelGetAABB(model); - for (int i = 0; i < 6; i++) { - lua_pushnumber(L, aabb[i]); - } - return 6; -} - -int l_lovrModelGetAnimator(lua_State* L) { - Model* model = luax_checktype(L, 1, Model); - Animator* animator = lovrModelGetAnimator(model); - luax_pushobject(L, animator); - return 1; -} - -int l_lovrModelSetAnimator(lua_State* L) { - Model* model = luax_checktype(L, 1, Model); - if (lua_isnil(L, 2)) { - lovrModelSetAnimator(model, NULL); - } else { - Animator* animator = luax_checktype(L, 2, Animator); - lovrModelSetAnimator(model, animator); - } - return 0; -} - -int l_lovrModelGetAnimationCount(lua_State* L) { - Model* model = luax_checktype(L, 1, Model); - lua_pushinteger(L, lovrModelGetAnimationCount(model)); - return 1; -} - -int l_lovrModelGetMaterial(lua_State* L) { - Model* model = luax_checktype(L, 1, Model); - Material* material = lovrModelGetMaterial(model); - if (material) { - luax_pushobject(L, material); - } else { - lua_pushnil(L); - } - return 1; -} - -int l_lovrModelSetMaterial(lua_State* L) { - Model* model = luax_checktype(L, 1, Model); - if (lua_isnoneornil(L, 2)) { - lovrModelSetMaterial(model, NULL); - } else { - Material* material = luax_checktype(L, 2, Material); - lovrModelSetMaterial(model, material); - } - return 0; -} - -int l_lovrModelGetMesh(lua_State* L) { - Model* model = luax_checktype(L, 1, Model); - Mesh* mesh = lovrModelGetMesh(model); - luax_pushobject(L, mesh); - return 1; -} - const luaL_Reg lovrModel[] = { { "draw", l_lovrModelDraw }, - { "getAABB", l_lovrModelGetAABB }, - { "getAnimator", l_lovrModelGetAnimator }, - { "setAnimator", l_lovrModelSetAnimator }, - { "getAnimationCount", l_lovrModelGetAnimationCount }, - { "getMaterial", l_lovrModelGetMaterial }, - { "setMaterial", l_lovrModelSetMaterial }, - { "getMesh", l_lovrModelGetMesh }, { NULL, NULL } }; diff --git a/src/api/types/modelData.c b/src/api/types/modelData.c index be3e11de..7eddbf96 100644 --- a/src/api/types/modelData.c +++ b/src/api/types/modelData.c @@ -1,260 +1,6 @@ #include "api.h" #include "data/modelData.h" -#include "lib/math.h" - -int l_lovrModelDataGetVertexData(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - luax_pushobject(L, modelData->vertexData); - return 1; -} - -int l_lovrModelDataGetTriangleCount(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - lua_pushinteger(L, modelData->indexCount / 3); - return 1; -} - -int l_lovrModelDataGetTriangle(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - int index = luaL_checkinteger(L, 2); - if (modelData->indexSize == sizeof(uint16_t)) { - lua_pushinteger(L, modelData->indices.shorts[index]); - lua_pushinteger(L, modelData->indices.shorts[index]); - lua_pushinteger(L, modelData->indices.shorts[index]); - } else { - lua_pushinteger(L, modelData->indices.ints[index]); - lua_pushinteger(L, modelData->indices.ints[index]); - lua_pushinteger(L, modelData->indices.ints[index]); - } - return 3; -} - -int l_lovrModelDataGetNodeCount(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - lua_pushinteger(L, modelData->nodeCount); - return 1; -} - -int l_lovrModelDataGetNodeName(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - int index = luaL_checkint(L, 2) - 1; - lovrAssert(index >= 0 && index < modelData->nodeCount, "Invalid node index: %d", index); - ModelNode* node = &modelData->nodes[index]; - lua_pushstring(L, node->name); - return 1; -} - -static int luax_writenodetransform(lua_State* L, mat4 m, int transformIndex) { - float x, y, z, sx, sy, sz, angle, ax, ay, az; - mat4_getTransform(m, &x, &y, &z, &sx, &sy, &sz, &angle, &ax, &ay, &az); - lua_pushnumber(L, x); - lua_pushnumber(L, y); - lua_pushnumber(L, z); - lua_pushnumber(L, sx); - lua_pushnumber(L, sy); - lua_pushnumber(L, sz); - lua_pushnumber(L, angle); - lua_pushnumber(L, ax); - lua_pushnumber(L, ay); - lua_pushnumber(L, az); - return 10; -} - -int l_lovrModelDataGetLocalNodeTransform(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - int index = luaL_checkint(L, 2) - 1; - lovrAssert(index >= 0 && index < modelData->nodeCount, "Invalid node index: %d", index); - ModelNode* node = &modelData->nodes[index]; - return luax_writenodetransform(L, node->transform, 3); -} - -int l_lovrModelDataGetGlobalNodeTransform(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - int index = luaL_checkint(L, 2) - 1; - lovrAssert(index >= 0 && index < modelData->nodeCount, "Invalid node index: %d", index); - ModelNode* node = &modelData->nodes[index]; - return luax_writenodetransform(L, node->globalTransform, 3); -} - -int l_lovrModelDataGetNodeParent(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - int index = luaL_checkint(L, 2) - 1; - lovrAssert(index >= 0 && index < modelData->nodeCount, "Invalid node index: %d", index); - ModelNode* node = &modelData->nodes[index]; - if (node->parent == -1) { - lua_pushnil(L); - } else { - lua_pushinteger(L, node->parent); - } - return 1; -} - -int l_lovrModelDataGetNodeChildren(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - int index = luaL_checkint(L, 2) - 1; - lovrAssert(index >= 0 && index < modelData->nodeCount, "Invalid node index: %d", index); - ModelNode* node = &modelData->nodes[index]; - - if (lua_istable(L, 3)) { - lua_settop(L, 3); - } else { - lua_settop(L, 2); - lua_createtable(L, node->children.length, 0); - } - - for (int i = 0; i < node->children.length; i++) { - lua_pushinteger(L, node->children.data[i]); - lua_rawseti(L, 3, i + 1); - } - - return 1; -} - -int l_lovrModelDataGetNodeComponentCount(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - int index = luaL_checkint(L, 2) - 1; - lovrAssert(index >= 0 && index < modelData->nodeCount, "Invalid node index: %d", index); - ModelNode* node = &modelData->nodes[index]; - lua_pushinteger(L, node->primitives.length); - return 1; -} - -int l_lovrModelDataGetNodeComponent(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - int nodeIndex = luaL_checkint(L, 2) - 1; - int primitiveIndex = luaL_checkint(L, 3) - 1; - lovrAssert(nodeIndex >= 0 && nodeIndex < modelData->nodeCount, "Invalid node index: %d", nodeIndex + 1); - ModelNode* node = &modelData->nodes[nodeIndex]; - lovrAssert(primitiveIndex >= 0 && primitiveIndex < node->primitives.length, "Invalid component index: %d", primitiveIndex + 1); - ModelPrimitive* primitive = &modelData->primitives[node->primitives.data[primitiveIndex]]; - lua_pushinteger(L, primitive->drawStart); - lua_pushinteger(L, primitive->drawCount); - lua_pushinteger(L, primitive->material); - return 3; -} - -int l_lovrModelDataGetAnimationCount(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - lua_pushinteger(L, modelData->animationCount); - return 1; -} - -int l_lovrModelDataGetMaterialCount(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - lua_pushinteger(L, modelData->materialCount); - return 1; -} - -static ModelMaterial* luax_checkmodelmaterial(lua_State* L, int index) { - ModelData* modelData = luax_checktype(L, index, ModelData); - int materialIndex = luaL_checkint(L, index + 1) - 1; - lovrAssert(materialIndex >= 0 && materialIndex < modelData->materialCount, "Invalid material index: %d", materialIndex + 1); - return &modelData->materials[materialIndex]; -} - -int l_lovrModelDataGetMetalness(lua_State* L) { - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - lua_pushnumber(L, material->metalness); - return 1; -} - -int l_lovrModelDataGetRoughness(lua_State* L) { - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - lua_pushnumber(L, material->roughness); - return 1; -} - -int l_lovrModelDataGetDiffuseColor(lua_State* L) { - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - Color color = material->diffuseColor; - lua_pushnumber(L, color.r); - lua_pushnumber(L, color.g); - lua_pushnumber(L, color.b); - lua_pushnumber(L, color.a); - return 4; -} - -int l_lovrModelDataGetEmissiveColor(lua_State* L) { - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - Color color = material->emissiveColor; - lua_pushnumber(L, color.r); - lua_pushnumber(L, color.g); - lua_pushnumber(L, color.b); - lua_pushnumber(L, color.a); - return 4; -} - -int l_lovrModelDataGetDiffuseTexture(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - TextureData* textureData = modelData->textures.data[material->diffuseTexture]; - luax_pushobject(L, textureData); - return 1; -} - -int l_lovrModelDataGetEmissiveTexture(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - TextureData* textureData = modelData->textures.data[material->emissiveTexture]; - luax_pushobject(L, textureData); - return 1; -} - -int l_lovrModelDataGetMetalnessTexture(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - TextureData* textureData = modelData->textures.data[material->metalnessTexture]; - luax_pushobject(L, textureData); - return 1; -} - -int l_lovrModelDataGetRoughnessTexture(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - TextureData* textureData = modelData->textures.data[material->roughnessTexture]; - luax_pushobject(L, textureData); - return 1; -} - -int l_lovrModelDataGetOcclusionTexture(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - TextureData* textureData = modelData->textures.data[material->occlusionTexture]; - luax_pushobject(L, textureData); - return 1; -} - -int l_lovrModelDataGetNormalTexture(lua_State* L) { - ModelData* modelData = luax_checktype(L, 1, ModelData); - ModelMaterial* material = luax_checkmodelmaterial(L, 1); - TextureData* textureData = modelData->textures.data[material->normalTexture]; - luax_pushobject(L, textureData); - return 1; -} const luaL_Reg lovrModelData[] = { - { "getVertexData", l_lovrModelDataGetVertexData }, - { "getTriangleCount", l_lovrModelDataGetTriangleCount }, - { "getTriangle", l_lovrModelDataGetTriangle }, - { "getNodeCount", l_lovrModelDataGetNodeCount }, - { "getNodeName", l_lovrModelDataGetNodeName }, - { "getLocalNodeTransform", l_lovrModelDataGetLocalNodeTransform }, - { "getGlobalNodeTransform", l_lovrModelDataGetGlobalNodeTransform }, - { "getNodeParent", l_lovrModelDataGetNodeParent }, - { "getNodeChildren", l_lovrModelDataGetNodeChildren }, - { "getNodeComponentCount", l_lovrModelDataGetNodeComponentCount }, - { "getNodeComponent", l_lovrModelDataGetNodeComponent }, - { "getAnimationCount", l_lovrModelDataGetAnimationCount }, - { "getMaterialCount", l_lovrModelDataGetMaterialCount }, - { "getMetalness", l_lovrModelDataGetMetalness }, - { "getRoughness", l_lovrModelDataGetRoughness }, - { "getDiffuseColor", l_lovrModelDataGetDiffuseColor }, - { "getEmissiveColor", l_lovrModelDataGetEmissiveColor }, - { "getDiffuseTexture", l_lovrModelDataGetDiffuseTexture }, - { "getEmissiveTexture", l_lovrModelDataGetEmissiveTexture }, - { "getMetalnessTexture", l_lovrModelDataGetMetalnessTexture }, - { "getRoughnessTexture", l_lovrModelDataGetRoughnessTexture }, - { "getOcclusionTexture", l_lovrModelDataGetOcclusionTexture }, - { "getNormalTexture", l_lovrModelDataGetNormalTexture }, { NULL, NULL } }; diff --git a/src/api/types/vertexData.c b/src/api/types/vertexData.c index ac61cc57..45d176a6 100644 --- a/src/api/types/vertexData.c +++ b/src/api/types/vertexData.c @@ -1,7 +1,7 @@ #include "api.h" #include "api/data.h" -int luax_loadvertices(lua_State* L, int index, VertexFormat* format, VertexPointer vertices) { +int luax_loadvertices(lua_State* L, int index, VertexFormat* format, AttributePointer vertices) { uint32_t count = lua_objlen(L, index); for (uint32_t i = 0; i < count; i++) { @@ -16,9 +16,13 @@ int luax_loadvertices(lua_State* L, int index, VertexFormat* format, VertexPoint for (int k = 0; k < attribute.count; k++) { lua_rawgeti(L, -1, ++component); switch (attribute.type) { - case ATTR_FLOAT: *vertices.floats++ = luax_optfloat(L, -1, 0.f); break; - case ATTR_BYTE: *vertices.bytes++ = luaL_optint(L, -1, 255); break; - case ATTR_INT: *vertices.ints++ = luaL_optint(L, -1, 0); break; + case I8: *vertices.i8++ = luaL_optinteger(L, -1, 0); break; + case U8: *vertices.u8++ = luaL_optinteger(L, -1, 0); break; + case I16: *vertices.i16++ = luaL_optinteger(L, -1, 0); break; + case U16: *vertices.u16++ = luaL_optinteger(L, -1, 0); break; + case I32: *vertices.i32++ = luaL_optinteger(L, -1, 0); break; + case U32: *vertices.u32++ = luaL_optinteger(L, -1, 0); break; + case F32: *vertices.u32++ = luaL_optnumber(L, -1, 0); break; } lua_pop(L, 1); } @@ -83,18 +87,22 @@ int luax_pushvertexformat(lua_State* L, VertexFormat* format) { return 1; } -int luax_pushvertexattribute(lua_State* L, VertexPointer* vertex, Attribute attribute) { +int luax_pushvertexattribute(lua_State* L, AttributePointer* vertex, Attribute attribute) { for (int i = 0; i < attribute.count; i++) { switch (attribute.type) { - case ATTR_FLOAT: lua_pushnumber(L, *vertex->floats++); break; - case ATTR_BYTE: lua_pushnumber(L, *vertex->bytes++); break; - case ATTR_INT: lua_pushinteger(L, *vertex->ints++); break; + case I8: lua_pushinteger(L, *vertex->i8++); break; + case U8: lua_pushinteger(L, *vertex->u8++); break; + case I16: lua_pushinteger(L, *vertex->i16++); break; + case U16: lua_pushinteger(L, *vertex->u16++); break; + case I32: lua_pushinteger(L, *vertex->i32++); break; + case U32: lua_pushinteger(L, *vertex->u32++); break; + case F32: lua_pushnumber(L, *vertex->f32++); break; } } return attribute.count; } -int luax_pushvertex(lua_State* L, VertexPointer* vertex, VertexFormat* format) { +int luax_pushvertex(lua_State* L, AttributePointer* vertex, VertexFormat* format) { int count = 0; for (int i = 0; i < format->count; i++) { count += luax_pushvertexattribute(L, vertex, format->attributes[i]); @@ -102,17 +110,21 @@ int luax_pushvertex(lua_State* L, VertexPointer* vertex, VertexFormat* format) { return count; } -void luax_setvertexattribute(lua_State* L, int index, VertexPointer* vertex, Attribute attribute) { +void luax_setvertexattribute(lua_State* L, int index, AttributePointer* vertex, Attribute attribute) { for (int i = 0; i < attribute.count; i++) { switch (attribute.type) { - case ATTR_FLOAT: *vertex->floats++ = luax_optfloat(L, index++, 0.f); break; - case ATTR_BYTE: *vertex->bytes++ = luaL_optint(L, index++, 255); break; - case ATTR_INT: *vertex->ints++ = luaL_optint(L, index++, 0); break; + case I8: *vertex->i8++ = luaL_optinteger(L, index++, 0); break; + case U8: *vertex->u8++ = luaL_optinteger(L, index++, 0); break; + case I16: *vertex->i16++ = luaL_optinteger(L, index++, 0); break; + case U16: *vertex->u16++ = luaL_optinteger(L, index++, 0); break; + case I32: *vertex->i32++ = luaL_optinteger(L, index++, 0); break; + case U32: *vertex->u32++ = luaL_optinteger(L, index++, 0); break; + case F32: *vertex->f32++ = luaL_optnumber(L, index++, 0.); break; } } } -void luax_setvertex(lua_State* L, int index, VertexPointer* vertex, VertexFormat* format) { +void luax_setvertex(lua_State* L, int index, AttributePointer* vertex, VertexFormat* format) { if (lua_istable(L, index)) { int component = 0; for (int i = 0; i < format->count; i++) { @@ -120,9 +132,13 @@ void luax_setvertex(lua_State* L, int index, VertexPointer* vertex, VertexFormat for (int j = 0; j < attribute.count; j++) { lua_rawgeti(L, index, ++component); switch (attribute.type) { - case ATTR_FLOAT: *vertex->floats++ = luax_optfloat(L, -1, 0.f); break; - case ATTR_BYTE: *vertex->bytes++ = luaL_optint(L, -1, 255); break; - case ATTR_INT: *vertex->ints++ = luaL_optint(L, -1, 0); break; + case I8: *vertex->i8++ = luaL_optinteger(L, -1, 0); break; + case U8: *vertex->u8++ = luaL_optinteger(L, -1, 0); break; + case I16: *vertex->i16++ = luaL_optinteger(L, -1, 0); break; + case U16: *vertex->u16++ = luaL_optinteger(L, -1, 0); break; + case I32: *vertex->i32++ = luaL_optinteger(L, -1, 0); break; + case U32: *vertex->u32++ = luaL_optinteger(L, -1, 0); break; + case F32: *vertex->f32++ = luaL_optnumber(L, -1, 0.); break; } lua_pop(L, 1); } @@ -152,7 +168,7 @@ int l_lovrVertexDataGetFormat(lua_State* L) { int l_lovrVertexDataGetVertex(lua_State* L) { VertexData* vertexData = luax_checktype(L, 1, VertexData); uint32_t index = (uint32_t) luaL_checkint(L, 2) - 1; - VertexPointer vertex = { .raw = (uint8_t*) vertexData->blob.data + index * vertexData->format.stride }; + AttributePointer vertex = { .raw = (uint8_t*) vertexData->blob.data + index * vertexData->format.stride }; return luax_pushvertex(L, &vertex, &vertexData->format); } @@ -161,8 +177,8 @@ int l_lovrVertexDataSetVertex(lua_State* L) { uint32_t index = (uint32_t) luaL_checkint(L, 2) - 1; lovrAssert(index < vertexData->count, "Invalid vertex index: %d", index + 1); VertexFormat* format = &vertexData->format; - VertexPointer vertex = { .raw = vertexData->blob.data }; - vertex.bytes += index * format->stride; + AttributePointer vertex = { .raw = vertexData->blob.data }; + vertex.u8 += index * format->stride; luax_setvertex(L, 3, &vertex, format); return 0; } @@ -175,8 +191,8 @@ int l_lovrVertexDataGetVertexAttribute(lua_State* L) { lovrAssert(vertexIndex < vertexData->count, "Invalid vertex index: %d", vertexIndex + 1); lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid attribute index: %d", attributeIndex + 1); Attribute attribute = format->attributes[attributeIndex]; - VertexPointer vertex = { .raw = vertexData->blob.data }; - vertex.bytes += vertexIndex * format->stride + attribute.offset; + AttributePointer vertex = { .raw = vertexData->blob.data }; + vertex.u8 += vertexIndex * format->stride + attribute.offset; return luax_pushvertexattribute(L, &vertex, attribute); } @@ -188,8 +204,8 @@ int l_lovrVertexDataSetVertexAttribute(lua_State* L) { lovrAssert(vertexIndex < vertexData->count, "Invalid vertex index: %d", vertexIndex + 1); lovrAssert(attributeIndex >= 0 && attributeIndex < format->count, "Invalid attribute index: %d", attributeIndex + 1); Attribute attribute = format->attributes[attributeIndex]; - VertexPointer vertex = { .raw = vertexData->blob.data }; - vertex.bytes += vertexIndex * format->stride + attribute.offset; + AttributePointer vertex = { .raw = vertexData->blob.data }; + vertex.u8 += vertexIndex * format->stride + attribute.offset; luax_setvertexattribute(L, 4, &vertex, attribute); return 0; } @@ -201,8 +217,8 @@ int l_lovrVertexDataSetVertices(lua_State* L) { uint32_t vertexCount = lua_objlen(L, 2); int start = luaL_optinteger(L, 3, 1) - 1; lovrAssert(start + vertexCount <= vertexData->count, "VertexData can only hold %d vertices", vertexData->count); - VertexPointer vertices = { .raw = vertexData->blob.data }; - vertices.bytes += start * format->stride; + AttributePointer vertices = { .raw = vertexData->blob.data }; + vertices.u8 += start * format->stride; for (uint32_t i = 0; i < vertexCount; i++) { lua_rawgeti(L, 2, i + 1); diff --git a/src/data/modelData.c b/src/data/modelData.c index 2d38db15..bb377f9c 100644 --- a/src/data/modelData.c +++ b/src/data/modelData.c @@ -1,600 +1,432 @@ #include "data/modelData.h" -#include "filesystem/filesystem.h" -#include "filesystem/file.h" #include "lib/math.h" -#include -#include -#include -#include -#include +#include "lib/jsmn/jsmn.h" +#include -#ifdef LOVR_USE_ASSIMP -#include -#include -#include -#include -#include -#include -#include -#include +// Notes: +// - We parse in two passes. +// - In the first pass we figure out how much memory we need to allocate. +// - Then we allocate the memory and do a second pass to fill everything in. +// - Plan is to make this parser destructive, so it mutates the input Blob in order to avoid doing +// work in some situations, for speed. May make sense to provide a non-destructive option. +// - Currently this is most useful for reusing string memory by changing quotes to \0's. +// - Caveats: +// - The IO callback must not hang onto the filenames that are passed into it. -static void normalizePath(char* path, char* dst, size_t size) { - char* slash = path; - while ((slash = strchr(path, '\\')) != NULL) { *slash++ = '/'; } +#define MAGIC_glTF 0x46546c67 +#define MAGIC_JSON 0x4e4f534a +#define MAGIC_BIN 0x004e4942 - if (path[0] == '/') { - strncpy(dst, path, size); - return; +#define KEY_EQ(k, s) !strncmp(k.data, s, k.length) +#define TOK_INT(j, t) strtol(j + t->start, NULL, 10) +#define TOK_BOOL(j, t) (*(j + t->start) == 't') +#define TOK_FLOAT(j, t) strtof(j + t->start, NULL) + +typedef struct { + struct { int count; jsmntok_t* token; } accessors; + struct { int count; jsmntok_t* token; } blobs; + struct { int count; jsmntok_t* token; } views; + struct { int count; jsmntok_t* token; } nodes; + struct { int count; jsmntok_t* token; } meshes; + int childCount; + int primitiveCount; +} gltfInfo; + +static int nomString(const char* data, jsmntok_t* token, gltfString* string) { + lovrAssert(token->type == JSMN_STRING, "Expected string"); + string->data = (char*) data + token->start; + string->length = token->end - token->start; + return 1; +} + +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); } +} - 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; +// Kinda like sum(map(arr, obj => #obj[key])) +static jsmntok_t* aggregate(const char* json, jsmntok_t* token, const char* target, int* total) { + *total = 0; + int size = (token++)->size; + for (int i = 0; i < size; i++) { + if (token->size > 0) { + int keys = (token++)->size; + for (int k = 0; k < keys; k++) { + gltfString key; + token += nomString(json, token, &key); + if (KEY_EQ(key, target)) { + *total += token->size; + } + token += nomValue(json, token, 1, 0); } } - while (*path != '\0' && *path != '/') { - *dst++ = *path++; + } + return token; +} + +static void preparse(const char* json, jsmntok_t* tokens, int tokenCount, gltfInfo* info, size_t* dataSize) { + for (jsmntok_t* token = tokens + 1; token < tokens + tokenCount;) { // +1 to skip root object + gltfString key; + token += nomString(json, token, &key); + + if (KEY_EQ(key, "accessors")) { + info->accessors.token = token; + info->accessors.count = token->size; + *dataSize += info->accessors.count * sizeof(ModelAccessor); + token += nomValue(json, token, 1, 0); + } else if (KEY_EQ(key, "buffers")) { + info->blobs.token = token; + info->blobs.count = token->size; + *dataSize += info->blobs.count * sizeof(ModelBlob); + token += nomValue(json, token, 1, 0); + } else if (KEY_EQ(key, "bufferViews")) { + info->views.token = token; + info->views.count = token->size; + *dataSize += info->views.count * sizeof(ModelView); + token += nomValue(json, token, 1, 0); + } else if (KEY_EQ(key, "nodes")) { + info->nodes.token = token; + info->nodes.count = token->size; + *dataSize += info->nodes.count * sizeof(ModelNode); + token = aggregate(json, token, "children", &info->childCount); + } else if (KEY_EQ(key, "meshes")) { + info->meshes.token = token; + info->meshes.count = token->size; + *dataSize += info->meshes.count * sizeof(ModelMesh); + token = aggregate(json, token, "primitives", &info->primitiveCount); + *dataSize += info->primitiveCount * sizeof(ModelPrimitive); + } else { + token += nomValue(json, token, 1, 0); // Skip } - *dst++ = '/'; - } - - *--dst = '\0'; -} - -static void assimpSumChildren(struct aiNode* assimpNode, int* totalChildren) { - (*totalChildren)++; - for (unsigned int i = 0; i < assimpNode->mNumChildren; i++) { - assimpSumChildren(assimpNode->mChildren[i], totalChildren); } } -static void assimpNodeTraversal(ModelData* modelData, struct aiNode* assimpNode, int* nodeId) { - int currentIndex = *nodeId; - ModelNode* node = &modelData->nodes[currentIndex]; - node->name = strdup(assimpNode->mName.data); - map_set(&modelData->nodeMap, node->name, currentIndex); +static void parseAccessors(const char* json, jsmntok_t* token, ModelData* model) { + if (!token) return; - // Transform - struct aiMatrix4x4 m = assimpNode->mTransformation; - aiTransposeMatrix4(&m); - mat4_set(node->transform, (float*) &m); - if (node->parent == -1) { - mat4_set(node->globalTransform, node->transform); - } else { - mat4_set(node->globalTransform, modelData->nodes[node->parent].globalTransform); - mat4_multiply(node->globalTransform, node->transform); - } + int count = (token++)->size; + for (int i = 0; i < count; i++) { + ModelAccessor* accessor = &model->accessors[i]; + gltfString key; + int keyCount = (token++)->size; - // 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); - } -} - -static void aabbIterator(ModelData* modelData, ModelNode* node, float aabb[6]) { - for (int i = 0; i < node->primitives.length; i++) { - ModelPrimitive* primitive = &modelData->primitives[node->primitives.data[i]]; - for (int j = 0; j < primitive->drawCount; j++) { - uint32_t index; - if (modelData->indexSize == sizeof(uint16_t)) { - index = modelData->indices.shorts[primitive->drawStart + j]; + for (int k = 0; k < keyCount; k++) { + token += nomString(json, token, &key); + if (KEY_EQ(key, "bufferView")) { + accessor->view = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "count")) { + accessor->count = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "byteOffset")) { + accessor->offset = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "componentType")) { + switch (TOK_INT(json, token)) { + case 5120: accessor->type = I8; break; + case 5121: accessor->type = U8; break; + case 5122: accessor->type = I16; break; + case 5123: accessor->type = U16; break; + case 5125: accessor->type = U32; break; + case 5126: accessor->type = F32; break; + default: break; + } + token++; + } else if (KEY_EQ(key, "type")) { + gltfString type; + token += nomString(json, token, &type); + if (KEY_EQ(type, "SCALAR")) { + accessor->components = 1; + } else if (type.length == 4 && type.data[0] == 'V') { + accessor->components = type.data[3] - '0'; + } else if (type.length == 4 && type.data[0] == 'M') { + lovrThrow("Matrix accessors are not supported"); + } else { + lovrThrow("Unknown attribute type"); + } + } else if (KEY_EQ(key, "normalized")) { + accessor->normalized = TOK_BOOL(json, token), token++; } else { - index = modelData->indices.ints[primitive->drawStart + j]; + token += nomValue(json, token, 1, 0); // Skip } - float vertex[3]; - VertexPointer vertices = { .raw = modelData->vertexData->blob.data }; - vec3_init(vertex, (float*) (vertices.bytes + index * modelData->vertexData->format.stride)); - mat4_transform(node->globalTransform, &vertex[0], &vertex[1], &vertex[2]); - aabb[0] = MIN(aabb[0], vertex[0]); - aabb[1] = MAX(aabb[1], vertex[0]); - aabb[2] = MIN(aabb[2], vertex[1]); - aabb[3] = MAX(aabb[3], vertex[1]); - aabb[4] = MIN(aabb[4], vertex[2]); - aabb[5] = MAX(aabb[5], vertex[2]); } } - - for (int i = 0; i < node->children.length; i++) { - ModelNode* child = &modelData->nodes[node->children.data[i]]; - aabbIterator(modelData, child, aabb); - } } -static float readMaterialScalar(struct aiMaterial* assimpMaterial, const char* key, unsigned int type, unsigned int index) { - float scalar; - if (aiGetMaterialFloatArray(assimpMaterial, key, type, index, &scalar, NULL) == aiReturn_SUCCESS) { - return scalar; - } else { - return 1.f; - } -} +static void parseBlobs(const char* json, jsmntok_t* token, ModelData* model, ModelDataIO io, void* binData) { + if (!token) return; -static Color readMaterialColor(struct aiMaterial* assimpMaterial, const char* key, unsigned int type, unsigned int index, Color fallback) { - struct aiColor4D assimpColor; - if (aiGetMaterialColor(assimpMaterial, key, type, index, &assimpColor) == aiReturn_SUCCESS) { - return (Color) { .r = assimpColor.r, .g = assimpColor.g, .b = assimpColor.b, .a = assimpColor.a }; - } else { - return fallback; - } -} + int count = (token++)->size; + for (int i = 0; i < count; i++) { + ModelBlob* blob = &model->blobs[i]; + gltfString key; + int keyCount = (token++)->size; + size_t bytesRead = 0; + bool hasUri = false; -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) { - return 0; - } - - char* path = str.data; - - int* cachedTexture = map_get(textureCache, path); - if (cachedTexture) { - return *cachedTexture; - } - - 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'; - else fullPath[0] = '\0'; - strncat(fullPath, path, LOVR_PATH_MAX - 1); - normalizePath(fullPath, normalizedPath, LOVR_PATH_MAX); - - size_t size; - void* data = lovrFilesystemRead(normalizedPath, &size); - if (!data) { - return 0; - } - - Blob* blob = lovrBlobCreate(data, size, path); - TextureData* textureData = lovrTextureDataCreateFromBlob(blob, true); - lovrRelease(blob); - int textureIndex = modelData->textures.length; - vec_push(&modelData->textures, textureData); - map_set(textureCache, path, textureIndex); - return textureIndex; -} - -// Blob IO (to avoid reading data twice) -static size_t assimpBlobRead(struct aiFile* assimpFile, char* buffer, size_t size, size_t count) { - Blob* blob = (Blob*) assimpFile->UserData; - char* data = blob->data; - size_t bytes = MIN(count * size * sizeof(char), blob->size - blob->seek); - memcpy(buffer, data + blob->seek, bytes); - blob->seek += bytes; - return bytes / size; -} - -static size_t assimpBlobGetSize(struct aiFile* assimpFile) { - Blob* blob = (Blob*) assimpFile->UserData; - return blob->size; -} - -static aiReturn assimpBlobSeek(struct aiFile* assimpFile, size_t position, enum aiOrigin origin) { - 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; -} - -static size_t assimpBlobTell(struct aiFile* assimpFile) { - Blob* blob = (Blob*) assimpFile->UserData; - return blob->seek; -} - -// File IO (for reading referenced materials/textures) -static size_t assimpFileRead(struct aiFile* assimpFile, char* buffer, size_t size, size_t count) { - File* file = (File*) assimpFile->UserData; - unsigned long bytes = lovrFileRead(file, buffer, size * count); - return bytes / size; -} - -static size_t assimpFileGetSize(struct aiFile* assimpFile) { - File* file = (File*) assimpFile->UserData; - return lovrFileGetSize(file); -} - -static aiReturn assimpFileSeek(struct aiFile* assimpFile, size_t position, enum aiOrigin origin) { - File* file = (File*) assimpFile->UserData; - return lovrFileSeek(file, position) ? aiReturn_FAILURE : aiReturn_SUCCESS; -} - -static size_t assimpFileTell(struct aiFile* assimpFile) { - File* file = (File*) assimpFile->UserData; - return lovrFileTell(file); -} - -static struct aiFile* assimpFileOpen(struct aiFileIO* io, const char* path, const char* mode) { - struct aiFile* assimpFile = malloc(sizeof(struct aiFile)); - Blob* blob = (Blob*) io->UserData; - if (!strcmp(blob->name, path)) { - blob->seek = 0; - assimpFile->ReadProc = assimpBlobRead; - assimpFile->FileSizeProc = assimpBlobGetSize; - assimpFile->SeekProc = assimpBlobSeek; - assimpFile->TellProc = assimpBlobTell; - assimpFile->UserData = (void*) blob; - } else { - char tempPath[LOVR_PATH_MAX]; - char normalizedPath[LOVR_PATH_MAX]; - strncpy(tempPath, path, LOVR_PATH_MAX); - normalizePath(tempPath, normalizedPath, LOVR_PATH_MAX); - - File* file = lovrFileCreate(normalizedPath); - if (lovrFileOpen(file, OPEN_READ)) { - lovrRelease(file); - return NULL; + for (int k = 0; k < keyCount; k++) { + token += nomString(json, token, &key); + if (KEY_EQ(key, "byteLength")) { + blob->size = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "uri")) { + hasUri = true; + gltfString filename; + token += nomString(json, token, &filename); + filename.data[filename.length] = '\0'; // Change the quote into a terminator (I'll be b0k) + blob->data = io.read(filename.data, &bytesRead); + lovrAssert(blob->data, "Unable to read %s", filename.data); + } else { + token += nomValue(json, token, 1, 0); // Skip + } } - assimpFile->ReadProc = assimpFileRead; - assimpFile->FileSizeProc = assimpFileGetSize; - assimpFile->SeekProc = assimpFileSeek; - assimpFile->TellProc = assimpFileTell; - assimpFile->UserData = (void*) file; + if (hasUri) { + lovrAssert(bytesRead == blob->size, "Couldn't read all of buffer data"); + } else { + lovrAssert(binData && i == 0, "Buffer is missing URI"); + blob->data = binData; + } } - - return assimpFile; } -static void assimpFileClose(struct aiFileIO* io, struct aiFile* assimpFile) { - void* blob = io->UserData; - if (assimpFile->UserData != blob) { - File* file = (File*) assimpFile->UserData; - lovrFileClose(file); - lovrRelease(file); +static void parseViews(const char* json, jsmntok_t* token, ModelData* model) { + if (!token) return; + + int count = (token++)->size; + for (int i = 0; i < count; i++) { + ModelView* view = &model->views[i]; + gltfString key; + int keyCount = (token++)->size; + + for (int k = 0; k < keyCount; k++) { + token += nomString(json, token, &key); + if (KEY_EQ(key, "buffer")) { + view->blob = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "byteOffset")) { + view->offset = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "byteLength")) { + view->length = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "byteStride")) { + view->stride = TOK_INT(json, token), token++; + } else { + token += nomValue(json, token, 1, 0); // Skip + } + } } - free(assimpFile); } -ModelData* lovrModelDataInit(ModelData* modelData, Blob* blob) { - struct aiFileIO assimpIO; - assimpIO.OpenProc = assimpFileOpen; - assimpIO.CloseProc = assimpFileClose; - assimpIO.UserData = (void*) blob; +static void parseNodes(const char* json, jsmntok_t* token, ModelData* model) { + if (!token) return; - struct aiPropertyStore* propertyStore = aiCreatePropertyStore(); - aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE); - aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBBC_MAX_BONES, 48); - unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_OptimizeGraph | aiProcess_SplitByBoneCount; - const struct aiScene* scene = aiImportFileExWithProperties(blob->name, flags, &assimpIO, propertyStore); - aiReleasePropertyStore(propertyStore); - lovrAssert(scene, "Unable to load model from '%s': %s", blob->name, aiGetErrorString()); + int childIndex = 0; + int count = (token++)->size; // Enter array + for (int i = 0; i < count; i++) { + ModelNode* node = &model->nodes[i]; + float translation[3] = { 0, 0, 0 }; + float rotation[4] = { 0, 0, 0, 0 }; + float scale[3] = { 1, 1, 1 }; + bool matrix = false; - uint32_t vertexCount = 0; - bool hasNormals = false; - bool hasUVs = false; - bool hasVertexColors = false; - bool hasTangents = false; - bool isSkinned = false; + gltfString key; + int keyCount = (token++)->size; // Enter object + for (int k = 0; k < keyCount; k++) { + token += nomString(json, token, &key); - for (unsigned int m = 0; m < scene->mNumMeshes; m++) { - struct aiMesh* assimpMesh = scene->mMeshes[m]; - vertexCount += assimpMesh->mNumVertices; - modelData->indexCount += assimpMesh->mNumFaces * 3; - hasNormals |= assimpMesh->mNormals != NULL; - hasUVs |= assimpMesh->mTextureCoords[0] != NULL; - hasVertexColors |= assimpMesh->mColors[0] != NULL; - hasTangents |= assimpMesh->mTangents != NULL; - isSkinned |= assimpMesh->mNumBones > 0; + if (KEY_EQ(key, "children")) { + node->children = &model->childMap[childIndex]; + node->childCount = (token++)->size; + for (uint32_t j = 0; j < node->childCount; j++) { + model->childMap[childIndex++] = TOK_INT(json, token), token++; + } + } else if (KEY_EQ(key, "mesh")) { + node->mesh = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "matrix")) { + lovrAssert(token->size == 16, "Node matrix needs 16 elements"); + matrix = true; + for (int j = 0; j < token->size; j++) { + node->transform[j] = TOK_FLOAT(json, token), token++; + } + } else if (KEY_EQ(key, "translation")) { + lovrAssert(token->size == 3, "Node translation needs 3 elements"); + translation[0] = TOK_FLOAT(json, token), token++; + translation[1] = TOK_FLOAT(json, token), token++; + translation[2] = TOK_FLOAT(json, token), token++; + } else if (KEY_EQ(key, "rotation")) { + lovrAssert(token->size == 4, "Node rotation needs 4 elements"); + rotation[0] = TOK_FLOAT(json, token), token++; + rotation[1] = TOK_FLOAT(json, token), token++; + rotation[2] = TOK_FLOAT(json, token), token++; + rotation[3] = TOK_FLOAT(json, token), token++; + } else if (KEY_EQ(key, "scale")) { + lovrAssert(token->size == 3, "Node scale needs 3 elements"); + scale[0] = TOK_FLOAT(json, token), token++; + scale[1] = TOK_FLOAT(json, token), token++; + scale[2] = TOK_FLOAT(json, token), token++; + } else { + token += nomValue(json, token, 1, 0); // Skip + } + } + + // 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]); + } } +} - VertexFormat format; - vertexFormatInit(&format); - vertexFormatAppend(&format, "lovrPosition", ATTR_FLOAT, 3); +static jsmntok_t* parsePrimitive(const char* json, jsmntok_t* token, int index, ModelData* model) { + gltfString key; + ModelPrimitive* primitive = &model->primitives[index]; + int keyCount = (token++)->size; // Enter object + memset(primitive->attributes, 0xff, sizeof(primitive->attributes)); + primitive->indices = -1; + primitive->mode = DRAW_TRIANGLES; - if (hasNormals) vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3); - if (hasUVs) vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2); - if (hasVertexColors) vertexFormatAppend(&format, "lovrVertexColor", ATTR_BYTE, 4); - if (hasTangents) vertexFormatAppend(&format, "lovrTangent", ATTR_FLOAT, 3); - size_t boneByteOffset = format.stride; - if (isSkinned) vertexFormatAppend(&format, "lovrBones", ATTR_INT, 4); - if (isSkinned) vertexFormatAppend(&format, "lovrBoneWeights", ATTR_FLOAT, 4); + for (int k = 0; k < keyCount; k++) { + token += nomString(json, token, &key); - // Allocate - modelData->vertexData = lovrVertexDataCreate(vertexCount, &format); - modelData->indexSize = vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t); - modelData->indices.raw = malloc(modelData->indexCount * modelData->indexSize); - modelData->primitiveCount = scene->mNumMeshes; - modelData->primitives = malloc(modelData->primitiveCount * sizeof(ModelPrimitive)); + if (KEY_EQ(key, "material")) { + primitive->material = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "indices")) { + primitive->indices = TOK_INT(json, token), token++; + } else if (KEY_EQ(key, "mode")) { + switch (TOK_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"); + } + token++; + } else if (KEY_EQ(key, "attributes")) { + int attributeCount = (token++)->size; + for (int i = 0; i < attributeCount; i++) { + gltfString name; + token += nomString(json, token, &name); + int accessor = TOK_INT(json, token); + if (KEY_EQ(name, "POSITION")) { + primitive->attributes[ATTR_POSITION] = accessor; + } else if (KEY_EQ(name, "NORMAL")) { + primitive->attributes[ATTR_NORMAL] = accessor; + } else if (KEY_EQ(name, "TEXCOORD_0")) { + primitive->attributes[ATTR_TEXCOORD] = accessor; + } else if (KEY_EQ(name, "COLOR_0")) { + primitive->attributes[ATTR_COLOR] = accessor; + } else if (KEY_EQ(name, "TANGENT")) { + primitive->attributes[ATTR_TANGENT] = accessor; + } else if (KEY_EQ(name, "JOINTS_0")) { + primitive->attributes[ATTR_BONES] = accessor; + } else if (KEY_EQ(name, "WEIGHTS_0")) { + primitive->attributes[ATTR_WEIGHTS] = accessor; + } + token++; + } + } else { + token += nomValue(json, token, 1, 0); // Skip + } + } + return token; +} - // Load vertices - IndexPointer indices = modelData->indices; - uint32_t vertex = 0; - uint32_t index = 0; - for (unsigned int m = 0; m < scene->mNumMeshes; m++) { - struct aiMesh* assimpMesh = scene->mMeshes[m]; - ModelPrimitive* primitive = &modelData->primitives[m]; - primitive->material = assimpMesh->mMaterialIndex; - primitive->drawStart = index; - primitive->drawCount = 0; - uint32_t baseVertex = vertex; +static void parseMeshes(const char* json, jsmntok_t* token, ModelData* model) { + if (!token) return; - // Indices - for (unsigned int f = 0; f < assimpMesh->mNumFaces; f++) { - struct aiFace assimpFace = assimpMesh->mFaces[f]; - lovrAssert(assimpFace.mNumIndices == 3, "Only triangular faces are supported"); + int primitiveIndex = 0; + int count = (token++)->size; // Enter array + for (int i = 0; i < count; i++) { + gltfString key; + ModelMesh* mesh = &model->meshes[i]; + int keyCount = (token++)->size; // Enter object + for (int k = 0; k < keyCount; k++) { + token += nomString(json, token, &key); - primitive->drawCount += assimpFace.mNumIndices; - - if (modelData->indexSize == sizeof(uint16_t)) { - for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) { - indices.shorts[index++] = baseVertex + assimpFace.mIndices[i]; + if (KEY_EQ(key, "primitives")) { + mesh->primitives = &model->primitives[primitiveIndex]; + mesh->primitiveCount = (token++)->size; + for (uint32_t j = 0; j < mesh->primitiveCount; j++) { + token = parsePrimitive(json, token, primitiveIndex++, model); } } else { - for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) { - indices.ints[index++] = baseVertex + assimpFace.mIndices[i]; - } - } - } - - // Vertices - for (unsigned int v = 0; v < assimpMesh->mNumVertices; v++) { - VertexPointer vertices = { .raw = modelData->vertexData->blob.data }; - vertices.bytes += vertex * modelData->vertexData->format.stride; - - *vertices.floats++ = assimpMesh->mVertices[v].x; - *vertices.floats++ = assimpMesh->mVertices[v].y; - *vertices.floats++ = assimpMesh->mVertices[v].z; - - if (hasNormals) { - if (assimpMesh->mNormals) { - *vertices.floats++ = assimpMesh->mNormals[v].x; - *vertices.floats++ = assimpMesh->mNormals[v].y; - *vertices.floats++ = assimpMesh->mNormals[v].z; - } else { - *vertices.floats++ = 0; - *vertices.floats++ = 0; - *vertices.floats++ = 0; - } - } - - if (hasUVs) { - if (assimpMesh->mTextureCoords[0]) { - *vertices.floats++ = assimpMesh->mTextureCoords[0][v].x; - *vertices.floats++ = assimpMesh->mTextureCoords[0][v].y; - } else { - *vertices.floats++ = 0; - *vertices.floats++ = 0; - } - } - - if (hasVertexColors) { - 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; - } - } - - if (hasTangents) { - if (assimpMesh->mTangents) { - *vertices.floats++ = assimpMesh->mTangents[v].x; - *vertices.floats++ = assimpMesh->mTangents[v].y; - *vertices.floats++ = assimpMesh->mTangents[v].z; - } else { - *vertices.floats++ = 0; - *vertices.floats++ = 0; - *vertices.floats++ = 0; - } - } - - vertex++; - } - - // Bones - primitive->boneCount = assimpMesh->mNumBones; - map_init(&primitive->boneMap); - for (unsigned int b = 0; b < assimpMesh->mNumBones; b++) { - struct aiBone* assimpBone = assimpMesh->mBones[b]; - Bone* bone = &primitive->bones[b]; - - bone->name = strdup(assimpBone->mName.data); - aiTransposeMatrix4(&assimpBone->mOffsetMatrix); - mat4_set(bone->offset, (float*) &assimpBone->mOffsetMatrix); - map_set(&primitive->boneMap, bone->name, b); - - for (unsigned int w = 0; w < assimpBone->mNumWeights; w++) { - uint32_t vertexIndex = baseVertex + assimpBone->mWeights[w].mVertexId; - float weight = assimpBone->mWeights[w].mWeight; - VertexPointer vertices = { .raw = modelData->vertexData->blob.data }; - vertices.bytes += vertexIndex * modelData->vertexData->format.stride; - uint32_t* bones = (uint32_t*) (vertices.bytes + boneByteOffset); - float* weights = (float*) (bones + MAX_BONES_PER_VERTEX); - - int boneSlot = 0; - while (weights[boneSlot] > 0) { - boneSlot++; - lovrAssert(boneSlot < MAX_BONES_PER_VERTEX, "Too many bones for vertex %d", vertexIndex); - } - - bones[boneSlot] = b; - weights[boneSlot] = weight; + token += nomValue(json, token, 1, 0); // Skip } } } - - // Materials - map_int_t textureCache; - map_init(&textureCache); - vec_init(&modelData->textures); - vec_push(&modelData->textures, NULL); - modelData->materialCount = scene->mNumMaterials; - modelData->materials = malloc(modelData->materialCount * sizeof(ModelMaterial)); - for (unsigned int m = 0; m < scene->mNumMaterials; m++) { - ModelMaterial* material = &modelData->materials[m]; - struct aiMaterial* assimpMaterial = scene->mMaterials[m]; - - material->diffuseColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_DIFFUSE, (Color) { 1., 1., 1., 1. }); - material->emissiveColor = readMaterialColor(assimpMaterial, AI_MATKEY_COLOR_EMISSIVE, (Color) { 0., 0., 0., 0. }); - material->diffuseTexture = readMaterialTexture(assimpMaterial, aiTextureType_DIFFUSE, modelData, &textureCache, blob->name); - material->emissiveTexture = readMaterialTexture(assimpMaterial, aiTextureType_EMISSIVE, modelData, &textureCache, blob->name); - material->metalnessTexture = readMaterialTexture(assimpMaterial, aiTextureType_UNKNOWN, modelData, &textureCache, blob->name); - material->roughnessTexture = material->metalnessTexture; - material->occlusionTexture = readMaterialTexture(assimpMaterial, aiTextureType_LIGHTMAP, modelData, &textureCache, blob->name); - material->normalTexture = readMaterialTexture(assimpMaterial, aiTextureType_NORMALS, modelData, &textureCache, blob->name); - material->metalness = readMaterialScalar(assimpMaterial, "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0); - material->roughness = readMaterialScalar(assimpMaterial, "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0); - } - map_deinit(&textureCache); - - // Nodes - modelData->nodeCount = 0; - assimpSumChildren(scene->mRootNode, &modelData->nodeCount); - modelData->nodes = malloc(modelData->nodeCount * sizeof(ModelNode)); - modelData->nodes[0].parent = -1; - map_init(&modelData->nodeMap); - int nodeIndex = 0; - assimpNodeTraversal(modelData, scene->mRootNode, &nodeIndex); - - // Animations - modelData->animationCount = scene->mNumAnimations; - modelData->animations = malloc(modelData->animationCount * sizeof(Animation)); - for (int i = 0; i < modelData->animationCount; i++) { - struct aiAnimation* assimpAnimation = scene->mAnimations[i]; - float ticksPerSecond = assimpAnimation->mTicksPerSecond; - - Animation* animation = &modelData->animations[i]; - animation->name = strdup(assimpAnimation->mName.data); - animation->duration = assimpAnimation->mDuration / ticksPerSecond; - animation->channelCount = assimpAnimation->mNumChannels; - map_init(&animation->channels); - - for (int j = 0; j < animation->channelCount; j++) { - struct aiNodeAnim* assimpChannel = assimpAnimation->mChannels[j]; - AnimationChannel channel; - - channel.node = strdup(assimpChannel->mNodeName.data); - 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; - vec_push(&channel.positionKeyframes, ((Keyframe) { - .time = assimpKeyframe.mTime / ticksPerSecond, - .data = { position.x, position.y, position.z } - })); - } - - for (unsigned int k = 0; k < assimpChannel->mNumRotationKeys; k++) { - struct aiQuatKey assimpKeyframe = assimpChannel->mRotationKeys[k]; - struct aiQuaternion quaternion = assimpKeyframe.mValue; - vec_push(&channel.rotationKeyframes, ((Keyframe) { - .time = assimpKeyframe.mTime / ticksPerSecond, - .data = { quaternion.x, quaternion.y, quaternion.z, quaternion.w } - })); - } - - for (unsigned int k = 0; k < assimpChannel->mNumScalingKeys; k++) { - struct aiVectorKey assimpKeyframe = assimpChannel->mScalingKeys[k]; - struct aiVector3D scale = assimpKeyframe.mValue; - vec_push(&channel.scaleKeyframes, ((Keyframe) { - .time = assimpKeyframe.mTime / ticksPerSecond, - .data = { scale.x, scale.y, scale.z } - })); - } - - map_set(&animation->channels, channel.node, channel); - } - } - - aiReleaseImport(scene); - return modelData; } -#else -static void aabbIterator(ModelData* modelData, ModelNode* node, float aabb[6]) {} -ModelData* lovrModelDataInit(ModelData* modelData, Blob* blob) { - return NULL; + +ModelData* lovrModelDataInit(ModelData* model, Blob* blob, ModelDataIO io) { + uint8_t* data = blob->data; + gltfHeader* header = (gltfHeader*) data; + bool glb = header->magic == MAGIC_glTF; + const char *jsonData, *binData; + size_t jsonLength, binLength; + + if (glb) { + gltfChunkHeader* jsonHeader = (gltfChunkHeader*) &header[1]; + lovrAssert(jsonHeader->type == MAGIC_JSON, "Invalid JSON header"); + + jsonData = (char*) &jsonHeader[1]; + jsonLength = jsonHeader->length; + + gltfChunkHeader* binHeader = (gltfChunkHeader*) &jsonData[jsonLength]; + lovrAssert(binHeader->type == MAGIC_BIN, "Invalid BIN header"); + + binData = (char*) &binHeader[1]; + binLength = binHeader->length; + + // Hang onto the data since it's already here rather than make a copy of it + lovrRetain(blob); + } else { + jsonData = (char*) data; + jsonLength = blob->size; + binData = NULL; + binLength = 0; + } + + jsmn_parser parser; + jsmn_init(&parser); + + jsmntok_t tokens[1024]; // TODO malloc or token queue + int tokenCount = jsmn_parse(&parser, jsonData, jsonLength, tokens, 1024); + lovrAssert(tokenCount >= 0, "Invalid JSON"); + lovrAssert(tokens[0].type == JSMN_OBJECT, "No root object"); + + gltfInfo info = { 0 }; + size_t dataSize = 0; + preparse(jsonData, tokens, tokenCount, &info, &dataSize); + + size_t offset = 0; + model->data = calloc(1, dataSize); + model->glbBlob = glb ? blob : NULL; + model->accessorCount = info.accessors.count; + model->blobCount = info.blobs.count; + model->viewCount = info.views.count; + model->meshCount = info.meshes.count; + model->nodeCount = info.nodes.count; + model->primitiveCount = info.primitiveCount; + model->accessors = (ModelAccessor*) (model->data + offset), offset += info.accessors.count * sizeof(ModelAccessor); + model->blobs = (ModelBlob*) (model->data + offset), offset += info.blobs.count * sizeof(ModelBlob); + model->views = (ModelView*) (model->data + offset), offset += info.views.count * sizeof(ModelView); + model->meshes = (ModelMesh*) (model->data + offset), offset += info.meshes.count * sizeof(ModelMesh); + model->nodes = (ModelNode*) (model->data + offset), offset += info.nodes.count * sizeof(ModelNode); + model->primitives = (ModelPrimitive*) (model->data + offset), offset += info.primitiveCount * sizeof(ModelPrimitive); + model->childMap = (uint32_t*) (model->data + offset), offset += info.childCount * sizeof(uint32_t); + + parseAccessors(jsonData, info.accessors.token, model); + parseBlobs(jsonData, info.blobs.token, model, io, (void*) binData); + parseViews(jsonData, info.views.token, model); + parseNodes(jsonData, info.nodes.token, model); + parseMeshes(jsonData, info.meshes.token, model); + + return model; } -#endif void lovrModelDataDestroy(void* ref) { - ModelData* modelData = ref; - - for (int i = 0; i < modelData->nodeCount; i++) { - vec_deinit(&modelData->nodes[i].children); - vec_deinit(&modelData->nodes[i].primitives); - free((char*) modelData->nodes[i].name); - } - - for (int i = 0; i < modelData->primitiveCount; i++) { - ModelPrimitive* primitive = &modelData->primitives[i]; - for (int j = 0; j < primitive->boneCount; j++) { - free((char*) primitive->bones[j].name); - } - map_deinit(&primitive->boneMap); - } - - 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); - free((char*) animation->name); - } - - for (int i = 0; i < modelData->textures.length; i++) { - lovrRelease(modelData->textures.data[i]); - } - - vec_deinit(&modelData->textures); - map_deinit(&modelData->nodeMap); - - lovrRelease(modelData->vertexData); - - free(modelData->nodes); - free(modelData->primitives); - free(modelData->animations); - free(modelData->materials); - free(modelData->indices.raw); -} - -void lovrModelDataGetAABB(ModelData* modelData, float aabb[6]) { - aabb[0] = FLT_MAX; - aabb[1] = -FLT_MAX; - aabb[2] = FLT_MAX; - aabb[3] = -FLT_MAX; - aabb[4] = FLT_MAX; - aabb[5] = -FLT_MAX; - aabbIterator(modelData, &modelData->nodes[0], aabb); + // } diff --git a/src/data/modelData.h b/src/data/modelData.h index a81a3526..6d8b5437 100644 --- a/src/data/modelData.h +++ b/src/data/modelData.h @@ -1,52 +1,90 @@ #include "data/blob.h" -#include "data/textureData.h" -#include "data/vertexData.h" #include "util.h" #include "lib/map/map.h" #include "lib/vec/vec.h" #pragma once -#define MAX_BONES_PER_VERTEX 4 #define MAX_BONES 48 -typedef struct { - const char* name; - float offset[16]; -} Bone; +typedef enum { + ATTR_POSITION, + ATTR_NORMAL, + ATTR_TEXCOORD, + ATTR_COLOR, + ATTR_TANGENT, + ATTR_BONES, + ATTR_WEIGHTS, + MAX_DEFAULT_ATTRIBUTES +} DefaultAttribute; + +typedef enum { + DRAW_POINTS, + DRAW_LINES, + DRAW_LINE_LOOP, + DRAW_LINE_STRIP, + DRAW_TRIANGLES, + DRAW_TRIANGLE_STRIP, + DRAW_TRIANGLE_FAN +} DrawMode; + +typedef enum { I8, U8, I16, U16, I32, U32, F32 } AttributeType; typedef struct { + uint32_t magic; + uint32_t version; + uint32_t length; +} gltfHeader; + +typedef struct { + uint32_t length; + uint32_t type; +} gltfChunkHeader; + +typedef struct { + char* data; + size_t length; +} gltfString; + +typedef struct { + int view; + int count; + int offset; + AttributeType type; + int components : 3; + int normalized : 1; +} ModelAccessor; + +typedef struct { + void* data; + size_t size; +} ModelBlob; + +typedef struct { + int blob; + int offset; + int length; + int stride; +} ModelView; + +typedef struct { + DrawMode mode; + int attributes[MAX_DEFAULT_ATTRIBUTES]; + int indices; int material; - int drawStart; - int drawCount; - Bone bones[MAX_BONES]; - map_int_t boneMap; - int boneCount; } ModelPrimitive; -typedef vec_t(unsigned int) vec_uint_t; - -typedef struct ModelNode { - const char* name; - float transform[16]; - float globalTransform[16]; - int parent; - vec_uint_t children; - vec_uint_t primitives; -} ModelNode; +typedef struct { + ModelPrimitive* primitives; + uint32_t primitiveCount; +} ModelMesh; typedef struct { - Color diffuseColor; - Color emissiveColor; - int diffuseTexture; - int emissiveTexture; - int metalnessTexture; - int roughnessTexture; - int occlusionTexture; - int normalTexture; - float metalness; - float roughness; -} ModelMaterial; + float transform[16]; + uint32_t* children; + uint32_t childCount; + int mesh; +} ModelNode; typedef struct { double time; @@ -73,23 +111,27 @@ typedef struct { typedef struct { Ref ref; - VertexData* vertexData; - IndexPointer indices; - int indexCount; - size_t indexSize; + uint8_t* data; + Blob* glbBlob; + ModelAccessor* accessors; + ModelBlob* blobs; + ModelView* views; + ModelMesh* meshes; ModelNode* nodes; - map_int_t nodeMap; ModelPrimitive* primitives; - Animation* animations; - ModelMaterial* materials; - vec_void_t textures; + uint32_t* childMap; + int accessorCount; + int blobCount; + int viewCount; + int meshCount; int nodeCount; int primitiveCount; - int animationCount; - int materialCount; } ModelData; -ModelData* lovrModelDataInit(ModelData* modelData, Blob* blob); +typedef struct { + void* (*read)(const char* path, size_t* bytesRead); +} ModelDataIO; + +ModelData* lovrModelDataInit(ModelData* model, Blob* blob, ModelDataIO io); #define lovrModelDataCreate(...) lovrModelDataInit(lovrAlloc(ModelData), __VA_ARGS__) void lovrModelDataDestroy(void* ref); -void lovrModelDataGetAABB(ModelData* modelData, float aabb[6]); diff --git a/src/data/vertexData.c b/src/data/vertexData.c index 5a658442..f5c404ae 100644 --- a/src/data/vertexData.c +++ b/src/data/vertexData.c @@ -1,7 +1,15 @@ #include "data/vertexData.h" #include -static const size_t attributeTypeSizes[3] = { 4, 1, 4 }; +static const size_t attributeTypeSizes[] = { + [I8] = 1, + [U8] = 1, + [I16] = 2, + [U16] = 2, + [I32] = 4, + [U32] = 4, + [F32] = 4 +}; void vertexFormatInit(VertexFormat* format) { memset(format, 0, sizeof(*format)); @@ -20,10 +28,10 @@ VertexData* lovrVertexDataInit(VertexData* vertexData, uint32_t count, VertexFor } else { format = &vertexData->format; vertexFormatInit(&vertexData->format); - vertexFormatAppend(&vertexData->format, "lovrPosition", ATTR_FLOAT, 3); - vertexFormatAppend(&vertexData->format, "lovrNormal", ATTR_FLOAT, 3); - vertexFormatAppend(&vertexData->format, "lovrTexCoord", ATTR_FLOAT, 2); - vertexFormatAppend(&vertexData->format, "lovrVertexColor", ATTR_BYTE, 4); + vertexFormatAppend(&vertexData->format, "lovrPosition", F32, 3); + vertexFormatAppend(&vertexData->format, "lovrNormal", F32, 3); + vertexFormatAppend(&vertexData->format, "lovrTexCoord", F32, 2); + vertexFormatAppend(&vertexData->format, "lovrVertexColor", U8, 4); } size_t size = format->stride * count; diff --git a/src/data/vertexData.h b/src/data/vertexData.h index 93281f67..d967534c 100644 --- a/src/data/vertexData.h +++ b/src/data/vertexData.h @@ -1,15 +1,10 @@ #include "blob.h" +#include "data/modelData.h" #include #include #pragma once -typedef enum { - ATTR_FLOAT, - ATTR_BYTE, - ATTR_INT -} AttributeType; - typedef struct { const char* name; AttributeType type; @@ -26,10 +21,14 @@ typedef struct { typedef union { void* raw; - float* floats; - uint8_t* bytes; - int* ints; -} VertexPointer; + int8_t* i8; + uint8_t* u8; + int16_t* i16; + uint16_t* u16; + int32_t* i32; + uint32_t* u32; + float* f32; +} AttributePointer; typedef union { void* raw; diff --git a/src/graphics/animator.c b/src/graphics/animator.c index 99af197a..e112af80 100644 --- a/src/graphics/animator.c +++ b/src/graphics/animator.c @@ -19,6 +19,7 @@ Animator* lovrAnimatorInit(Animator* animator, ModelData* modelData) { vec_init(&animator->trackList); animator->speed = 1; + /* for (int i = 0; i < modelData->animationCount; i++) { Animation* animation = &modelData->animations[i]; @@ -35,6 +36,7 @@ Animator* lovrAnimatorInit(Animator* animator, ModelData* modelData) { map_set(&animator->trackMap, animation->name, track); vec_push(&animator->trackList, map_get(&animator->trackMap, animation->name)); } + */ return animator; } @@ -192,15 +194,17 @@ bool lovrAnimatorEvaluate(Animator* animator, const char* bone, mat4 transform) } int lovrAnimatorGetAnimationCount(Animator* animator) { - return animator->modelData->animationCount; + //oreturn animator->modelData->animationCount; + return 0; } const char* lovrAnimatorGetAnimationName(Animator* animator, int index) { - if (index < 0 || index >= animator->modelData->animationCount) { + if (index < 0 || index >= 0/*animator->modelData->animationCount*/) { return NULL; } - return animator->modelData->animations[index].name; + //return animator->modelData->animations[index].name; + return NULL; } void lovrAnimatorPlay(Animator* animator, const char* animation) { diff --git a/src/graphics/graphics.c b/src/graphics/graphics.c index 30d7752a..1b4e6a8d 100644 --- a/src/graphics/graphics.c +++ b/src/graphics/graphics.c @@ -70,11 +70,11 @@ static void lovrGraphicsInitBuffers() { VertexFormat empty = { .count = 0 }; Buffer* vertexBuffer = state.buffers[STREAM_VERTEX]; size_t stride = BUFFER_STRIDES[STREAM_VERTEX]; - MeshAttribute position = { vertexBuffer, 0, stride, ATTR_FLOAT, 3, .enabled = true }; - MeshAttribute normal = { vertexBuffer, 12, stride, ATTR_FLOAT, 3, .enabled = true }; - MeshAttribute texCoord = { vertexBuffer, 24, stride, ATTR_FLOAT, 2, .enabled = true }; - MeshAttribute drawId = { state.buffers[STREAM_DRAW_ID], 0, 0, ATTR_BYTE, 1, .integer = true, .enabled = true }; - MeshAttribute identity = { state.identityBuffer, 0, 0, ATTR_BYTE, 1, .divisor = 1, .integer = true, .enabled = true }; + MeshAttribute position = { vertexBuffer, 0, stride, F32, 3, .enabled = true }; + MeshAttribute normal = { vertexBuffer, 12, stride, F32, 3, .enabled = true }; + MeshAttribute texCoord = { vertexBuffer, 24, stride, F32, 2, .enabled = true }; + MeshAttribute drawId = { state.buffers[STREAM_DRAW_ID], 0, 0, U8, 1, .integer = true, .enabled = true }; + MeshAttribute identity = { state.identityBuffer, 0, 0, U8, 1, .divisor = 1, .integer = true, .enabled = true }; state.mesh = lovrMeshCreate(DRAW_TRIANGLES, empty, NULL, 0); lovrMeshAttachAttribute(state.mesh, "lovrPosition", &position); diff --git a/src/graphics/mesh.h b/src/graphics/mesh.h index 6e95f8db..ad5d17e0 100644 --- a/src/graphics/mesh.h +++ b/src/graphics/mesh.h @@ -1,3 +1,4 @@ +#include "data/modelData.h" #include "graphics/material.h" #include "graphics/shader.h" #include "graphics/opengl.h" @@ -22,16 +23,6 @@ typedef struct { typedef map_t(MeshAttribute) map_attribute_t; -typedef enum { - DRAW_POINTS, - DRAW_LINES, - DRAW_LINE_STRIP, - DRAW_LINE_LOOP, - DRAW_TRIANGLE_STRIP, - DRAW_TRIANGLES, - DRAW_TRIANGLE_FAN -} DrawMode; - typedef struct { Ref ref; DrawMode mode; @@ -51,7 +42,9 @@ typedef struct { } Mesh; Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, VertexFormat format, Buffer* vertexBuffer, uint32_t vertexCount); +Mesh* lovrMeshInitEmpty(Mesh* mesh, DrawMode drawMode); #define lovrMeshCreate(...) lovrMeshInit(lovrAlloc(Mesh), __VA_ARGS__) +#define lovrMeshCreateEmpty(...) lovrMeshInitEmpty(lovrAlloc(Mesh), __VA_ARGS__) void lovrMeshDestroy(void* ref); VertexFormat* lovrMeshGetVertexFormat(Mesh* mesh); Buffer* lovrMeshGetVertexBuffer(Mesh* mesh); diff --git a/src/graphics/model.c b/src/graphics/model.c index 2198ba0a..510c75c0 100644 --- a/src/graphics/model.c +++ b/src/graphics/model.c @@ -1,147 +1,60 @@ #include "graphics/model.h" #include "graphics/graphics.h" -#include "graphics/shader.h" -#include "data/blob.h" -#include "data/textureData.h" -#include "data/vertexData.h" -#include -#include +#include "resources/shaders.h" -static void renderNode(Model* model, int nodeIndex, int instances) { - ModelNode* node = &model->modelData->nodes[nodeIndex]; +Model* lovrModelInit(Model* model, ModelData* data) { + model->data = data; + lovrRetain(data); - if (node->primitives.length > 0) { - float globalInverse[16]; - if (model->animator) { - mat4_set(globalInverse, model->nodeTransforms[nodeIndex]); - mat4_invert(globalInverse); - } - - for (int i = 0; i < node->primitives.length; i++) { - ModelPrimitive* primitive = &model->modelData->primitives[node->primitives.data[i]]; - - if (model->animator) { - for (int i = 0; i < primitive->boneCount; i++) { - Bone* bone = &primitive->bones[i]; - int nodeIndex = *(int*) map_get(&model->modelData->nodeMap, bone->name); - - mat4 bonePose = model->pose[i]; - mat4_identity(bonePose); - mat4_set(bonePose, globalInverse); - mat4_multiply(bonePose, model->nodeTransforms[nodeIndex]); - mat4_multiply(bonePose, bone->offset); - } - } - - if (!model->material && model->materials) { - lovrMeshSetMaterial(model->mesh, model->materials[primitive->material]); - } - - lovrGraphicsBatch(&(BatchRequest) { - .type = BATCH_MESH, - .params.mesh = { - .object = model->mesh, - .mode = DRAW_TRIANGLES, - .rangeStart = primitive->drawStart, - .rangeCount = primitive->drawCount, - .pose = (float*) model->pose, - .instances = instances - }, - .transform = model->nodeTransforms[nodeIndex], - .material = lovrMeshGetMaterial(model->mesh) - }); + if (data->viewCount > 0) { + model->buffers = calloc(data->viewCount, sizeof(Buffer*)); + for (int i = 0; i < data->viewCount; i++) { + ModelView* view = &data->views[i]; + ModelBlob* blob = &data->blobs[view->blob]; + model->buffers[i] = lovrBufferCreate(view->length, (uint8_t*) blob->data + view->offset, BUFFER_GENERIC, USAGE_STATIC, false); } } - for (int i = 0; i < node->children.length; i++) { - renderNode(model, node->children.data[i], instances); - } -} + if (data->primitiveCount > 0) { + model->meshes = calloc(data->primitiveCount, sizeof(Mesh*)); + for (int i = 0; i < data->primitiveCount; i++) { + ModelPrimitive* primitive = &data->primitives[i]; + model->meshes[i] = lovrMeshCreateEmpty(primitive->mode); -Model* lovrModelInit(Model* model, ModelData* modelData) { - lovrRetain(modelData); - model->modelData = modelData; - model->aabbDirty = true; + bool setDrawRange = false; + for (int j = 0; j < MAX_DEFAULT_ATTRIBUTES; j++) { + if (primitive->attributes[j] >= 0) { + ModelAccessor* accessor = &data->accessors[primitive->attributes[j]]; - VertexFormat* format = &modelData->vertexData->format; - size_t vboSize = modelData->vertexData->count * format->stride; - Buffer* vertexBuffer = lovrBufferCreate(vboSize, modelData->vertexData->blob.data, BUFFER_VERTEX, USAGE_STATIC, false); - model->mesh = lovrMeshCreate(DRAW_TRIANGLES, *format, vertexBuffer, modelData->vertexData->count); - lovrRelease(vertexBuffer); + lovrMeshAttachAttribute(model->meshes[i], lovrShaderAttributeNames[j], &(MeshAttribute) { + .buffer = model->buffers[accessor->view], + .offset = accessor->offset, + .stride = data->views[accessor->view].stride, + .type = accessor->type, + .components = accessor->components, + .enabled = true + }); - size_t indexSize = modelData->indexSize; - uint32_t indexCount = modelData->indexCount; - Buffer* indexBuffer = lovrBufferCreate(indexCount * indexSize, modelData->indices.raw, BUFFER_INDEX, USAGE_STATIC, false); - lovrMeshSetIndexBuffer(model->mesh, indexBuffer, indexCount, indexSize); - lovrRelease(indexBuffer); - - lovrMeshAttachAttribute(model->mesh, "lovrDrawID", &(MeshAttribute) { - .buffer = lovrGraphicsGetIdentityBuffer(), - .type = ATTR_BYTE, - .components = 1, - .divisor = 1, - .integer = true, - .enabled = true - }); - - if (modelData->textures.length > 0) { - model->textures = calloc(modelData->textures.length, sizeof(Texture*)); - lovrAssert(model->textures, "Out of memory"); - } - - if (modelData->materialCount > 0) { - model->materials = calloc(modelData->materialCount, sizeof(Material*)); - lovrAssert(model->materials, "Out of memory"); - for (int i = 0; i < modelData->materialCount; i++) { - ModelMaterial* materialData = &modelData->materials[i]; - Material* material = lovrMaterialCreate(); - lovrMaterialSetScalar(material, SCALAR_METALNESS, materialData->metalness); - lovrMaterialSetScalar(material, SCALAR_ROUGHNESS, materialData->roughness); - lovrMaterialSetColor(material, COLOR_DIFFUSE, materialData->diffuseColor); - lovrMaterialSetColor(material, COLOR_EMISSIVE, materialData->emissiveColor); - for (MaterialTexture textureType = 0; textureType < MAX_MATERIAL_TEXTURES; textureType++) { - int textureIndex = 0; - - switch (textureType) { - case TEXTURE_DIFFUSE: textureIndex = materialData->diffuseTexture; break; - case TEXTURE_EMISSIVE: textureIndex = materialData->emissiveTexture; break; - case TEXTURE_METALNESS: textureIndex = materialData->metalnessTexture; break; - case TEXTURE_ROUGHNESS: textureIndex = materialData->roughnessTexture; break; - case TEXTURE_OCCLUSION: textureIndex = materialData->occlusionTexture; break; - case TEXTURE_NORMAL: textureIndex = materialData->normalTexture; break; - default: break; - } - - if (textureIndex) { - if (!model->textures[textureIndex]) { - TextureData* textureData = modelData->textures.data[textureIndex]; - bool srgb = textureType == TEXTURE_DIFFUSE || textureType == TEXTURE_EMISSIVE; - model->textures[textureIndex] = lovrTextureCreate(TEXTURE_2D, &textureData, 1, srgb, true, 0); + if (!setDrawRange && primitive->indices == -1) { + lovrMeshSetDrawRange(model->meshes[i], 0, accessor->count); + setDrawRange = true; } - - lovrMaterialSetTexture(material, textureType, model->textures[textureIndex]); } } - model->materials[i] = material; - } - } + lovrMeshAttachAttribute(model->meshes[i], "lovrDrawID", &(MeshAttribute) { + .buffer = lovrGraphicsGetIdentityBuffer(), + .type = U8, + .components = 1, + .divisor = 1, + .integer = true, + .enabled = true + }); - for (int i = 0; i < MAX_BONES; i++) { - mat4_identity(model->pose[i]); - } - - model->nodeTransforms = malloc(16 * modelData->nodeCount * sizeof(float)); - lovrAssert(model->nodeTransforms, "Out of memory"); - for (int i = 0; i < modelData->nodeCount; i++) { - ModelNode* node = &model->modelData->nodes[i]; - mat4 transform = model->nodeTransforms[i]; - - if (node->parent >= 0) { - mat4_set(transform, model->nodeTransforms[node->parent]); - mat4_multiply(transform, node->transform); - } else { - mat4_set(transform, node->transform); + if (primitive->indices >= 0) { + ModelAccessor* accessor = &data->accessors[primitive->indices]; + lovrMeshSetIndexBuffer(model->meshes[i], model->buffers[accessor->view], accessor->count, accessor->type == U16 ? 2 : 4); + } } } @@ -150,92 +63,16 @@ Model* lovrModelInit(Model* model, ModelData* modelData) { void lovrModelDestroy(void* ref) { Model* model = ref; - for (int i = 0; i < model->modelData->textures.length; i++) { - lovrRelease(model->textures[i]); + for (int i = 0; i < model->data->viewCount; i++) { + lovrRelease(model->buffers[i]); } - for (int i = 0; i < model->modelData->materialCount; i++) { - lovrRelease(model->materials[i]); + for (int i = 0; i < model->data->primitiveCount; i++) { + lovrRelease(model->meshes[i]); } - lovrRelease(model->animator); - lovrRelease(model->material); - free(model->textures); - free(model->materials); - lovrRelease(model->modelData); - lovrRelease(model->mesh); - free(model->nodeTransforms); + lovrRelease(model->data); + free(ref); } void lovrModelDraw(Model* model, mat4 transform, int instances) { - if (model->modelData->nodeCount == 0) { - return; - } - - if (model->animator) { - for (int i = 0; i < model->modelData->nodeCount; i++) { - ModelNode* node = &model->modelData->nodes[i]; - - float localTransform[16] = MAT4_IDENTITY; - if (!lovrAnimatorEvaluate(model->animator, node->name, localTransform)) { - mat4_set(localTransform, node->transform); - } - - mat4 globalTransform = model->nodeTransforms[i]; - if (node->parent >= 0) { - mat4_set(globalTransform, model->nodeTransforms[node->parent]); - mat4_multiply(globalTransform, localTransform); - } else { - mat4_set(globalTransform, localTransform); - } - } - } - - if (model->material) { - lovrMeshSetMaterial(model->mesh, model->material); - } - - lovrGraphicsPush(); - lovrGraphicsMatrixTransform(transform); - renderNode(model, 0, instances); - lovrGraphicsPop(); -} - -Animator* lovrModelGetAnimator(Model* model) { - return model->animator; -} - -void lovrModelSetAnimator(Model* model, Animator* animator) { - if (model->animator != animator) { - lovrRetain(animator); - lovrRelease(model->animator); - model->animator = animator; - } -} - -int lovrModelGetAnimationCount(Model* model) { - return model->modelData->animationCount; -} - -Material* lovrModelGetMaterial(Model* model) { - return model->material; -} - -void lovrModelSetMaterial(Model* model, Material* material) { - if (model->material != material) { - lovrRetain(material); - lovrRelease(model->material); - model->material = material; - } -} - -Mesh* lovrModelGetMesh(Model* model) { - return model->mesh; -} - -const float* lovrModelGetAABB(Model* model) { - if (model->aabbDirty) { - lovrModelDataGetAABB(model->modelData, model->aabb); - model->aabbDirty = false; - } - - return model->aabb; + // } diff --git a/src/graphics/model.h b/src/graphics/model.h index ddd97207..d9b14163 100644 --- a/src/graphics/model.h +++ b/src/graphics/model.h @@ -1,36 +1,18 @@ #include "data/modelData.h" -#include "graphics/animator.h" -#include "graphics/material.h" #include "graphics/mesh.h" -#include "graphics/texture.h" #include "lib/math.h" #include "util.h" -#include #pragma once typedef struct { Ref ref; - ModelData* modelData; - Texture** textures; - Material** materials; - Material* material; - Animator* animator; - Mesh* mesh; - float pose[MAX_BONES][16]; - float (*nodeTransforms)[16]; - float aabb[6]; - bool aabbDirty; + ModelData* data; + Buffer** buffers; + Mesh** meshes; } Model; -Model* lovrModelInit(Model* model, ModelData* modelData); +Model* lovrModelInit(Model* model, ModelData* data); #define lovrModelCreate(...) lovrModelInit(lovrAlloc(Model), __VA_ARGS__) void lovrModelDestroy(void* ref); void lovrModelDraw(Model* model, mat4 transform, int instances); -Animator* lovrModelGetAnimator(Model* model); -void lovrModelSetAnimator(Model* model, Animator* animator); -int lovrModelGetAnimationCount(Model* model); -Material* lovrModelGetMaterial(Model* model); -void lovrModelSetMaterial(Model* model, Material* material); -Mesh* lovrModelGetMesh(Model* model); -const float* lovrModelGetAABB(Model* model); diff --git a/src/graphics/opengl.c b/src/graphics/opengl.c index 7a2d1dfa..fc0aed12 100644 --- a/src/graphics/opengl.c +++ b/src/graphics/opengl.c @@ -196,6 +196,19 @@ static bool isTextureFormatDepth(TextureFormat format) { } } +static GLenum convertAttributeType(AttributeType type) { + switch (type) { + case I8: return GL_BYTE; + case U8: return GL_UNSIGNED_BYTE; + case I16: return GL_SHORT; + case U16: return GL_UNSIGNED_SHORT; + case I32: return GL_INT; + case U32: return GL_UNSIGNED_INT; + case F32: return GL_FLOAT; + default: lovrThrow("Unreachable"); + } +} + static GLenum convertBufferType(BufferType type) { switch (type) { case BUFFER_VERTEX: return GL_ARRAY_BUFFER; @@ -537,20 +550,13 @@ static void lovrGpuBindMesh(Mesh* mesh, Shader* shader, int divisorMultiplier) { int count = current.components; int stride = current.stride; GLvoid* offset = (GLvoid*) current.offset; + GLenum type = convertAttributeType(current.type); // TODO if (current.integer) { - switch (current.type) { - case ATTR_BYTE: glVertexAttribIPointer(i, count, GL_UNSIGNED_BYTE, stride, offset); break; - case ATTR_INT: glVertexAttribIPointer(i, count, GL_INT, stride, offset); break; - default: lovrThrow("Cannot use float data for int attribute"); - } + glVertexAttribIPointer(i, count, type, stride, offset); } else { - switch (current.type) { - case ATTR_FLOAT: glVertexAttribPointer(i, count, GL_FLOAT, GL_TRUE, stride, offset); break; - case ATTR_BYTE: glVertexAttribPointer(i, count, GL_UNSIGNED_BYTE, GL_TRUE, stride, offset); break; - case ATTR_INT: glVertexAttribPointer(i, count, GL_INT, GL_TRUE, stride, offset); break; - } + glVertexAttribPointer(i, count, type, GL_TRUE, stride, offset); } } } @@ -1973,7 +1979,6 @@ Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, VertexFormat format, Buffer* verte .stride = format.stride, .type = format.attributes[i].type, .components = format.attributes[i].count, - .integer = format.attributes[i].type == ATTR_INT, .enabled = true })); } @@ -1981,6 +1986,13 @@ Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, VertexFormat format, Buffer* verte return mesh; } +Mesh* lovrMeshInitEmpty(Mesh* mesh, DrawMode mode) { + mesh->mode = mode; + glGenVertexArrays(1, &mesh->vao); + map_init(&mesh->attributes); + return mesh; +} + void lovrMeshDestroy(void* ref) { Mesh* mesh = ref; lovrGraphicsFlushMesh(mesh); diff --git a/src/headset/openvr.c b/src/headset/openvr.c index c18b94f7..91c3318f 100644 --- a/src/headset/openvr.c +++ b/src/headset/openvr.c @@ -401,78 +401,7 @@ static ModelData* openvrControllerNewModelData(Controller* controller) { RenderModel_t* vrModel = state.deviceModels[id]; - ModelData* modelData = lovrAlloc(ModelData); - - VertexFormat format; - vertexFormatInit(&format); - vertexFormatAppend(&format, "lovrPosition", ATTR_FLOAT, 3); - vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3); - vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2); - - modelData->vertexData = lovrVertexDataCreate(vrModel->unVertexCount, &format); - - float* vertices = (float*) modelData->vertexData->blob.data; - int vertex = 0; - for (size_t i = 0; i < vrModel->unVertexCount; i++) { - float* position = vrModel->rVertexData[i].vPosition.v; - float* normal = vrModel->rVertexData[i].vNormal.v; - float* texCoords = vrModel->rVertexData[i].rfTextureCoord; - - vertices[vertex++] = position[0]; - vertices[vertex++] = position[1]; - vertices[vertex++] = position[2]; - - vertices[vertex++] = normal[0]; - vertices[vertex++] = normal[1]; - vertices[vertex++] = normal[2]; - - vertices[vertex++] = texCoords[0]; - vertices[vertex++] = texCoords[1]; - } - - modelData->indexCount = vrModel->unTriangleCount * 3; - modelData->indexSize = sizeof(uint16_t); - modelData->indices.raw = malloc(modelData->indexCount * modelData->indexSize); - memcpy(modelData->indices.raw, vrModel->rIndexData, modelData->indexCount * modelData->indexSize); - - modelData->nodeCount = 1; - modelData->primitiveCount = 1; - modelData->animationCount = 0; - modelData->materialCount = 1; - - modelData->nodes = calloc(1, sizeof(ModelNode)); - modelData->primitives = calloc(1, sizeof(ModelPrimitive)); - modelData->materials = calloc(1, sizeof(ModelMaterial)); - - // Nodes - ModelNode* root = &modelData->nodes[0]; - root->parent = -1; - vec_init(&root->children); - vec_init(&root->primitives); - vec_push(&root->primitives, 0); - mat4_identity(root->transform); - mat4_identity(root->globalTransform); - modelData->primitives[0].material = 0; - modelData->primitives[0].drawStart = 0; - modelData->primitives[0].drawCount = modelData->indexCount; - map_init(&modelData->primitives[0].boneMap); - map_init(&modelData->nodeMap); - - // Material - RenderModel_TextureMap_t* vrTexture = state.deviceTextures[id]; - TextureData* textureData = lovrTextureDataCreate(vrTexture->unWidth, vrTexture->unHeight, 0, FORMAT_RGBA); - memcpy(textureData->blob.data, vrTexture->rubTextureMapData, vrTexture->unWidth * vrTexture->unHeight * 4); - - vec_init(&modelData->textures); - vec_push(&modelData->textures, NULL); - vec_push(&modelData->textures, textureData); - - ModelMaterial* material = &modelData->materials[0]; - material->diffuseColor = (Color) { 1, 1, 1, 1 }; - material->emissiveColor = (Color) { 0, 0, 0, 1 }; - material->diffuseTexture = 1; - - return modelData; + return NULL; } static void openvrRenderTo(void (*callback)(void*), void* userdata) { diff --git a/src/lib/jsmn/LICENSE b/src/lib/jsmn/LICENSE new file mode 100644 index 00000000..19ea8424 --- /dev/null +++ b/src/lib/jsmn/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Serge A. Zaitsev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/lib/jsmn/jsmn.c b/src/lib/jsmn/jsmn.c new file mode 100644 index 00000000..564d9b66 --- /dev/null +++ b/src/lib/jsmn/jsmn.c @@ -0,0 +1,313 @@ +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} diff --git a/src/lib/jsmn/jsmn.h b/src/lib/jsmn/jsmn.h new file mode 100644 index 00000000..5a5200ee --- /dev/null +++ b/src/lib/jsmn/jsmn.h @@ -0,0 +1,76 @@ +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/src/resources/shaders.c b/src/resources/shaders.c index cf4c64ce..3111c51a 100644 --- a/src/resources/shaders.c +++ b/src/resources/shaders.c @@ -173,3 +173,13 @@ const char* lovrShaderTextureUniforms[] = { "lovrNormalTexture", "lovrEnvironmentTexture" }; + +const char* lovrShaderAttributeNames[] = { + "lovrPosition", + "lovrNormal", + "lovrTexCoord", + "lovrVertexColor", + "lovrTangent", + "lovrBones", + "lovrBoneWeights" +}; diff --git a/src/resources/shaders.h b/src/resources/shaders.h index 6d53a063..49de259f 100644 --- a/src/resources/shaders.h +++ b/src/resources/shaders.h @@ -17,3 +17,4 @@ extern const char* lovrFillVertexShader; extern const char* lovrShaderScalarUniforms[]; extern const char* lovrShaderColorUniforms[]; extern const char* lovrShaderTextureUniforms[]; +extern const char* lovrShaderAttributeNames[];