Start parsing glTF;

This commit is contained in:
bjorn 2018-09-25 17:10:09 -07:00 committed by Bjorn Swenson
parent 2eb23864b6
commit 6b323e3476
24 changed files with 1075 additions and 1334 deletions

View File

@ -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)

View File

@ -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);

View File

@ -2,11 +2,11 @@
#include "data/vertexData.h"
#include <stdbool.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);
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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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 }
};

View File

@ -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 }
};

View File

@ -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);

View File

@ -1,600 +1,432 @@
#include "data/modelData.h"
#include "filesystem/filesystem.h"
#include "filesystem/file.h"
#include "lib/math.h"
#include <float.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "lib/jsmn/jsmn.h"
#include <stdbool.h>
#ifdef LOVR_USE_ASSIMP
#include <assimp/cfileio.h>
#include <assimp/cimport.h>
#include <assimp/config.h>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/matrix4x4.h>
#include <assimp/vector3.h>
#include <assimp/postprocess.h>
// 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);
//
}

View File

@ -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]);

View File

@ -1,7 +1,15 @@
#include "data/vertexData.h"
#include <string.h>
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;

View File

@ -1,15 +1,10 @@
#include "blob.h"
#include "data/modelData.h"
#include <stdlib.h>
#include <stdint.h>
#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;

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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 <stdlib.h>
#include <string.h>
#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;
//
}

View File

@ -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 <stdbool.h>
#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);

View File

@ -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);

View File

@ -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) {

19
src/lib/jsmn/LICENSE Normal file
View File

@ -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.

313
src/lib/jsmn/jsmn.c Normal file
View File

@ -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;
}

76
src/lib/jsmn/jsmn.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef __JSMN_H_
#define __JSMN_H_
#include <stddef.h>
#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_ */

View File

@ -173,3 +173,13 @@ const char* lovrShaderTextureUniforms[] = {
"lovrNormalTexture",
"lovrEnvironmentTexture"
};
const char* lovrShaderAttributeNames[] = {
"lovrPosition",
"lovrNormal",
"lovrTexCoord",
"lovrVertexColor",
"lovrTangent",
"lovrBones",
"lovrBoneWeights"
};

View File

@ -17,3 +17,4 @@ extern const char* lovrFillVertexShader;
extern const char* lovrShaderScalarUniforms[];
extern const char* lovrShaderColorUniforms[];
extern const char* lovrShaderTextureUniforms[];
extern const char* lovrShaderAttributeNames[];