Load joint info from GLB

This commit is contained in:
Camden Dixie O'Brien committed 2025-10-01 10:50:30 +01:00
1 parent 221d68906d
commit 6454098c6a
1 file changed
+256 -14
+256 -14
View File
@@ -3,6 +3,7 @@
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <fcntl.h> #include <fcntl.h>
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
@@ -27,6 +28,7 @@ static_assert(
#define VERSION 2 #define VERSION 2
#define MAX_KEY_SIZE 128 #define MAX_KEY_SIZE 128
#define MAX_JOINT_COUNT 32
enum { enum {
CHUNK_TYPE_JSON = 0x4e4f534a, // "JSON" CHUNK_TYPE_JSON = 0x4e4f534a, // "JSON"
@@ -54,6 +56,19 @@ typedef struct {
uint32_t indices; uint32_t indices;
} mesh_t; } mesh_t;
typedef struct {
uint32_t inverse_bind_matrices;
uint32_t joints[MAX_JOINT_COUNT];
uint32_t joint_count;
} skin_t;
typedef struct {
uint32_t parent;
vec3_t translation;
vec4_t rotation;
vec3_t scale;
} joint_t;
typedef struct { typedef struct {
uint32_t view; uint32_t view;
uint32_t offset; uint32_t offset;
@@ -185,6 +200,100 @@ static uint32_t read_uint(const uint8_t **p)
return value; return value;
} }
static float read_float(const uint8_t **p)
{
enum {
INTEGRAL,
DECIMAL,
EXPONENT,
} state
= INTEGRAL;
bool neg_val = false, neg_exp = false;
float val = 0, exp = 0;
unsigned div = 10;
while (!ends_value(**p)) {
const uint8_t c = *((*p)++);
switch (state) {
case INTEGRAL:
if (isdigit(c))
val = 10 * val + (c - '0');
else if (c == '-')
neg_val = true;
else if (c == '.')
state = DECIMAL;
else
assert(false);
break;
case DECIMAL:
if (isdigit(c))
val += (float)(c - '0') / div;
else if (c == 'e')
state = EXPONENT;
else
assert(false);
div *= 10;
break;
case EXPONENT:
if (isdigit(c))
exp = 10 * exp + (c - '0');
else if (c == '-')
neg_exp = true;
else
assert(false);
break;
default:
assert(false);
}
}
if (neg_val)
val *= -1;
if (neg_exp)
exp *= -1;
return val * powf(10, exp);
}
static vec3_t read_vec3(const uint8_t **p)
{
vec3_t v;
assert(**p == '[');
++(*p);
v.x = read_float(p);
assert(**p == ',');
++(*p);
v.y = read_float(p);
assert(**p == ',');
++(*p);
v.z = read_float(p);
assert(**p == ']');
++(*p);
return v;
}
static vec4_t read_vec4(const uint8_t **p)
{
vec4_t v;
assert(**p == '[');
++(*p);
v.x = read_float(p);
assert(**p == ',');
++(*p);
v.y = read_float(p);
assert(**p == ',');
++(*p);
v.z = read_float(p);
assert(**p == ',');
++(*p);
v.w = read_float(p);
assert(**p == ']');
++(*p);
return v;
}
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_KEY_SIZE];
@@ -269,6 +378,127 @@ static mesh_t read_meshes(const uint8_t *p)
assert(false); assert(false);
} }
static skin_t read_skins(const uint8_t *p)
{
skin_t skin = {};
char buffer[MAX_KEY_SIZE];
size_t len;
assert(*p == '[');
++p;
assert(*p == '{');
++p;
while (*p != '}') {
len = read_key(&p, buffer);
assert(*p == ':');
++p;
if (memcmp("inverseBindMatrices", buffer, len) == 0) {
skin.inverse_bind_matrices = read_uint(&p);
} else if (memcmp("joints", buffer, len) == 0) {
assert(*p == '[');
++p;
for (skin.joint_count = 0; *p != ']'; ++skin.joint_count) {
assert(skin.joint_count < MAX_JOINT_COUNT);
skin.joints[skin.joint_count] = read_uint(&p);
if (*p == ',')
++p;
}
++p;
} else {
skip_value(&p);
}
if (*p == ',')
++p;
}
++p;
assert(*p == ']');
return skin;
}
static void read_joint_children(
const uint8_t **p, skin_t skin, joint_t joints[MAX_JOINT_COUNT],
uint32_t parent)
{
assert(**p == '[');
++(*p);
while (**p != ']') {
const uint32_t i = read_uint(p);
uint32_t j;
for (j = 0; j < skin.joint_count; ++j) {
if (skin.joints[j] == i)
break;
}
assert(j < skin.joint_count);
assert(joints[j].parent == UINT32_MAX);
joints[j].parent = parent;
if (**p == ',')
++(*p);
}
++(*p);
}
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;
for (uint32_t i = 0; i < skin.joint_count; ++i)
joints[i].parent = UINT32_MAX;
assert(*p == '[');
++p;
for (uint32_t i = 0; *p != ']'; ++i) {
uint32_t j;
for (j = 0; j < skin.joint_count; ++j) {
if (skin.joints[j] == i)
break;
}
if (j >= skin.joint_count) {
skip_value(&p);
if (*p == ',')
++p;
continue;
}
assert(*p == '{');
++p;
while (*p != '}') {
len = read_key(&p, buffer);
assert(*p == ':');
++p;
if (memcmp("children", buffer, len) == 0) {
read_joint_children(&p, skin, joints, j);
} else if (memcmp("translation", buffer, len) == 0) {
joints[j].translation = read_vec3(&p);
} else if (memcmp("rotation", buffer, len) == 0) {
joints[j].rotation = read_vec4(&p);
} else if (memcmp("scale", buffer, len) == 0) {
joints[j].scale = read_vec3(&p);
} else {
skip_value(&p);
}
if (*p == ',')
++p;
}
++p;
if (*p == ',')
++p;
}
}
static accessor_t read_accessor(const uint8_t **p) static accessor_t read_accessor(const uint8_t **p)
{ {
char buffer[MAX_KEY_SIZE]; char buffer[MAX_KEY_SIZE];
@@ -374,6 +604,9 @@ static views_t read_views(const uint8_t *p, accessors_t accessors)
if (*p == ',') if (*p == ',')
++p; ++p;
} }
if (accessors.normals.view == accessors.positions.view)
views.positions = views.normals;
return views; return views;
} }
@@ -394,6 +627,8 @@ static gltf_info_t read_json(const uint8_t *json)
* them in the necessary order. * them in the necessary order.
*/ */
const uint8_t *meshes_json = nullptr; const uint8_t *meshes_json = nullptr;
const uint8_t *skins_json = nullptr;
const uint8_t *nodes_json = nullptr;
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 != '}') {
@@ -402,6 +637,10 @@ static gltf_info_t read_json(const uint8_t *json)
++json; ++json;
if (memcmp("meshes", buffer, len) == 0) if (memcmp("meshes", buffer, len) == 0)
meshes_json = json; meshes_json = json;
else if (memcmp("skins", buffer, len) == 0)
skins_json = json;
else if (memcmp("nodes", buffer, len) == 0)
nodes_json = json;
else if (memcmp("accessors", buffer, len) == 0) else if (memcmp("accessors", buffer, len) == 0)
accessors_json = json; accessors_json = json;
else if (memcmp("bufferViews", buffer, len) == 0) else if (memcmp("bufferViews", buffer, len) == 0)
@@ -411,36 +650,39 @@ static gltf_info_t read_json(const uint8_t *json)
++json; ++json;
} }
assert(meshes_json != nullptr); assert(meshes_json != nullptr);
assert(skins_json != nullptr);
assert(nodes_json != nullptr);
assert(accessors_json != nullptr); assert(accessors_json != nullptr);
assert(views_json != nullptr); assert(views_json != nullptr);
mesh_t mesh = read_meshes(meshes_json); const mesh_t mesh = read_meshes(meshes_json);
accessors_t accessors = read_accessors(accessors_json, mesh); const skin_t skin = read_skins(skins_json);
views_t views = read_views(views_json, accessors); joint_t joints[MAX_JOINT_COUNT] = {};
read_joints(nodes_json, skin, joints);
const accessors_t accessors = read_accessors(accessors_json, mesh);
const views_t views = read_views(views_json, accessors);
size_t normals_len, positions_len;
if (accessors.positions.view == accessors.normals.view) { if (accessors.positions.view == accessors.normals.view) {
views.positions = views.normals;
assert(views.normals.stride == sizeof(vec3_t)); assert(views.normals.stride == sizeof(vec3_t));
assert(views.positions.stride == sizeof(vec3_t));
normals_len = accessors.normals.count * sizeof(vec3_t);
positions_len = accessors.positions.count * sizeof(vec3_t);
} else { } else {
normals_len = views.normals.len; const size_t normal_size
positions_len = views.positions.len; = 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.stride == 0);
assert(views.indices.len / accessors.indices.count == sizeof(index_t)); assert(views.indices.len / accessors.indices.count == sizeof(index_t));
return (gltf_info_t) { return (gltf_info_t) {
.normals = { .normals = {
.offset = views.normals.offset + accessors.normals.offset, .offset = views.normals.offset + accessors.normals.offset,
.len = normals_len, .len = accessors.normals.count * sizeof(vec3_t),
}, },
.positions = { .positions = {
.offset = views.positions.offset + accessors.positions.offset, .offset = views.positions.offset + accessors.positions.offset,
.len = positions_len, .len = accessors.positions.count * sizeof(vec3_t),
}, },
.indices = { .indices = {
.offset = views.indices.offset + accessors.indices.offset, .offset = views.indices.offset + accessors.indices.offset,