Tidy up GLB loading a little

This commit is contained in:
Camden Dixie O'Brien committed 2025-10-01 10:50:30 +01:00
1 parent 6454098c6a
commit 955f606fb5
1 file changed
+102 -76
+102 -76
View File
@@ -27,7 +27,7 @@ static_assert(
#define MAGIC 0x46546c67 // "glTF"
#define VERSION 2
#define MAX_KEY_SIZE 128
#define MAX_STRING_SIZE 128
#define MAX_JOINT_COUNT 32
enum {
@@ -39,6 +39,25 @@ enum {
RENDERING_MODE_TRIANGLES = 4,
};
typedef enum {
COMPONENT_TYPE_S8 = 5120, // Byte
COMPONENT_TYPE_U8 = 5121, // Unsigned byte
COMPONENT_TYPE_S16 = 5122, // Short
COMPONENT_TYPE_U16 = 5123, // Unsigned short
COMPONENT_TYPE_U32 = 5125, // Unsigned int
COMPONENT_TYPE_F32 = 5126, // Float
} component_type_t;
typedef enum {
ACCESSOR_TYPE_SCALAR,
ACCESSOR_TYPE_VEC2,
ACCESSOR_TYPE_VEC3,
ACCESSOR_TYPE_VEC4,
ACCESSOR_TYPE_MAT2,
ACCESSOR_TYPE_MAT3,
ACCESSOR_TYPE_MAT4,
} accessor_type_t;
typedef struct {
uint32_t magic;
uint32_t version;
@@ -53,6 +72,8 @@ typedef struct {
typedef struct {
uint32_t normals;
uint32_t positions;
uint32_t bones;
uint32_t weights;
uint32_t indices;
} mesh_t;
@@ -120,17 +141,17 @@ static void *map_file(const char *path)
return addr;
}
static int read_key(const uint8_t **p, char buffer[MAX_KEY_SIZE])
static void read_string(const uint8_t **p, char buffer[MAX_STRING_SIZE])
{
assert(**p == '"');
++(*p);
size_t len = 0;
while (**p != '"') {
assert(len < MAX_KEY_SIZE);
assert(len < MAX_STRING_SIZE - 1);
buffer[len++] = *(*p)++;
}
++(*p);
return len;
buffer[len] = '\0';
}
static bool ends_value(uint8_t b)
@@ -296,8 +317,7 @@ static vec4_t read_vec4(const uint8_t **p)
static mesh_t read_mesh_primitives(const uint8_t **p)
{
char buffer[MAX_KEY_SIZE];
size_t len;
char buffer[MAX_STRING_SIZE];
mesh_t mesh = {};
assert(**p == '[');
@@ -307,21 +327,21 @@ static mesh_t read_mesh_primitives(const uint8_t **p)
++(*p);
while (**p != '}') {
len = read_key(p, buffer);
read_string(p, buffer);
assert(**p == ':');
++(*p);
if (memcmp("attributes", buffer, len) == 0) {
if (strcmp("attributes", buffer) == 0) {
assert(**p == '{');
++(*p);
while (**p != '}') {
len = read_key(p, buffer);
read_string(p, buffer);
assert(**p == ':');
++(*p);
if (memcmp("NORMAL", buffer, len) == 0) {
if (strcmp("NORMAL", buffer) == 0) {
mesh.normals = read_uint(p);
} else if (memcmp("POSITION", buffer, len) == 0) {
} else if (strcmp("POSITION", buffer) == 0) {
mesh.positions = read_uint(p);
} else {
skip_value(p);
@@ -330,9 +350,9 @@ static mesh_t read_mesh_primitives(const uint8_t **p)
++(*p);
}
++(*p);
} else if (memcmp("indices", buffer, len) == 0) {
} else if (strcmp("indices", buffer) == 0) {
mesh.indices = read_uint(p);
} else if (memcmp("mode", buffer, len) == 0) {
} else if (strcmp("mode", buffer) == 0) {
assert(read_uint(p) == RENDERING_MODE_TRIANGLES);
} else {
skip_value(p);
@@ -351,8 +371,7 @@ static mesh_t read_mesh_primitives(const uint8_t **p)
static mesh_t read_meshes(const uint8_t *p)
{
char buffer[MAX_KEY_SIZE];
size_t len;
char buffer[MAX_STRING_SIZE];
assert(*p == '[');
++p;
@@ -360,10 +379,10 @@ static mesh_t read_meshes(const uint8_t *p)
++p;
while (*p != '}') {
len = read_key(&p, buffer);
read_string(&p, buffer);
assert(*p == ':');
++p;
if (memcmp("primitives", buffer, len) == 0)
if (strcmp("primitives", buffer) == 0)
return read_mesh_primitives(&p);
else
skip_value(&p);
@@ -371,18 +390,14 @@ static mesh_t read_meshes(const uint8_t *p)
++p;
}
/*
* If we reach here, the primitives key was missing from the mesh
* definition.
*/
// Primitives key missing from mesh object
assert(false);
}
static skin_t read_skins(const uint8_t *p)
{
skin_t skin = {};
char buffer[MAX_KEY_SIZE];
size_t len;
char buffer[MAX_STRING_SIZE];
assert(*p == '[');
++p;
@@ -390,13 +405,13 @@ static skin_t read_skins(const uint8_t *p)
++p;
while (*p != '}') {
len = read_key(&p, buffer);
read_string(&p, buffer);
assert(*p == ':');
++p;
if (memcmp("inverseBindMatrices", buffer, len) == 0) {
if (strcmp("inverseBindMatrices", buffer) == 0) {
skin.inverse_bind_matrices = read_uint(&p);
} else if (memcmp("joints", buffer, len) == 0) {
} else if (strcmp("joints", buffer) == 0) {
assert(*p == '[');
++p;
for (skin.joint_count = 0; *p != ']'; ++skin.joint_count) {
@@ -447,8 +462,7 @@ static void read_joint_children(
static void
read_joints(const uint8_t *p, skin_t skin, joint_t joints[MAX_JOINT_COUNT])
{
char buffer[MAX_KEY_SIZE];
size_t len;
char buffer[MAX_STRING_SIZE];
for (uint32_t i = 0; i < skin.joint_count; ++i)
joints[i].parent = UINT32_MAX;
@@ -473,21 +487,20 @@ read_joints(const uint8_t *p, skin_t skin, joint_t joints[MAX_JOINT_COUNT])
++p;
while (*p != '}') {
len = read_key(&p, buffer);
read_string(&p, buffer);
assert(*p == ':');
++p;
if (memcmp("children", buffer, len) == 0) {
if (strcmp("children", buffer) == 0)
read_joint_children(&p, skin, joints, j);
} else if (memcmp("translation", buffer, len) == 0) {
else if (strcmp("translation", buffer) == 0)
joints[j].translation = read_vec3(&p);
} else if (memcmp("rotation", buffer, len) == 0) {
else if (strcmp("rotation", buffer) == 0)
joints[j].rotation = read_vec4(&p);
} else if (memcmp("scale", buffer, len) == 0) {
else if (strcmp("scale", buffer) == 0)
joints[j].scale = read_vec3(&p);
} else {
else
skip_value(&p);
}
if (*p == ',')
++p;
@@ -499,27 +512,47 @@ read_joints(const uint8_t *p, skin_t skin, joint_t joints[MAX_JOINT_COUNT])
}
}
static accessor_t read_accessor(const uint8_t **p)
static accessor_type_t read_accessor_type(const uint8_t **p)
{
char buffer[MAX_KEY_SIZE];
size_t len;
char buffer[MAX_STRING_SIZE];
read_string(p, buffer);
if (strcmp("SCALAR", buffer) == 0)
return ACCESSOR_TYPE_SCALAR;
else if (strcmp("VEC3", buffer) == 0)
return ACCESSOR_TYPE_VEC3;
else if (strcmp("VEC4", buffer) == 0)
return ACCESSOR_TYPE_VEC4;
else if (strcmp("MAT4", buffer) == 0)
return ACCESSOR_TYPE_MAT4;
else
assert(false);
}
static accessor_t read_accessor(
const uint8_t **p, component_type_t component_type, accessor_type_t type)
{
char buffer[MAX_STRING_SIZE];
accessor_t accessor = {};
assert(**p == '{');
++(*p);
while (**p != '}') {
len = read_key(p, buffer);
read_string(p, buffer);
assert(**p == ':');
++(*p);
if (memcmp("bufferView", buffer, len) == 0)
if (strcmp("bufferView", buffer) == 0)
accessor.view = read_uint(p);
else if (memcmp("byteOffset", buffer, len) == 0)
else if (strcmp("byteOffset", buffer) == 0)
accessor.offset = read_uint(p);
else if (memcmp("count", buffer, len) == 0)
else if (strcmp("count", buffer) == 0)
accessor.count = read_uint(p);
else if (memcmp("sparse", buffer, len) == 0)
else if (strcmp("componentType", buffer) == 0)
assert(read_uint(p) == component_type);
else if (strcmp("type", buffer) == 0)
assert(read_accessor_type(p) == type);
else if (strcmp("sparse", buffer) == 0)
assert(false); // Sparse accessors are not supported
else
skip_value(p);
@@ -538,14 +571,18 @@ static accessors_t read_accessors(const uint8_t *p, mesh_t mesh)
assert(*p == '[');
++p;
for (uint32_t i = 0; *p != ']'; ++i) {
if (i == mesh.normals)
accessors.normals = read_accessor(&p);
else if (i == mesh.positions)
accessors.positions = read_accessor(&p);
else if (i == mesh.indices)
accessors.indices = read_accessor(&p);
else
if (i == mesh.normals) {
accessors.normals
= read_accessor(&p, COMPONENT_TYPE_F32, ACCESSOR_TYPE_VEC3);
} else if (i == mesh.positions) {
accessors.positions
= read_accessor(&p, COMPONENT_TYPE_F32, ACCESSOR_TYPE_VEC3);
} else if (i == mesh.indices) {
accessors.indices = read_accessor(
&p, COMPONENT_TYPE_U16, ACCESSOR_TYPE_SCALAR);
} else {
skip_value(&p);
}
if (*p == ',')
++p;
@@ -555,25 +592,24 @@ static accessors_t read_accessors(const uint8_t *p, mesh_t mesh)
static view_t read_view(const uint8_t **p)
{
char buffer[MAX_KEY_SIZE];
size_t len;
char buffer[MAX_STRING_SIZE];
view_t view = {};
assert(**p == '{');
++(*p);
while (**p != '}') {
len = read_key(p, buffer);
read_string(p, buffer);
assert(**p == ':');
++(*p);
if (memcmp("buffer", buffer, len) == 0)
if (strcmp("buffer", buffer) == 0)
assert(read_uint(p) == 0);
else if (memcmp("byteOffset", buffer, len) == 0)
else if (strcmp("byteOffset", buffer) == 0)
view.offset = read_uint(p);
else if (memcmp("byteLength", buffer, len) == 0)
else if (strcmp("byteLength", buffer) == 0)
view.len = read_uint(p);
else if (memcmp("byteStride", buffer, len) == 0)
else if (strcmp("byteStride", buffer) == 0)
view.stride = read_uint(p);
else
skip_value(p);
@@ -612,8 +648,7 @@ static views_t read_views(const uint8_t *p, accessors_t accessors)
static gltf_info_t read_json(const uint8_t *json)
{
char buffer[MAX_KEY_SIZE];
size_t len;
char buffer[MAX_STRING_SIZE];
assert(*json == '{');
++json;
@@ -632,18 +667,18 @@ static gltf_info_t read_json(const uint8_t *json)
const uint8_t *accessors_json = nullptr;
const uint8_t *views_json = nullptr;
while (*json != '}') {
len = read_key(&json, buffer);
read_string(&json, buffer);
assert(*json == ':');
++json;
if (memcmp("meshes", buffer, len) == 0)
if (strcmp("meshes", buffer) == 0)
meshes_json = json;
else if (memcmp("skins", buffer, len) == 0)
else if (strcmp("skins", buffer) == 0)
skins_json = json;
else if (memcmp("nodes", buffer, len) == 0)
else if (strcmp("nodes", buffer) == 0)
nodes_json = json;
else if (memcmp("accessors", buffer, len) == 0)
else if (strcmp("accessors", buffer) == 0)
accessors_json = json;
else if (memcmp("bufferViews", buffer, len) == 0)
else if (strcmp("bufferViews", buffer) == 0)
views_json = json;
skip_value(&json);
if (*json == ',')
@@ -663,17 +698,8 @@ static gltf_info_t read_json(const uint8_t *json)
const accessors_t accessors = read_accessors(accessors_json, mesh);
const views_t views = read_views(views_json, accessors);
if (accessors.positions.view == accessors.normals.view) {
assert(views.normals.stride == sizeof(vec3_t));
} else {
const size_t normal_size
= views.normals.len / accessors.normals.count;
assert(normal_size == sizeof(vec3_t));
const size_t position_size
= views.positions.len / accessors.positions.count;
assert(position_size == sizeof(vec3_t));
}
assert(views.indices.len / accessors.indices.count == sizeof(index_t));
// Assume indices are in their own buffer view
assert(views.indices.stride == 0);
return (gltf_info_t) {
.normals = {