Use internal scene graph for models;

Allows for loading of more complicated models and multiple materials
for a single model.  Also fixes bugs with Mesh draw ranges.  Model:getAABB
is temporarily removed.
This commit is contained in:
bjorn 2017-10-14 18:01:00 -07:00
parent 7b20ad9650
commit f5e8535726
7 changed files with 181 additions and 243 deletions

View File

@ -23,19 +23,9 @@ int l_lovrModelSetTexture(lua_State* L) {
return 0;
}
int l_lovrModelGetAABB(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float* aabb = lovrModelGetAABB(model);
for (int i = 0; i < 6; i++) {
lua_pushnumber(L, aabb[i]);
}
return 6;
}
const luaL_Reg lovrModel[] = {
{ "draw", l_lovrModelDraw },
{ "getTexture", l_lovrModelGetTexture },
{ "setTexture", l_lovrModelSetTexture },
{ "getAABB", l_lovrModelGetAABB },
{ NULL, NULL }
};

View File

@ -119,8 +119,11 @@ void lovrMeshDraw(Mesh* mesh, mat4 transform) {
lovrMeshUnmap(mesh);
}
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
if (transform) {
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
}
lovrGraphicsBindTexture(mesh->texture);
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsPrepare();
@ -129,11 +132,15 @@ void lovrMeshDraw(Mesh* mesh, mat4 transform) {
size_t start = mesh->rangeStart;
size_t count = mesh->rangeCount;
if (mesh->map.length > 0) {
glDrawElements(mesh->drawMode, mesh->map.length, GL_UNSIGNED_INT, (GLvoid*) start);
count = mesh->isRangeEnabled ? mesh->rangeCount : mesh->map.length;
glDrawElements(mesh->drawMode, count, GL_UNSIGNED_INT, (GLvoid*) (start * sizeof(unsigned int)));
} else {
glDrawArrays(mesh->drawMode, start, count);
}
lovrGraphicsPop();
if (transform) {
lovrGraphicsPop();
}
}
MeshFormat lovrMeshGetVertexFormat(Mesh* mesh) {
@ -224,7 +231,9 @@ void lovrMeshGetDrawRange(Mesh* mesh, int* start, int* count) {
}
int lovrMeshSetDrawRange(Mesh* mesh, int start, int count) {
if (start < 0 || count < 0 || (size_t) start + count > mesh->count) {
size_t limit = mesh->map.length > 0 ? mesh->map.length : mesh->count;
if (start < 0 || count < 0 || (size_t) start + count > limit) {
return 1;
}

View File

@ -3,66 +3,24 @@
#include "math/mat4.h"
#include "math/vec3.h"
#include <stdlib.h>
#include <float.h>
static void visitNode(Model* model, ModelData* modelData, ModelNode* node, mat4 transform, vec_float_t* vertices, vec_uint_t* indices) {
float newTransform[16];
static void renderNode(Model* model, int nodeIndex) {
ModelNode* node = &model->modelData->nodes[nodeIndex];
if (transform) {
mat4_set(newTransform, transform);
} else {
mat4_identity(newTransform);
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, node->transform);
for (int i = 0; i < node->primitives.length; i++) {
ModelPrimitive* primitive = &model->modelData->primitives[node->primitives.data[i]];
lovrMeshSetDrawRange(model->mesh, primitive->drawStart, primitive->drawCount);
lovrMeshDraw(model->mesh, NULL);
}
mat4_multiply(newTransform, node->transform);
int indexOffset = vertices->length / 3;
// Meshes
for (int m = 0; m < node->meshes.length; m++) {
ModelMesh* mesh = modelData->meshes.data[node->meshes.data[m]];
// Transformed vertices
for (int v = 0; v < mesh->vertices.length; v++) {
ModelVertex vertex = mesh->vertices.data[v];
float vec[3] = { vertex.x, vertex.y, vertex.z };
mat4_transform(newTransform, vec);
vec_pusharr(vertices, vec, 3);
model->aabb[0] = MIN(model->aabb[0], vec[0]);
model->aabb[1] = MAX(model->aabb[1], vec[0]);
model->aabb[2] = MIN(model->aabb[2], vec[1]);
model->aabb[3] = MAX(model->aabb[3], vec[1]);
model->aabb[4] = MIN(model->aabb[4], vec[2]);
model->aabb[5] = MAX(model->aabb[5], vec[2]);
if (modelData->hasNormals) {
ModelVertex normal = mesh->normals.data[v];
vec_push(vertices, normal.x);
vec_push(vertices, normal.y);
vec_push(vertices, normal.z);
}
if (modelData->hasTexCoords) {
ModelVertex texCoord = mesh->texCoords.data[v];
vec_push(vertices, texCoord.x);
vec_push(vertices, texCoord.y);
}
}
// Face vertex indices
for (int f = 0; f < mesh->faces.length; f++) {
ModelFace face = mesh->faces.data[f];
vec_push(indices, face.indices[0] + indexOffset);
vec_push(indices, face.indices[1] + indexOffset);
vec_push(indices, face.indices[2] + indexOffset);
}
for (int i = 0; i < node->children.length; i++) {
renderNode(model, node->children.data[i]);
}
for (int c = 0; c < node->children.length; c++) {
visitNode(model, modelData, node->children.data[c], newTransform, vertices, indices);
}
lovrGraphicsPop();
}
Model* lovrModelCreate(ModelData* modelData) {
@ -70,51 +28,33 @@ Model* lovrModelCreate(ModelData* modelData) {
if (!model) return NULL;
model->modelData = modelData;
model->aabb[0] = FLT_MAX;
model->aabb[1] = FLT_MIN;
model->aabb[2] = FLT_MAX;
model->aabb[3] = FLT_MIN;
model->aabb[4] = FLT_MAX;
model->aabb[5] = FLT_MIN;
vec_float_t vertices;
vec_init(&vertices);
vec_uint_t indices;
vec_init(&indices);
visitNode(model, modelData, modelData->root, NULL, &vertices, &indices);
MeshFormat format;
vec_init(&format);
int components = 3;
MeshAttribute position = { .name = "lovrPosition", .type = MESH_FLOAT, .count = 3 };
vec_push(&format, position);
MeshAttribute attribute = { .name = "lovrPosition", .type = MESH_FLOAT, .count = 3 };
vec_push(&format, attribute);
if (modelData->hasNormals) {
MeshAttribute normal = { .name = "lovrNormal", .type = MESH_FLOAT, .count = 3 };
vec_push(&format, normal);
components += 3;
MeshAttribute attribute = { .name = "lovrNormal", .type = MESH_FLOAT, .count = 3 };
vec_push(&format, attribute);
}
if (modelData->hasTexCoords) {
MeshAttribute texCoord = { .name = "lovrTexCoord", .type = MESH_FLOAT, .count = 2 };
vec_push(&format, texCoord);
components += 2;
if (modelData->hasUVs) {
MeshAttribute attribute = { .name = "lovrTexCoord", .type = MESH_FLOAT, .count = 2 };
vec_push(&format, attribute);
}
model->mesh = lovrMeshCreate(vertices.length / components, &format, MESH_TRIANGLES, MESH_STATIC);
void* data = lovrMeshMap(model->mesh, 0, vertices.length / components, 0, 1);
memcpy(data, vertices.data, vertices.length * sizeof(float));
model->mesh = lovrMeshCreate(modelData->vertexCount, &format, MESH_TRIANGLES, MESH_STATIC);
void* data = lovrMeshMap(model->mesh, 0, modelData->vertexCount, 0, 1);
memcpy(data, modelData->vertices, modelData->vertexCount * modelData->vertexSize * sizeof(float));
lovrMeshUnmap(model->mesh);
lovrMeshSetVertexMap(model->mesh, indices.data, indices.length);
lovrMeshSetVertexMap(model->mesh, modelData->indices, modelData->indexCount);
lovrMeshSetRangeEnabled(model->mesh, 1);
model->texture = NULL;
vec_deinit(&format);
vec_deinit(&vertices);
vec_deinit(&indices);
return model;
}
@ -129,7 +69,14 @@ void lovrModelDestroy(const Ref* ref) {
}
void lovrModelDraw(Model* model, mat4 transform) {
lovrMeshDraw(model->mesh, transform);
if (model->modelData->nodeCount == 0) {
return;
}
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
renderNode(model, 0);
lovrGraphicsPop();
}
Texture* lovrModelGetTexture(Model* model) {
@ -148,7 +95,3 @@ void lovrModelSetTexture(Model* model, Texture* texture) {
lovrRetain(&model->texture->ref);
}
}
float* lovrModelGetAABB(Model* model) {
return model->aabb;
}

View File

@ -12,7 +12,6 @@ typedef struct {
ModelData* modelData;
Mesh* mesh;
Texture* texture;
float aabb[6];
} Model;
Model* lovrModelCreate(ModelData* modelData);
@ -20,4 +19,3 @@ void lovrModelDestroy(const Ref* ref);
void lovrModelDraw(Model* model, mat4 transform);
Texture* lovrModelGetTexture(Model* model);
void lovrModelSetTexture(Model* model, Texture* texture);
float* lovrModelGetAABB(Model* model);

View File

@ -600,53 +600,49 @@ ModelData* lovrHeadsetControllerNewModelData(Controller* controller) {
ModelData* modelData = malloc(sizeof(ModelData));
if (!modelData) return NULL;
ModelMesh* mesh = malloc(sizeof(ModelMesh));
vec_init(&modelData->meshes);
vec_push(&modelData->meshes, mesh);
modelData->indexCount = vrModel->unTriangleCount;
modelData->indices = malloc(modelData->indexCount * sizeof(unsigned int));
memcpy(modelData->indices, vrModel->rIndexData, modelData->indexCount * sizeof(unsigned int));
vec_init(&mesh->faces);
for (uint32_t i = 0; i < vrModel->unTriangleCount; i++) {
ModelFace face;
face.indices[0] = vrModel->rIndexData[3 * i + 0];
face.indices[1] = vrModel->rIndexData[3 * i + 1];
face.indices[2] = vrModel->rIndexData[3 * i + 2];
vec_push(&mesh->faces, face);
}
modelData->vertexCount = vrModel->unVertexCount;
modelData->vertexSize = 8;
modelData->vertices = malloc(modelData->vertexCount * modelData->vertexSize * sizeof(float));
vec_init(&mesh->vertices);
vec_init(&mesh->normals);
vec_init(&mesh->texCoords);
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;
ModelVertex v;
v.x = position[0];
v.y = position[1];
v.z = position[2];
vec_push(&mesh->vertices, v);
modelData->vertices[vertex++] = position[0];
modelData->vertices[vertex++] = position[1];
modelData->vertices[vertex++] = position[2];
v.x = normal[0];
v.y = normal[1];
v.z = normal[2];
vec_push(&mesh->normals, v);
modelData->vertices[vertex++] = normal[0];
modelData->vertices[vertex++] = normal[1];
modelData->vertices[vertex++] = normal[2];
v.x = texCoords[0];
v.y = texCoords[1];
v.z = 0.f;
vec_push(&mesh->texCoords, v);
modelData->vertices[vertex++] = texCoords[0];
modelData->vertices[vertex++] = texCoords[1];
}
ModelNode* root = malloc(sizeof(ModelNode));
vec_init(&root->meshes);
vec_push(&root->meshes, 0);
vec_init(&root->children);
mat4_identity(root->transform);
modelData->nodeCount = 1;
modelData->primitiveCount = 1;
modelData->nodes = malloc(1 * sizeof(ModelNode));
modelData->primitives = malloc(1 * sizeof(ModelPrimitive));
ModelNode* root = &modelData->nodes[0];
root->parent = -1;
mat4_identity(root->transform);
vec_init(&root->children);
vec_init(&root->primitives);
vec_push(&root->primitives, 0);
modelData->primitives[0].drawStart = 0;
modelData->primitives[0].drawCount = modelData->vertexCount;
modelData->root = root;
modelData->hasNormals = 1;
modelData->hasTexCoords = 1;
modelData->hasUVs = 1;
return modelData;
}

View File

@ -2,29 +2,41 @@
#include "math/mat4.h"
#include <stdlib.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>
static void assimpNodeTraversal(ModelNode* node, struct aiNode* assimpNode) {
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];
// Transform
struct aiMatrix4x4 m = assimpNode->mTransformation;
aiTransposeMatrix4(&m);
mat4_set(node->transform, (float*) &m);
// Meshes
vec_init(&node->meshes);
vec_pusharr(&node->meshes, assimpNode->mMeshes, assimpNode->mNumMeshes);
// 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++) {
ModelNode* child = malloc(sizeof(ModelNode));
assimpNodeTraversal(child, assimpNode->mChildren[n]);
vec_push(&node->children, child);
(*nodeId)++;
vec_push(&node->children, *nodeId);
ModelNode* child = &modelData->nodes[*nodeId];
child->parent = currentIndex;
assimpNodeTraversal(modelData, assimpNode->mChildren[n], nodeId);
}
}
@ -32,104 +44,104 @@ ModelData* lovrModelDataCreate(Blob* blob) {
ModelData* modelData = malloc(sizeof(ModelData));
if (!modelData) return NULL;
modelData->hasNormals = 0;
modelData->hasTexCoords = 0;
struct aiPropertyStore* propertyStore = aiCreatePropertyStore();
aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_OptimizeGraph | aiProcess_FlipUVs;
const struct aiScene* scene = aiImportFileFromMemory(blob->data, blob->size, flags, NULL);
const struct aiScene* scene = aiImportFileFromMemoryWithProperties(blob->data, blob->size, flags, NULL, propertyStore);
aiReleasePropertyStore(propertyStore);
modelData->nodeCount = 0;
modelData->vertexCount = 0;
modelData->indexCount = 0;
modelData->hasNormals = 0;
modelData->hasUVs = 0;
// Meshes
vec_init(&modelData->meshes);
for (unsigned int m = 0; m < scene->mNumMeshes; m++) {
struct aiMesh* assimpMesh = scene->mMeshes[m];
ModelMesh* mesh = malloc(sizeof(ModelMesh));
vec_push(&modelData->meshes, mesh);
modelData->vertexCount += assimpMesh->mNumVertices;
modelData->indexCount += assimpMesh->mNumFaces * 3;
modelData->hasNormals |= assimpMesh->mNormals != NULL;
modelData->hasUVs |= assimpMesh->mTextureCoords[0] != NULL;
}
// Faces
vec_init(&mesh->faces);
// Allocate
modelData->primitiveCount = scene->mNumMeshes;
modelData->primitives = malloc(modelData->primitiveCount * sizeof(ModelPrimitive));
modelData->vertexSize = 3 + (modelData->hasNormals ? 3 : 0) + (modelData->hasUVs ? 2 : 0);
modelData->vertices = malloc(modelData->vertexSize * modelData->vertexCount * sizeof(float));
modelData->indices = malloc(modelData->indexCount * sizeof(uint32_t));
// Load
int vertex = 0;
int index = 0;
for (unsigned int m = 0; m < scene->mNumMeshes; m++) {
struct aiMesh* assimpMesh = scene->mMeshes[m];
modelData->primitives[m].drawStart = index;
modelData->primitives[m].drawCount = 0;
// 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");
// Skip lines and points, polygons are triangulated
if (assimpFace.mNumIndices != 3) {
continue;
}
modelData->primitives[m].drawCount += assimpFace.mNumIndices;
ModelFace face;
for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) {
face.indices[i] = assimpFace.mIndices[i];
modelData->indices[index++] = (vertex / modelData->vertexSize) + assimpFace.mIndices[i];
}
vec_push(&mesh->faces, face);
}
// Vertices
vec_init(&mesh->vertices);
for (unsigned int v = 0; v < assimpMesh->mNumVertices; v++) {
ModelVertex vertex;
vertex.x = assimpMesh->mVertices[v].x;
vertex.y = assimpMesh->mVertices[v].y;
vertex.z = assimpMesh->mVertices[v].z;
vec_push(&mesh->vertices, vertex);
}
modelData->vertices[vertex++] = assimpMesh->mVertices[v].x;
modelData->vertices[vertex++] = assimpMesh->mVertices[v].y;
modelData->vertices[vertex++] = assimpMesh->mVertices[v].z;
// Normals
lovrAssert(assimpMesh->mNormals, "Model must have normals");
if (modelData->hasNormals) {
if (assimpMesh->mNormals) {
modelData->vertices[vertex++] = assimpMesh->mNormals[v].x;
modelData->vertices[vertex++] = assimpMesh->mNormals[v].y;
modelData->vertices[vertex++] = assimpMesh->mNormals[v].z;
} else {
modelData->vertices[vertex++] = 0;
modelData->vertices[vertex++] = 0;
modelData->vertices[vertex++] = 0;
}
}
modelData->hasNormals = 1;
vec_init(&mesh->normals);
for (unsigned int n = 0; n < assimpMesh->mNumVertices; n++) {
ModelVertex normal;
normal.x = assimpMesh->mNormals[n].x;
normal.y = assimpMesh->mNormals[n].y;
normal.z = assimpMesh->mNormals[n].z;
vec_push(&mesh->normals, normal);
}
modelData->hasTexCoords = modelData->hasTexCoords || assimpMesh->mTextureCoords[0] != NULL;
if (assimpMesh->mTextureCoords[0]) {
vec_init(&mesh->texCoords);
for (unsigned int i = 0; i < assimpMesh->mNumVertices; i++) {
ModelVertex texCoord;
texCoord.x = assimpMesh->mTextureCoords[0][i].x;
texCoord.y = assimpMesh->mTextureCoords[0][i].y;
vec_push(&mesh->texCoords, texCoord);
if (modelData->hasUVs) {
if (assimpMesh->mTextureCoords[0]) {
modelData->vertices[vertex++] = assimpMesh->mTextureCoords[0][v].x;
modelData->vertices[vertex++] = assimpMesh->mTextureCoords[0][v].y;
} else {
modelData->vertices[vertex++] = 0;
modelData->vertices[vertex++] = 0;
}
}
}
}
// Nodes
modelData->root = malloc(sizeof(ModelNode));
assimpNodeTraversal(modelData->root, scene->mRootNode);
modelData->nodeCount = 0;
assimpSumChildren(scene->mRootNode, &modelData->nodeCount);
modelData->nodes = malloc(modelData->nodeCount * sizeof(ModelNode));
modelData->nodes[0].parent = -1;
int nodeIndex = 0;
assimpNodeTraversal(modelData, scene->mRootNode, &nodeIndex);
aiReleaseImport(scene);
return modelData;
}
void lovrModelDataDestroy(ModelData* modelData) {
for (int i = 0; i < modelData->meshes.length; i++) {
ModelMesh* mesh = modelData->meshes.data[i];
vec_deinit(&mesh->faces);
vec_deinit(&mesh->vertices);
vec_deinit(&mesh->normals);
if (modelData->hasTexCoords) {
vec_deinit(&mesh->texCoords);
}
free(mesh);
for (int i = 0; i < modelData->nodeCount; i++) {
vec_deinit(&modelData->nodes[i].children);
vec_deinit(&modelData->nodes[i].primitives);
}
vec_void_t nodes;
vec_init(&nodes);
vec_push(&nodes, modelData->root);
while (nodes.length > 0) {
ModelNode* node = vec_first(&nodes);
vec_extend(&nodes, &node->children);
vec_deinit(&node->meshes);
vec_deinit(&node->children);
vec_splice(&nodes, 0, 1);
free(node);
}
vec_deinit(&modelData->meshes);
vec_deinit(&nodes);
free(modelData->nodes);
free(modelData->primitives);
free(modelData->vertices);
free(modelData->indices);
free(modelData);
}

View File

@ -5,39 +5,29 @@
#pragma once
typedef struct {
float x;
float y;
float z;
} ModelVertex;
int drawStart;
int drawCount;
} ModelPrimitive;
typedef vec_t(ModelVertex) vec_model_vertex_t;
typedef struct {
unsigned int indices[3];
} ModelFace;
typedef vec_t(ModelFace) vec_model_face_t;
typedef struct {
vec_model_face_t faces;
vec_model_vertex_t vertices;
vec_model_vertex_t normals;
vec_model_vertex_t texCoords;
} ModelMesh;
typedef vec_t(ModelMesh*) vec_model_mesh_t;
typedef struct {
typedef struct ModelNode {
float transform[16];
vec_uint_t meshes;
vec_void_t children;
int parent;
vec_uint_t children;
vec_uint_t primitives;
} ModelNode;
typedef struct {
ModelNode* root;
vec_model_mesh_t meshes;
ModelNode* nodes;
ModelPrimitive* primitives;
float* vertices;
uint32_t* indices;
int nodeCount;
int primitiveCount;
int vertexCount;
int vertexSize;
int indexCount;
int hasNormals;
int hasTexCoords;
int hasUVs;
} ModelData;
ModelData* lovrModelDataCreate(Blob* blob);