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 MAGIC 0x46546c67 // "glTF"
#define VERSION 2 #define VERSION 2
#define MAX_KEY_SIZE 128 #define MAX_STRING_SIZE 128
#define MAX_JOINT_COUNT 32 #define MAX_JOINT_COUNT 32
enum { enum {
@@ -39,6 +39,25 @@ enum {
RENDERING_MODE_TRIANGLES = 4, 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 { typedef struct {
uint32_t magic; uint32_t magic;
uint32_t version; uint32_t version;
@@ -53,6 +72,8 @@ typedef struct {
typedef struct { typedef struct {
uint32_t normals; uint32_t normals;
uint32_t positions; uint32_t positions;
uint32_t bones;
uint32_t weights;
uint32_t indices; uint32_t indices;
} mesh_t; } mesh_t;
@@ -120,17 +141,17 @@ static void *map_file(const char *path)
return addr; 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 == '"'); assert(**p == '"');
++(*p); ++(*p);
size_t len = 0; size_t len = 0;
while (**p != '"') { while (**p != '"') {
assert(len < MAX_KEY_SIZE); assert(len < MAX_STRING_SIZE - 1);
buffer[len++] = *(*p)++; buffer[len++] = *(*p)++;
} }
++(*p); ++(*p);
return len; buffer[len] = '\0';
} }
static bool ends_value(uint8_t b) 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) static mesh_t read_mesh_primitives(const uint8_t **p)
{ {
char buffer[MAX_KEY_SIZE]; char buffer[MAX_STRING_SIZE];
size_t len;
mesh_t mesh = {}; mesh_t mesh = {};
assert(**p == '['); assert(**p == '[');
@@ -307,21 +327,21 @@ static mesh_t read_mesh_primitives(const uint8_t **p)
++(*p); ++(*p);
while (**p != '}') { while (**p != '}') {
len = read_key(p, buffer); read_string(p, buffer);
assert(**p == ':'); assert(**p == ':');
++(*p); ++(*p);
if (memcmp("attributes", buffer, len) == 0) { if (strcmp("attributes", buffer) == 0) {
assert(**p == '{'); assert(**p == '{');
++(*p); ++(*p);
while (**p != '}') { while (**p != '}') {
len = read_key(p, buffer); read_string(p, buffer);
assert(**p == ':'); assert(**p == ':');
++(*p); ++(*p);
if (memcmp("NORMAL", buffer, len) == 0) { if (strcmp("NORMAL", buffer) == 0) {
mesh.normals = read_uint(p); mesh.normals = read_uint(p);
} else if (memcmp("POSITION", buffer, len) == 0) { } else if (strcmp("POSITION", buffer) == 0) {
mesh.positions = read_uint(p); mesh.positions = read_uint(p);
} else { } else {
skip_value(p); skip_value(p);
@@ -330,9 +350,9 @@ static mesh_t read_mesh_primitives(const uint8_t **p)
++(*p); ++(*p);
} }
++(*p); ++(*p);
} else if (memcmp("indices", buffer, len) == 0) { } else if (strcmp("indices", buffer) == 0) {
mesh.indices = read_uint(p); 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); assert(read_uint(p) == RENDERING_MODE_TRIANGLES);
} else { } else {
skip_value(p); 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) static mesh_t read_meshes(const uint8_t *p)
{ {
char buffer[MAX_KEY_SIZE]; char buffer[MAX_STRING_SIZE];
size_t len;
assert(*p == '['); assert(*p == '[');
++p; ++p;
@@ -360,10 +379,10 @@ static mesh_t read_meshes(const uint8_t *p)
++p; ++p;
while (*p != '}') { while (*p != '}') {
len = read_key(&p, buffer); read_string(&p, buffer);
assert(*p == ':'); assert(*p == ':');
++p; ++p;
if (memcmp("primitives", buffer, len) == 0) if (strcmp("primitives", buffer) == 0)
return read_mesh_primitives(&p); return read_mesh_primitives(&p);
else else
skip_value(&p); skip_value(&p);
@@ -371,18 +390,14 @@ static mesh_t read_meshes(const uint8_t *p)
++p; ++p;
} }
/* // Primitives key missing from mesh object
* If we reach here, the primitives key was missing from the mesh
* definition.
*/
assert(false); assert(false);
} }
static skin_t read_skins(const uint8_t *p) static skin_t read_skins(const uint8_t *p)
{ {
skin_t skin = {}; skin_t skin = {};
char buffer[MAX_KEY_SIZE]; char buffer[MAX_STRING_SIZE];
size_t len;
assert(*p == '['); assert(*p == '[');
++p; ++p;
@@ -390,13 +405,13 @@ static skin_t read_skins(const uint8_t *p)
++p; ++p;
while (*p != '}') { while (*p != '}') {
len = read_key(&p, buffer); read_string(&p, buffer);
assert(*p == ':'); assert(*p == ':');
++p; ++p;
if (memcmp("inverseBindMatrices", buffer, len) == 0) { if (strcmp("inverseBindMatrices", buffer) == 0) {
skin.inverse_bind_matrices = read_uint(&p); skin.inverse_bind_matrices = read_uint(&p);
} else if (memcmp("joints", buffer, len) == 0) { } else if (strcmp("joints", buffer) == 0) {
assert(*p == '['); assert(*p == '[');
++p; ++p;
for (skin.joint_count = 0; *p != ']'; ++skin.joint_count) { for (skin.joint_count = 0; *p != ']'; ++skin.joint_count) {
@@ -447,8 +462,7 @@ static void read_joint_children(
static void static void
read_joints(const uint8_t *p, skin_t skin, joint_t joints[MAX_JOINT_COUNT]) read_joints(const uint8_t *p, skin_t skin, joint_t joints[MAX_JOINT_COUNT])
{ {
char buffer[MAX_KEY_SIZE]; char buffer[MAX_STRING_SIZE];
size_t len;
for (uint32_t i = 0; i < skin.joint_count; ++i) for (uint32_t i = 0; i < skin.joint_count; ++i)
joints[i].parent = UINT32_MAX; 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; ++p;
while (*p != '}') { while (*p != '}') {
len = read_key(&p, buffer); read_string(&p, buffer);
assert(*p == ':'); assert(*p == ':');
++p; ++p;
if (memcmp("children", buffer, len) == 0) { if (strcmp("children", buffer) == 0)
read_joint_children(&p, skin, joints, j); 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); 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); 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); joints[j].scale = read_vec3(&p);
} else { else
skip_value(&p); skip_value(&p);
}
if (*p == ',') if (*p == ',')
++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]; char buffer[MAX_STRING_SIZE];
size_t len; 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 = {}; accessor_t accessor = {};
assert(**p == '{'); assert(**p == '{');
++(*p); ++(*p);
while (**p != '}') { while (**p != '}') {
len = read_key(p, buffer); read_string(p, buffer);
assert(**p == ':'); assert(**p == ':');
++(*p); ++(*p);
if (memcmp("bufferView", buffer, len) == 0) if (strcmp("bufferView", buffer) == 0)
accessor.view = read_uint(p); accessor.view = read_uint(p);
else if (memcmp("byteOffset", buffer, len) == 0) else if (strcmp("byteOffset", buffer) == 0)
accessor.offset = read_uint(p); accessor.offset = read_uint(p);
else if (memcmp("count", buffer, len) == 0) else if (strcmp("count", buffer) == 0)
accessor.count = read_uint(p); 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 assert(false); // Sparse accessors are not supported
else else
skip_value(p); skip_value(p);
@@ -538,14 +571,18 @@ static accessors_t read_accessors(const uint8_t *p, mesh_t mesh)
assert(*p == '['); assert(*p == '[');
++p; ++p;
for (uint32_t i = 0; *p != ']'; ++i) { for (uint32_t i = 0; *p != ']'; ++i) {
if (i == mesh.normals) if (i == mesh.normals) {
accessors.normals = read_accessor(&p); accessors.normals
else if (i == mesh.positions) = read_accessor(&p, COMPONENT_TYPE_F32, ACCESSOR_TYPE_VEC3);
accessors.positions = read_accessor(&p); } else if (i == mesh.positions) {
else if (i == mesh.indices) accessors.positions
accessors.indices = read_accessor(&p); = read_accessor(&p, COMPONENT_TYPE_F32, ACCESSOR_TYPE_VEC3);
else } else if (i == mesh.indices) {
accessors.indices = read_accessor(
&p, COMPONENT_TYPE_U16, ACCESSOR_TYPE_SCALAR);
} else {
skip_value(&p); skip_value(&p);
}
if (*p == ',') if (*p == ',')
++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) static view_t read_view(const uint8_t **p)
{ {
char buffer[MAX_KEY_SIZE]; char buffer[MAX_STRING_SIZE];
size_t len;
view_t view = {}; view_t view = {};
assert(**p == '{'); assert(**p == '{');
++(*p); ++(*p);
while (**p != '}') { while (**p != '}') {
len = read_key(p, buffer); read_string(p, buffer);
assert(**p == ':'); assert(**p == ':');
++(*p); ++(*p);
if (memcmp("buffer", buffer, len) == 0) if (strcmp("buffer", buffer) == 0)
assert(read_uint(p) == 0); assert(read_uint(p) == 0);
else if (memcmp("byteOffset", buffer, len) == 0) else if (strcmp("byteOffset", buffer) == 0)
view.offset = read_uint(p); view.offset = read_uint(p);
else if (memcmp("byteLength", buffer, len) == 0) else if (strcmp("byteLength", buffer) == 0)
view.len = read_uint(p); view.len = read_uint(p);
else if (memcmp("byteStride", buffer, len) == 0) else if (strcmp("byteStride", buffer) == 0)
view.stride = read_uint(p); view.stride = read_uint(p);
else else
skip_value(p); 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) static gltf_info_t read_json(const uint8_t *json)
{ {
char buffer[MAX_KEY_SIZE]; char buffer[MAX_STRING_SIZE];
size_t len;
assert(*json == '{'); assert(*json == '{');
++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 *accessors_json = nullptr;
const uint8_t *views_json = nullptr; const uint8_t *views_json = nullptr;
while (*json != '}') { while (*json != '}') {
len = read_key(&json, buffer); read_string(&json, buffer);
assert(*json == ':'); assert(*json == ':');
++json; ++json;
if (memcmp("meshes", buffer, len) == 0) if (strcmp("meshes", buffer) == 0)
meshes_json = json; meshes_json = json;
else if (memcmp("skins", buffer, len) == 0) else if (strcmp("skins", buffer) == 0)
skins_json = json; skins_json = json;
else if (memcmp("nodes", buffer, len) == 0) else if (strcmp("nodes", buffer) == 0)
nodes_json = json; nodes_json = json;
else if (memcmp("accessors", buffer, len) == 0) else if (strcmp("accessors", buffer) == 0)
accessors_json = json; accessors_json = json;
else if (memcmp("bufferViews", buffer, len) == 0) else if (strcmp("bufferViews", buffer) == 0)
views_json = json; views_json = json;
skip_value(&json); skip_value(&json);
if (*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 accessors_t accessors = read_accessors(accessors_json, mesh);
const views_t views = read_views(views_json, accessors); const views_t views = read_views(views_json, accessors);
if (accessors.positions.view == accessors.normals.view) { // Assume indices are in their own buffer view
assert(views.normals.stride == sizeof(vec3_t)); assert(views.indices.stride == 0);
} 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));
return (gltf_info_t) { return (gltf_info_t) {
.normals = { .normals = {