From 5f19c2e1be8c60d0aebfcd144a4193981d52ef2a Mon Sep 17 00:00:00 2001 From: bjorn Date: Wed, 6 Jul 2022 19:22:02 -0700 Subject: [PATCH] Model:getBoundingBox; Model:getBoundingSphere; --- src/api/l_graphics_model.c | 40 ++++--- src/modules/data/modelData.c | 195 ++++++++++++++++++++++++++++++++ src/modules/data/modelData.h | 5 + src/modules/graphics/graphics.c | 4 +- src/modules/graphics/graphics.h | 2 +- 5 files changed, 227 insertions(+), 19 deletions(-) diff --git a/src/api/l_graphics_model.c b/src/api/l_graphics_model.c index e4be8c57..7f21e514 100644 --- a/src/api/l_graphics_model.c +++ b/src/api/l_graphics_model.c @@ -11,8 +11,8 @@ static uint32_t luax_checkanimation(lua_State* L, int index, Model* model) { case LUA_TSTRING: { size_t length; const char* name = lua_tolstring(L, index, &length); - ModelData* modelData = lovrModelGetModelData(model); - uint64_t animationIndex = map_get(&modelData->animationMap, hash64(name, length)); + ModelData* data = lovrModelGetInfo(model)->data; + uint64_t animationIndex = map_get(&data->animationMap, hash64(name, length)); lovrCheck(animationIndex != MAP_NIL, "ModelData has no animation named '%s'", name); return (uint32_t) animationIndex; } @@ -26,8 +26,8 @@ uint32_t luax_checknodeindex(lua_State* L, int index, Model* model) { case LUA_TSTRING: { size_t length; const char* name = lua_tolstring(L, index, &length); - ModelData* modelData = lovrModelGetModelData(model); - uint64_t nodeIndex = map_get(&modelData->nodeMap, hash64(name, length)); + ModelData* data = lovrModelGetInfo(model)->data; + uint64_t nodeIndex = map_get(&data->nodeMap, hash64(name, length)); lovrCheck(nodeIndex != MAP_NIL, "ModelData has no node named '%s'", name); return (uint32_t) nodeIndex; } @@ -38,7 +38,8 @@ uint32_t luax_checknodeindex(lua_State* L, int index, Model* model) { static int l_lovrModelGetModelData(lua_State* L) { Model* model = luax_checktype(L, 1, Model); - luax_pushtype(L, ModelData, lovrModelGetModelData(model)); + ModelData* data = lovrModelGetInfo(model)->data; + luax_pushtype(L, ModelData, data); return 1; } @@ -153,35 +154,40 @@ static int l_lovrModelGetVertexCount(lua_State* L) { lua_pushinteger(L, count); return 1; } +*/ static int l_lovrModelGetWidth(lua_State* L) { Model* model = luax_checktype(L, 1, Model); + ModelData* data = lovrModelGetInfo(model)->data; float bounds[6]; - lovrModelGetBoundingBox(model, bounds); + lovrModelDataGetBoundingBox(data, bounds); lua_pushnumber(L, bounds[1] - bounds[0]); return 1; } static int l_lovrModelGetHeight(lua_State* L) { Model* model = luax_checktype(L, 1, Model); + ModelData* data = lovrModelGetInfo(model)->data; float bounds[6]; - lovrModelGetBoundingBox(model, bounds); + lovrModelDataGetBoundingBox(data, bounds); lua_pushnumber(L, bounds[3] - bounds[2]); return 1; } static int l_lovrModelGetDepth(lua_State* L) { Model* model = luax_checktype(L, 1, Model); + ModelData* data = lovrModelGetInfo(model)->data; float bounds[6]; - lovrModelGetBoundingBox(model, bounds); + lovrModelDataGetBoundingBox(data, bounds); lua_pushnumber(L, bounds[5] - bounds[4]); return 1; } static int l_lovrModelGetDimensions(lua_State* L) { Model* model = luax_checktype(L, 1, Model); + ModelData* data = lovrModelGetInfo(model)->data; float bounds[6]; - lovrModelGetBoundingBox(model, bounds); + lovrModelDataGetBoundingBox(data, bounds); lua_pushnumber(L, bounds[1] - bounds[0]); lua_pushnumber(L, bounds[3] - bounds[2]); lua_pushnumber(L, bounds[5] - bounds[4]); @@ -190,18 +196,20 @@ static int l_lovrModelGetDimensions(lua_State* L) { static int l_lovrModelGetCenter(lua_State* L) { Model* model = luax_checktype(L, 1, Model); + ModelData* data = lovrModelGetInfo(model)->data; float bounds[6]; - lovrModelGetBoundingBox(model, bounds); + lovrModelDataGetBoundingBox(data, bounds); lua_pushnumber(L, (bounds[0] + bounds[1]) / 2.f); lua_pushnumber(L, (bounds[2] + bounds[3]) / 2.f); lua_pushnumber(L, (bounds[4] + bounds[5]) / 2.f); - return 1; + return 3; } static int l_lovrModelGetBoundingBox(lua_State* L) { Model* model = luax_checktype(L, 1, Model); + ModelData* data = lovrModelGetInfo(model)->data; float bounds[6]; - lovrModelGetBoundingBox(model, bounds); + lovrModelDataGetBoundingBox(data, bounds); lua_pushnumber(L, bounds[0]); lua_pushnumber(L, bounds[1]); lua_pushnumber(L, bounds[2]); @@ -213,15 +221,15 @@ static int l_lovrModelGetBoundingBox(lua_State* L) { static int l_lovrModelGetBoundingSphere(lua_State* L) { Model* model = luax_checktype(L, 1, Model); + ModelData* data = lovrModelGetInfo(model)->data; float sphere[4]; - lovrModelGetBoundingSphere(model, sphere); + lovrModelDataGetBoundingSphere(data, sphere); lua_pushnumber(L, sphere[0]); lua_pushnumber(L, sphere[1]); lua_pushnumber(L, sphere[2]); lua_pushnumber(L, sphere[3]); return 4; } -*/ const luaL_Reg lovrModel[] = { { "getModelData", l_lovrModelGetModelData }, @@ -235,13 +243,13 @@ const luaL_Reg lovrModel[] = { { "getIndexBuffer", l_lovrModelGetIndexBuffer }, /*{ "getTriangles", l_lovrModelGetTriangles }, { "getTriangleCount", l_lovrModelGetTriangleCount }, - { "getVertexCount", l_lovrModelGetVertexCount }, + { "getVertexCount", l_lovrModelGetVertexCount },*/ { "getWidth", l_lovrModelGetWidth }, { "getHeight", l_lovrModelGetHeight }, { "getDepth", l_lovrModelGetDepth }, { "getDimensions", l_lovrModelGetDimensions }, { "getCenter", l_lovrModelGetCenter }, { "getBoundingBox", l_lovrModelGetBoundingBox }, - { "getBoundingSphere", l_lovrModelGetBoundingSphere },*/ + { "getBoundingSphere", l_lovrModelGetBoundingSphere }, { NULL, NULL } }; diff --git a/src/modules/data/modelData.c b/src/modules/data/modelData.c index 900ee06b..92fa1782 100644 --- a/src/modules/data/modelData.c +++ b/src/modules/data/modelData.c @@ -1,6 +1,7 @@ #include "data/modelData.h" #include "data/blob.h" #include "data/image.h" +#include "core/maf.h" #include #include @@ -207,3 +208,197 @@ void lovrModelDataCopyAttribute(ModelData* data, ModelAttribute* attribute, char lovrUnreachable(); } } + +static void boundingBoxHelper(ModelData* model, uint32_t nodeIndex, float* parentTransform) { + ModelNode* node = &model->nodes[nodeIndex]; + + float m[16]; + mat4_init(m, parentTransform); + + if (node->matrix) { + mat4_mul(m, node->transform.matrix); + } else { + float* T = node->transform.properties.translation; + float* R = node->transform.properties.rotation; + float* S = node->transform.properties.scale; + mat4_translate(m, T[0], T[1], T[2]); + mat4_rotateQuat(m, R); + mat4_scale(m, S[0], S[1], S[2]); + } + + for (uint32_t i = 0; i < node->primitiveCount; i++) { + ModelAttribute* position = model->primitives[node->primitiveIndex + i].attributes[ATTR_POSITION]; + + if (!position || !position->hasMin || !position->hasMax) { + continue; + } + + float xa[3] = { position->min[0] * m[0], position->min[0] * m[1], position->min[0] * m[2] }; + float xb[3] = { position->max[0] * m[0], position->max[0] * m[1], position->max[0] * m[2] }; + + float ya[3] = { position->min[1] * m[4], position->min[1] * m[5], position->min[1] * m[6] }; + float yb[3] = { position->max[1] * m[4], position->max[1] * m[5], position->max[1] * m[6] }; + + float za[3] = { position->min[2] * m[8], position->min[2] * m[9], position->min[2] * m[10] }; + float zb[3] = { position->max[2] * m[8], position->max[2] * m[9], position->max[2] * m[10] }; + + float min[3] = { + MIN(xa[0], xb[0]) + MIN(ya[0], yb[0]) + MIN(za[0], zb[0]) + m[12], + MIN(xa[1], xb[1]) + MIN(ya[1], yb[1]) + MIN(za[1], zb[1]) + m[13], + MIN(xa[2], xb[2]) + MIN(ya[2], yb[2]) + MIN(za[2], zb[2]) + m[14] + }; + + float max[3] = { + MAX(xa[0], xb[0]) + MAX(ya[0], yb[0]) + MAX(za[0], zb[0]) + m[12], + MAX(xa[1], xb[1]) + MAX(ya[1], yb[1]) + MAX(za[1], zb[1]) + m[13], + MAX(xa[2], xb[2]) + MAX(ya[2], yb[2]) + MAX(za[2], zb[2]) + m[14] + }; + + model->boundingBox[0] = MIN(model->boundingBox[0], min[0]); + model->boundingBox[1] = MAX(model->boundingBox[1], max[0]); + model->boundingBox[2] = MIN(model->boundingBox[2], min[1]); + model->boundingBox[3] = MAX(model->boundingBox[3], max[1]); + model->boundingBox[4] = MIN(model->boundingBox[4], min[2]); + model->boundingBox[5] = MAX(model->boundingBox[5], max[2]); + } + + for (uint32_t i = 0; i < node->childCount; i++) { + boundingBoxHelper(model, node->children[i], m); + } +} + +void lovrModelDataGetBoundingBox(ModelData* model, float box[6]) { + if (model->boundingBox[1] - model->boundingBox[0] == 0.f) { + boundingBoxHelper(model, model->rootNode, (float[16]) MAT4_IDENTITY); + } + + memcpy(box, model->boundingBox, sizeof(model->boundingBox)); +} + +static void boundingSphereHelper(ModelData* model, uint32_t nodeIndex, uint32_t* pointIndex, float* points, float* parentTransform) { + ModelNode* node = &model->nodes[nodeIndex]; + + float m[16]; + mat4_init(m, parentTransform); + + if (node->matrix) { + mat4_mul(m, node->transform.matrix); + } else { + float* T = node->transform.properties.translation; + float* R = node->transform.properties.rotation; + float* S = node->transform.properties.scale; + mat4_translate(m, T[0], T[1], T[2]); + mat4_rotateQuat(m, R); + mat4_scale(m, S[0], S[1], S[2]); + } + + for (uint32_t i = 0; i < node->primitiveCount; i++) { + ModelAttribute* position = model->primitives[node->primitiveIndex + i].attributes[ATTR_POSITION]; + + if (!position || !position->hasMin || !position->hasMax) { + continue; + } + + float* min = position->min; + float* max = position->max; + + float corners[8][4] = { + { min[0], min[1], min[2], 1.f }, + { min[0], min[1], max[2], 1.f }, + { min[0], max[1], min[2], 1.f }, + { min[0], max[1], max[2], 1.f }, + { max[0], min[1], min[2], 1.f }, + { max[0], min[1], max[2], 1.f }, + { max[0], max[1], min[2], 1.f }, + { max[0], max[1], max[2], 1.f } + }; + + for (uint32_t j = 0; j < 8; j++) { + mat4_transform(m, corners[j]); + memcpy(points + 3 * (*pointIndex)++, corners[j], 3 * sizeof(float)); + } + } + + for (uint32_t i = 0; i < node->childCount; i++) { + boundingSphereHelper(model, node->children[i], pointIndex, points, m); + } +} + +void lovrModelDataGetBoundingSphere(ModelData* model, float sphere[4]) { + if (model->boundingSphere[3] == 0.f) { + uint32_t totalPrimitiveCount = 0; + + for (uint32_t i = 0; i < model->nodeCount; i++) { + totalPrimitiveCount += model->nodes[i].primitiveCount; + } + + uint32_t pointCount = totalPrimitiveCount * 8; + float* points = malloc(pointCount * 3 * sizeof(float)); + lovrAssert(points, "Out of memory"); + + uint32_t pointIndex = 0; + boundingSphereHelper(model, model->rootNode, &pointIndex, points, (float[16]) MAT4_IDENTITY); + + // Find point furthest away from first point + + float max = 0.f; + float* a = NULL; + for (uint32_t i = 1; i < pointCount; i++) { + float dx = points[3 * i + 0] - points[0]; + float dy = points[3 * i + 1] - points[1]; + float dz = points[3 * i + 2] - points[2]; + float d2 = dx * dx + dy * dy + dz * dz; + + if (d2 > max) { + a = &points[3 * i]; + max = d2; + } + } + + // Find point furthest away from that point + + max = 0.f; + float* b = NULL; + for (uint32_t i = 0; i < pointCount; i++) { + float dx = points[3 * i + 0] - a[0]; + float dy = points[3 * i + 1] - a[1]; + float dz = points[3 * i + 2] - a[2]; + float d2 = dx * dx + dy * dy + dz * dz; + + if (d2 > max) { + b = &points[3 * i]; + max = d2; + } + } + + // Create and refine sphere + + float dx = a[0] - b[0]; + float dy = a[1] - b[1]; + float dz = a[2] - b[2]; + float x = (a[0] + b[0]) / 2.f; + float y = (a[1] + b[1]) / 2.f; + float z = (a[2] + b[2]) / 2.f; + float r = sqrtf(dx * dx + dy * dy + dz * dz) / 2.f; + float r2 = r * r; + + for (uint32_t i = 0; i < pointCount; i++) { + float dx = points[3 * i + 0] - x; + float dy = points[3 * i + 1] - y; + float dz = points[3 * i + 2] - z; + float d2 = dx * dx + dy * dy + dz * dz; + if (d2 > r2) { + r = sqrtf(d2); + r2 = r * r; + } + } + + model->boundingSphere[0] = x; + model->boundingSphere[1] = y; + model->boundingSphere[2] = z; + model->boundingSphere[3] = r; + free(points); + } + + memcpy(sphere, model->boundingSphere, sizeof(model->boundingSphere)); +} diff --git a/src/modules/data/modelData.h b/src/modules/data/modelData.h index e9c7f1f0..739ecd2e 100644 --- a/src/modules/data/modelData.h +++ b/src/modules/data/modelData.h @@ -214,6 +214,9 @@ typedef struct ModelData { uint32_t indexCount; AttributeType indexType; + float boundingBox[6]; + float boundingSphere[4]; + map_t animationMap; map_t materialMap; map_t nodeMap; @@ -228,3 +231,5 @@ ModelData* lovrModelDataInitStl(ModelData* model, struct Blob* blob, ModelDataIO void lovrModelDataDestroy(void* ref); void lovrModelDataAllocate(ModelData* model); void lovrModelDataCopyAttribute(ModelData* data, ModelAttribute* attribute, char* dst, AttributeType type, uint32_t components, bool normalized, uint32_t count, size_t stride, uint8_t clear); +void lovrModelDataGetBoundingBox(ModelData* data, float box[6]); +void lovrModelDataGetBoundingSphere(ModelData* data, float sphere[4]); diff --git a/src/modules/graphics/graphics.c b/src/modules/graphics/graphics.c index 83032307..de1e9de6 100644 --- a/src/modules/graphics/graphics.c +++ b/src/modules/graphics/graphics.c @@ -2423,8 +2423,8 @@ void lovrModelDestroy(void* ref) { free(model); } -ModelData* lovrModelGetModelData(Model* model) { - return model->info.data; +const ModelInfo* lovrModelGetInfo(Model* model) { + return &model->info; } void lovrModelResetPose(Model* model) { diff --git a/src/modules/graphics/graphics.h b/src/modules/graphics/graphics.h index 83ce3c56..1d19fe55 100644 --- a/src/modules/graphics/graphics.h +++ b/src/modules/graphics/graphics.h @@ -378,7 +378,7 @@ typedef enum { Model* lovrModelCreate(ModelInfo* info); void lovrModelDestroy(void* ref); -struct ModelData* lovrModelGetModelData(Model* model); +const ModelInfo* lovrModelGetInfo(Model* model); void lovrModelResetPose(Model* model); void lovrModelAnimate(Model* model, uint32_t animationIndex, float time, float alpha); void lovrModelGetNodePose(Model* model, uint32_t node, float position[4], float rotation[4], CoordinateSpace space);