mirror of https://github.com/bjornbytes/lovr.git
Parse animations;
This commit is contained in:
parent
6b323e3476
commit
4ed3cad851
|
@ -3,40 +3,35 @@
|
||||||
#include "lib/jsmn/jsmn.h"
|
#include "lib/jsmn/jsmn.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// Notes:
|
|
||||||
// - We parse in two passes.
|
|
||||||
// - In the first pass we figure out how much memory we need to allocate.
|
|
||||||
// - Then we allocate the memory and do a second pass to fill everything in.
|
|
||||||
// - Plan is to make this parser destructive, so it mutates the input Blob in order to avoid doing
|
|
||||||
// work in some situations, for speed. May make sense to provide a non-destructive option.
|
|
||||||
// - Currently this is most useful for reusing string memory by changing quotes to \0's.
|
|
||||||
// - Caveats:
|
|
||||||
// - The IO callback must not hang onto the filenames that are passed into it.
|
|
||||||
|
|
||||||
#define MAGIC_glTF 0x46546c67
|
#define MAGIC_glTF 0x46546c67
|
||||||
#define MAGIC_JSON 0x4e4f534a
|
#define MAGIC_JSON 0x4e4f534a
|
||||||
#define MAGIC_BIN 0x004e4942
|
#define MAGIC_BIN 0x004e4942
|
||||||
|
|
||||||
#define KEY_EQ(k, s) !strncmp(k.data, s, k.length)
|
#define KEY_EQ(k, s) !strncmp(k.data, s, k.length)
|
||||||
#define TOK_INT(j, t) strtol(j + t->start, NULL, 10)
|
#define NOM_KEY(j, t) hashKey((char*) j + (t++)->start)
|
||||||
#define TOK_BOOL(j, t) (*(j + t->start) == 't')
|
#define NOM_INT(j, t) strtol(j + (t++)->start, NULL, 10)
|
||||||
#define TOK_FLOAT(j, t) strtof(j + t->start, NULL)
|
#define NOM_BOOL(j, t) (*(j + (t++)->start) == 't')
|
||||||
|
#define NOM_FLOAT(j, t) strtof(j + (t++)->start, NULL)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct { int count; jsmntok_t* token; } accessors;
|
size_t totalSize;
|
||||||
struct { int count; jsmntok_t* token; } blobs;
|
jsmntok_t* accessors;
|
||||||
struct { int count; jsmntok_t* token; } views;
|
jsmntok_t* animations;
|
||||||
struct { int count; jsmntok_t* token; } nodes;
|
jsmntok_t* blobs;
|
||||||
struct { int count; jsmntok_t* token; } meshes;
|
jsmntok_t* views;
|
||||||
|
jsmntok_t* nodes;
|
||||||
|
jsmntok_t* meshes;
|
||||||
|
jsmntok_t* skins;
|
||||||
int childCount;
|
int childCount;
|
||||||
int primitiveCount;
|
int jointCount;
|
||||||
} gltfInfo;
|
} gltfInfo;
|
||||||
|
|
||||||
static int nomString(const char* data, jsmntok_t* token, gltfString* string) {
|
static uint32_t hashKey(char* key) {
|
||||||
lovrAssert(token->type == JSMN_STRING, "Expected string");
|
uint32_t hash = 0;
|
||||||
string->data = (char*) data + token->start;
|
for (int i = 0; key[i] != '"'; i++) {
|
||||||
string->length = token->end - token->start;
|
hash = (hash * 65599) + key[i];
|
||||||
return 1;
|
}
|
||||||
|
return hash ^ (hash >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nomValue(const char* data, jsmntok_t* token, int count, int sum) {
|
static int nomValue(const char* data, jsmntok_t* token, int count, int sum) {
|
||||||
|
@ -48,17 +43,14 @@ static int nomValue(const char* data, jsmntok_t* token, int count, int sum) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kinda like sum(map(arr, obj => #obj[key]))
|
// Kinda like total += sum(map(arr, obj => #obj[key]))
|
||||||
static jsmntok_t* aggregate(const char* json, jsmntok_t* token, const char* target, int* total) {
|
static jsmntok_t* aggregate(const char* json, jsmntok_t* token, uint32_t hash, int* total) {
|
||||||
*total = 0;
|
|
||||||
int size = (token++)->size;
|
int size = (token++)->size;
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
if (token->size > 0) {
|
if (token->size > 0) {
|
||||||
int keys = (token++)->size;
|
int keys = (token++)->size;
|
||||||
for (int k = 0; k < keys; k++) {
|
for (int k = 0; k < keys; k++) {
|
||||||
gltfString key;
|
if (NOM_KEY(json, token) == hash) {
|
||||||
token += nomString(json, token, &key);
|
|
||||||
if (KEY_EQ(key, target)) {
|
|
||||||
*total += token->size;
|
*total += token->size;
|
||||||
}
|
}
|
||||||
token += nomValue(json, token, 1, 0);
|
token += nomValue(json, token, 1, 0);
|
||||||
|
@ -68,39 +60,69 @@ static jsmntok_t* aggregate(const char* json, jsmntok_t* token, const char* targ
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void preparse(const char* json, jsmntok_t* tokens, int tokenCount, gltfInfo* info, size_t* dataSize) {
|
static void preparse(const char* json, jsmntok_t* tokens, int tokenCount, ModelData* model, gltfInfo* info) {
|
||||||
for (jsmntok_t* token = tokens + 1; token < tokens + tokenCount;) { // +1 to skip root object
|
for (jsmntok_t* token = tokens + 1; token < tokens + tokenCount;) { // +1 to skip root object
|
||||||
gltfString key;
|
switch (NOM_KEY(json, token)) {
|
||||||
token += nomString(json, token, &key);
|
case HASH16("accessors"):
|
||||||
|
info->accessors = token;
|
||||||
if (KEY_EQ(key, "accessors")) {
|
model->accessorCount = token->size;
|
||||||
info->accessors.token = token;
|
info->totalSize += token->size * sizeof(ModelAccessor);
|
||||||
info->accessors.count = token->size;
|
token += nomValue(json, token, 1, 0);
|
||||||
*dataSize += info->accessors.count * sizeof(ModelAccessor);
|
break;
|
||||||
token += nomValue(json, token, 1, 0);
|
case HASH16("animations"):
|
||||||
} else if (KEY_EQ(key, "buffers")) {
|
info->animations = token;
|
||||||
info->blobs.token = token;
|
model->animationCount = token->size;
|
||||||
info->blobs.count = token->size;
|
info->totalSize += token->size * sizeof(ModelAnimation);
|
||||||
*dataSize += info->blobs.count * sizeof(ModelBlob);
|
// Almost like aggregate, but we gotta aggregate 2 keys in a single pass
|
||||||
token += nomValue(json, token, 1, 0);
|
int size = (token++)->size;
|
||||||
} else if (KEY_EQ(key, "bufferViews")) {
|
for (int i = 0; i < size; i++) {
|
||||||
info->views.token = token;
|
if (token->size > 0) {
|
||||||
info->views.count = token->size;
|
int keyCount = (token++)->size;
|
||||||
*dataSize += info->views.count * sizeof(ModelView);
|
for (int k = 0; k < keyCount; k++) {
|
||||||
token += nomValue(json, token, 1, 0);
|
switch (NOM_KEY(json, token)) {
|
||||||
} else if (KEY_EQ(key, "nodes")) {
|
case HASH16("channels"): model->animationChannelCount += token->size; break;
|
||||||
info->nodes.token = token;
|
case HASH16("samplers"): model->animationSamplerCount += token->size; break;
|
||||||
info->nodes.count = token->size;
|
default: break;
|
||||||
*dataSize += info->nodes.count * sizeof(ModelNode);
|
}
|
||||||
token = aggregate(json, token, "children", &info->childCount);
|
token += nomValue(json, token, 1, 0);
|
||||||
} else if (KEY_EQ(key, "meshes")) {
|
}
|
||||||
info->meshes.token = token;
|
}
|
||||||
info->meshes.count = token->size;
|
}
|
||||||
*dataSize += info->meshes.count * sizeof(ModelMesh);
|
break;
|
||||||
token = aggregate(json, token, "primitives", &info->primitiveCount);
|
case HASH16("buffers"):
|
||||||
*dataSize += info->primitiveCount * sizeof(ModelPrimitive);
|
info->blobs = token;
|
||||||
} else {
|
model->blobCount = token->size;
|
||||||
token += nomValue(json, token, 1, 0); // Skip
|
info->totalSize += token->size * sizeof(ModelBlob);
|
||||||
|
token += nomValue(json, token, 1, 0);
|
||||||
|
break;
|
||||||
|
case HASH16("bufferViews"):
|
||||||
|
info->views = token;
|
||||||
|
model->viewCount = token->size;
|
||||||
|
info->totalSize += token->size * sizeof(ModelView);
|
||||||
|
token += nomValue(json, token, 1, 0);
|
||||||
|
break;
|
||||||
|
case HASH16("nodes"):
|
||||||
|
info->nodes = token;
|
||||||
|
model->nodeCount = token->size;
|
||||||
|
info->totalSize += token->size * sizeof(ModelNode);
|
||||||
|
token = aggregate(json, token, HASH16("children"), &info->childCount);
|
||||||
|
info->totalSize += info->childCount * sizeof(uint32_t);
|
||||||
|
break;
|
||||||
|
case HASH16("meshes"):
|
||||||
|
info->meshes = token;
|
||||||
|
model->meshCount = token->size;
|
||||||
|
info->totalSize += token->size * sizeof(ModelMesh);
|
||||||
|
token = aggregate(json, token, HASH16("primitives"), &model->primitiveCount);
|
||||||
|
info->totalSize += model->primitiveCount * sizeof(ModelPrimitive);
|
||||||
|
break;
|
||||||
|
case HASH16("skins"):
|
||||||
|
info->skins = token;
|
||||||
|
model->skinCount = token->size;
|
||||||
|
info->totalSize += token->size * sizeof(ModelSkin);
|
||||||
|
token = aggregate(json, token, HASH16("joints"), &info->jointCount);
|
||||||
|
info->totalSize += info->jointCount * sizeof(uint32_t);
|
||||||
|
break;
|
||||||
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,44 +133,117 @@ static void parseAccessors(const char* json, jsmntok_t* token, ModelData* model)
|
||||||
int count = (token++)->size;
|
int count = (token++)->size;
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
ModelAccessor* accessor = &model->accessors[i];
|
ModelAccessor* accessor = &model->accessors[i];
|
||||||
gltfString key;
|
|
||||||
int keyCount = (token++)->size;
|
|
||||||
|
|
||||||
|
int keyCount = (token++)->size;
|
||||||
for (int k = 0; k < keyCount; k++) {
|
for (int k = 0; k < keyCount; k++) {
|
||||||
token += nomString(json, token, &key);
|
switch (NOM_KEY(json, token)) {
|
||||||
if (KEY_EQ(key, "bufferView")) {
|
case HASH16("bufferView"): accessor->view = NOM_INT(json, token); break;
|
||||||
accessor->view = TOK_INT(json, token), token++;
|
case HASH16("count"): accessor->count = NOM_INT(json, token); break;
|
||||||
} else if (KEY_EQ(key, "count")) {
|
case HASH16("byteOffset"): accessor->offset = NOM_INT(json, token); break;
|
||||||
accessor->count = TOK_INT(json, token), token++;
|
case HASH16("normalized"): accessor->normalized = NOM_BOOL(json, token); break;
|
||||||
} else if (KEY_EQ(key, "byteOffset")) {
|
case HASH16("componentType"):
|
||||||
accessor->offset = TOK_INT(json, token), token++;
|
switch (NOM_INT(json, token)) {
|
||||||
} else if (KEY_EQ(key, "componentType")) {
|
case 5120: accessor->type = I8; break;
|
||||||
switch (TOK_INT(json, token)) {
|
case 5121: accessor->type = U8; break;
|
||||||
case 5120: accessor->type = I8; break;
|
case 5122: accessor->type = I16; break;
|
||||||
case 5121: accessor->type = U8; break;
|
case 5123: accessor->type = U16; break;
|
||||||
case 5122: accessor->type = I16; break;
|
case 5125: accessor->type = U32; break;
|
||||||
case 5123: accessor->type = U16; break;
|
case 5126: accessor->type = F32; break;
|
||||||
case 5125: accessor->type = U32; break;
|
default: break;
|
||||||
case 5126: accessor->type = F32; break;
|
}
|
||||||
default: break;
|
break;
|
||||||
|
case HASH16("type"):
|
||||||
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("SCALAR"): accessor->components += 1; break;
|
||||||
|
case HASH16("VEC2"): accessor->components += 2; break;
|
||||||
|
case HASH16("VEC3"): accessor->components += 3; break;
|
||||||
|
case HASH16("VEC4"): accessor->components += 4; break;
|
||||||
|
default: lovrThrow("Unsupported accessor type"); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static jsmntok_t* parseAnimationChannel(const char* json, jsmntok_t* token, int index, ModelData* model) {
|
||||||
|
ModelAnimationChannel* channel = &model->animationChannels[index];
|
||||||
|
int keyCount = (token++)->size;
|
||||||
|
for (int k = 0; k < keyCount; k++) {
|
||||||
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("sampler"): channel->sampler = NOM_INT(json, token); break;
|
||||||
|
case HASH16("target"): {
|
||||||
|
int targetKeyCount = (token++)->size;
|
||||||
|
for (int tk = 0; tk < targetKeyCount; tk++) {
|
||||||
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("node"): channel->nodeIndex = NOM_INT(json, token); break;
|
||||||
|
case HASH16("path"):
|
||||||
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("translation"): channel->property = PROP_TRANSLATION; break;
|
||||||
|
case HASH16("rotation"): channel->property = PROP_ROTATION; break;
|
||||||
|
case HASH16("scale"): channel->property = PROP_SCALE; break;
|
||||||
|
default: lovrThrow("Unknown animation target path"); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
token++;
|
}
|
||||||
} else if (KEY_EQ(key, "type")) {
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
gltfString type;
|
}
|
||||||
token += nomString(json, token, &type);
|
}
|
||||||
if (KEY_EQ(type, "SCALAR")) {
|
return token;
|
||||||
accessor->components = 1;
|
}
|
||||||
} else if (type.length == 4 && type.data[0] == 'V') {
|
|
||||||
accessor->components = type.data[3] - '0';
|
static jsmntok_t* parseAnimationSampler(const char* json, jsmntok_t* token, int index, ModelData* model) {
|
||||||
} else if (type.length == 4 && type.data[0] == 'M') {
|
ModelAnimationSampler* sampler = &model->animationSamplers[index];
|
||||||
lovrThrow("Matrix accessors are not supported");
|
int keyCount = (token++)->size;
|
||||||
} else {
|
for (int k = 0; k < keyCount; k++) {
|
||||||
lovrThrow("Unknown attribute type");
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("input"): sampler->times = NOM_INT(json, token); break;
|
||||||
|
case HASH16("output"): sampler->values = NOM_INT(json, token); break;
|
||||||
|
case HASH16("interpolation"):
|
||||||
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("LINEAR"): sampler->smoothing = SMOOTH_LINEAR; break;
|
||||||
|
case HASH16("STEP"): sampler->smoothing = SMOOTH_STEP; break;
|
||||||
|
case HASH16("CUBICSPLINE"): sampler->smoothing = SMOOTH_CUBIC; break;
|
||||||
|
default: lovrThrow("Unknown animation sampler interpolation"); break;
|
||||||
}
|
}
|
||||||
} else if (KEY_EQ(key, "normalized")) {
|
break;
|
||||||
accessor->normalized = TOK_BOOL(json, token), token++;
|
default: token += nomValue(json, token, 1, 0);
|
||||||
} else {
|
}
|
||||||
token += nomValue(json, token, 1, 0); // Skip
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parseAnimations(const char* json, jsmntok_t* token, ModelData* model) {
|
||||||
|
if (!token) return;
|
||||||
|
|
||||||
|
int channelIndex = 0;
|
||||||
|
int samplerIndex = 0;
|
||||||
|
int count = (token++)->size;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
ModelAnimation* animation = &model->animations[i];
|
||||||
|
|
||||||
|
int keyCount = (token++)->size;
|
||||||
|
for (int k = 0; k < keyCount; k++) {
|
||||||
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("channels"):
|
||||||
|
animation->channelCount = token->size;
|
||||||
|
animation->channels = &model->animationChannels[channelIndex];
|
||||||
|
for (int j = 0; j < animation->channelCount; j++) {
|
||||||
|
token = parseAnimationChannel(json, token, channelIndex++, model);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HASH16("samplers"):
|
||||||
|
animation->samplerCount = token->size;
|
||||||
|
animation->samplers = &model->animationSamplers[samplerIndex];
|
||||||
|
for (int j = 0; j < animation->samplerCount; j++) {
|
||||||
|
token = parseAnimationSampler(json, token, samplerIndex++, model);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,24 +255,23 @@ static void parseBlobs(const char* json, jsmntok_t* token, ModelData* model, Mod
|
||||||
int count = (token++)->size;
|
int count = (token++)->size;
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
ModelBlob* blob = &model->blobs[i];
|
ModelBlob* blob = &model->blobs[i];
|
||||||
gltfString key;
|
|
||||||
int keyCount = (token++)->size;
|
|
||||||
size_t bytesRead = 0;
|
size_t bytesRead = 0;
|
||||||
bool hasUri = false;
|
bool hasUri = false;
|
||||||
|
|
||||||
|
int keyCount = (token++)->size;
|
||||||
for (int k = 0; k < keyCount; k++) {
|
for (int k = 0; k < keyCount; k++) {
|
||||||
token += nomString(json, token, &key);
|
switch (NOM_KEY(json, token)) {
|
||||||
if (KEY_EQ(key, "byteLength")) {
|
case HASH16("byteLength"): blob->size = NOM_INT(json, token); break;
|
||||||
blob->size = TOK_INT(json, token), token++;
|
case HASH16("uri"):
|
||||||
} else if (KEY_EQ(key, "uri")) {
|
hasUri = true;
|
||||||
hasUri = true;
|
char* filename = (char*) json + token->start;
|
||||||
gltfString filename;
|
size_t length = token->end - token->start;
|
||||||
token += nomString(json, token, &filename);
|
filename[length] = '\0'; // Change the quote into a terminator (I'll be b0k)
|
||||||
filename.data[filename.length] = '\0'; // Change the quote into a terminator (I'll be b0k)
|
blob->data = io.read(filename, &bytesRead);
|
||||||
blob->data = io.read(filename.data, &bytesRead);
|
lovrAssert(blob->data, "Unable to read %s", filename);
|
||||||
lovrAssert(blob->data, "Unable to read %s", filename.data);
|
filename[length] = '"';
|
||||||
} else {
|
break;
|
||||||
token += nomValue(json, token, 1, 0); // Skip
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,21 +290,15 @@ static void parseViews(const char* json, jsmntok_t* token, ModelData* model) {
|
||||||
int count = (token++)->size;
|
int count = (token++)->size;
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
ModelView* view = &model->views[i];
|
ModelView* view = &model->views[i];
|
||||||
gltfString key;
|
|
||||||
int keyCount = (token++)->size;
|
|
||||||
|
|
||||||
|
int keyCount = (token++)->size;
|
||||||
for (int k = 0; k < keyCount; k++) {
|
for (int k = 0; k < keyCount; k++) {
|
||||||
token += nomString(json, token, &key);
|
switch (NOM_KEY(json, token)) {
|
||||||
if (KEY_EQ(key, "buffer")) {
|
case HASH16("buffer"): view->blob = NOM_INT(json, token); break;
|
||||||
view->blob = TOK_INT(json, token), token++;
|
case HASH16("byteOffset"): view->offset = NOM_INT(json, token); break;
|
||||||
} else if (KEY_EQ(key, "byteOffset")) {
|
case HASH16("byteLength"): view->length = NOM_INT(json, token); break;
|
||||||
view->offset = TOK_INT(json, token), token++;
|
case HASH16("byteStride"): view->stride = NOM_INT(json, token); break;
|
||||||
} else if (KEY_EQ(key, "byteLength")) {
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
view->length = TOK_INT(json, token), token++;
|
|
||||||
} else if (KEY_EQ(key, "byteStride")) {
|
|
||||||
view->stride = TOK_INT(json, token), token++;
|
|
||||||
} else {
|
|
||||||
token += nomValue(json, token, 1, 0); // Skip
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,43 +316,45 @@ static void parseNodes(const char* json, jsmntok_t* token, ModelData* model) {
|
||||||
float scale[3] = { 1, 1, 1 };
|
float scale[3] = { 1, 1, 1 };
|
||||||
bool matrix = false;
|
bool matrix = false;
|
||||||
|
|
||||||
gltfString key;
|
|
||||||
int keyCount = (token++)->size; // Enter object
|
int keyCount = (token++)->size; // Enter object
|
||||||
for (int k = 0; k < keyCount; k++) {
|
for (int k = 0; k < keyCount; k++) {
|
||||||
token += nomString(json, token, &key);
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("mesh"): node->mesh = NOM_INT(json, token); break;
|
||||||
if (KEY_EQ(key, "children")) {
|
case HASH16("skin"): node->skin = NOM_INT(json, token); break;
|
||||||
node->children = &model->childMap[childIndex];
|
case HASH16("children"):
|
||||||
node->childCount = (token++)->size;
|
node->children = &model->nodeChildren[childIndex];
|
||||||
for (uint32_t j = 0; j < node->childCount; j++) {
|
node->childCount = (token++)->size;
|
||||||
model->childMap[childIndex++] = TOK_INT(json, token), token++;
|
for (uint32_t j = 0; j < node->childCount; j++) {
|
||||||
}
|
model->nodeChildren[childIndex++] = NOM_INT(json, token);
|
||||||
} else if (KEY_EQ(key, "mesh")) {
|
}
|
||||||
node->mesh = TOK_INT(json, token), token++;
|
break;
|
||||||
} else if (KEY_EQ(key, "matrix")) {
|
case HASH16("matrix"):
|
||||||
lovrAssert(token->size == 16, "Node matrix needs 16 elements");
|
lovrAssert(token->size == 16, "Node matrix needs 16 elements");
|
||||||
matrix = true;
|
matrix = true;
|
||||||
for (int j = 0; j < token->size; j++) {
|
for (int j = 0; j < token->size; j++) {
|
||||||
node->transform[j] = TOK_FLOAT(json, token), token++;
|
node->transform[j] = NOM_FLOAT(json, token);
|
||||||
}
|
}
|
||||||
} else if (KEY_EQ(key, "translation")) {
|
break;
|
||||||
lovrAssert(token->size == 3, "Node translation needs 3 elements");
|
case HASH16("translation"):
|
||||||
translation[0] = TOK_FLOAT(json, token), token++;
|
lovrAssert(token->size == 3, "Node translation needs 3 elements");
|
||||||
translation[1] = TOK_FLOAT(json, token), token++;
|
translation[0] = NOM_FLOAT(json, token);
|
||||||
translation[2] = TOK_FLOAT(json, token), token++;
|
translation[1] = NOM_FLOAT(json, token);
|
||||||
} else if (KEY_EQ(key, "rotation")) {
|
translation[2] = NOM_FLOAT(json, token);
|
||||||
lovrAssert(token->size == 4, "Node rotation needs 4 elements");
|
break;
|
||||||
rotation[0] = TOK_FLOAT(json, token), token++;
|
case HASH16("rotation"):
|
||||||
rotation[1] = TOK_FLOAT(json, token), token++;
|
lovrAssert(token->size == 4, "Node rotation needs 4 elements");
|
||||||
rotation[2] = TOK_FLOAT(json, token), token++;
|
rotation[0] = NOM_FLOAT(json, token);
|
||||||
rotation[3] = TOK_FLOAT(json, token), token++;
|
rotation[1] = NOM_FLOAT(json, token);
|
||||||
} else if (KEY_EQ(key, "scale")) {
|
rotation[2] = NOM_FLOAT(json, token);
|
||||||
lovrAssert(token->size == 3, "Node scale needs 3 elements");
|
rotation[3] = NOM_FLOAT(json, token);
|
||||||
scale[0] = TOK_FLOAT(json, token), token++;
|
break;
|
||||||
scale[1] = TOK_FLOAT(json, token), token++;
|
case HASH16("scale"):
|
||||||
scale[2] = TOK_FLOAT(json, token), token++;
|
lovrAssert(token->size == 3, "Node scale needs 3 elements");
|
||||||
} else {
|
scale[0] = NOM_FLOAT(json, token);
|
||||||
token += nomValue(json, token, 1, 0); // Skip
|
scale[1] = NOM_FLOAT(json, token);
|
||||||
|
scale[2] = NOM_FLOAT(json, token);
|
||||||
|
break;
|
||||||
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,57 +369,45 @@ static void parseNodes(const char* json, jsmntok_t* token, ModelData* model) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static jsmntok_t* parsePrimitive(const char* json, jsmntok_t* token, int index, ModelData* model) {
|
static jsmntok_t* parsePrimitive(const char* json, jsmntok_t* token, int index, ModelData* model) {
|
||||||
gltfString key;
|
|
||||||
ModelPrimitive* primitive = &model->primitives[index];
|
ModelPrimitive* primitive = &model->primitives[index];
|
||||||
int keyCount = (token++)->size; // Enter object
|
|
||||||
memset(primitive->attributes, 0xff, sizeof(primitive->attributes));
|
memset(primitive->attributes, 0xff, sizeof(primitive->attributes));
|
||||||
primitive->indices = -1;
|
primitive->indices = -1;
|
||||||
primitive->mode = DRAW_TRIANGLES;
|
primitive->mode = DRAW_TRIANGLES;
|
||||||
|
|
||||||
|
int keyCount = (token++)->size; // Enter object
|
||||||
for (int k = 0; k < keyCount; k++) {
|
for (int k = 0; k < keyCount; k++) {
|
||||||
token += nomString(json, token, &key);
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("material"): primitive->material = NOM_INT(json, token); break;
|
||||||
if (KEY_EQ(key, "material")) {
|
case HASH16("indices"): primitive->indices = NOM_INT(json, token); break;
|
||||||
primitive->material = TOK_INT(json, token), token++;
|
case HASH16("mode"):
|
||||||
} else if (KEY_EQ(key, "indices")) {
|
switch (NOM_INT(json, token)) {
|
||||||
primitive->indices = TOK_INT(json, token), token++;
|
case 0: primitive->mode = DRAW_POINTS; break;
|
||||||
} else if (KEY_EQ(key, "mode")) {
|
case 1: primitive->mode = DRAW_LINES; break;
|
||||||
switch (TOK_INT(json, token)) {
|
case 2: primitive->mode = DRAW_LINE_LOOP; break;
|
||||||
case 0: primitive->mode = DRAW_POINTS; break;
|
case 3: primitive->mode = DRAW_LINE_STRIP; break;
|
||||||
case 1: primitive->mode = DRAW_LINES; break;
|
case 4: primitive->mode = DRAW_TRIANGLES; break;
|
||||||
case 2: primitive->mode = DRAW_LINE_LOOP; break;
|
case 5: primitive->mode = DRAW_TRIANGLE_STRIP; break;
|
||||||
case 3: primitive->mode = DRAW_LINE_STRIP; break;
|
case 6: primitive->mode = DRAW_TRIANGLE_FAN; break;
|
||||||
case 4: primitive->mode = DRAW_TRIANGLES; break;
|
default: lovrThrow("Unknown primitive mode");
|
||||||
case 5: primitive->mode = DRAW_TRIANGLE_STRIP; break;
|
|
||||||
case 6: primitive->mode = DRAW_TRIANGLE_FAN; break;
|
|
||||||
default: lovrThrow("Unknown primitive mode");
|
|
||||||
}
|
|
||||||
token++;
|
|
||||||
} else if (KEY_EQ(key, "attributes")) {
|
|
||||||
int attributeCount = (token++)->size;
|
|
||||||
for (int i = 0; i < attributeCount; i++) {
|
|
||||||
gltfString name;
|
|
||||||
token += nomString(json, token, &name);
|
|
||||||
int accessor = TOK_INT(json, token);
|
|
||||||
if (KEY_EQ(name, "POSITION")) {
|
|
||||||
primitive->attributes[ATTR_POSITION] = accessor;
|
|
||||||
} else if (KEY_EQ(name, "NORMAL")) {
|
|
||||||
primitive->attributes[ATTR_NORMAL] = accessor;
|
|
||||||
} else if (KEY_EQ(name, "TEXCOORD_0")) {
|
|
||||||
primitive->attributes[ATTR_TEXCOORD] = accessor;
|
|
||||||
} else if (KEY_EQ(name, "COLOR_0")) {
|
|
||||||
primitive->attributes[ATTR_COLOR] = accessor;
|
|
||||||
} else if (KEY_EQ(name, "TANGENT")) {
|
|
||||||
primitive->attributes[ATTR_TANGENT] = accessor;
|
|
||||||
} else if (KEY_EQ(name, "JOINTS_0")) {
|
|
||||||
primitive->attributes[ATTR_BONES] = accessor;
|
|
||||||
} else if (KEY_EQ(name, "WEIGHTS_0")) {
|
|
||||||
primitive->attributes[ATTR_WEIGHTS] = accessor;
|
|
||||||
}
|
}
|
||||||
token++;
|
break;
|
||||||
|
case HASH16("attributes"): {
|
||||||
|
int attributeCount = (token++)->size;
|
||||||
|
for (int i = 0; i < attributeCount; i++) {
|
||||||
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("POSITION"): primitive->attributes[ATTR_POSITION] = NOM_INT(json, token); break;
|
||||||
|
case HASH16("NORMAL"): primitive->attributes[ATTR_NORMAL] = NOM_INT(json, token); break;
|
||||||
|
case HASH16("TEXCOORD_0"): primitive->attributes[ATTR_TEXCOORD] = NOM_INT(json, token); break;
|
||||||
|
case HASH16("COLOR_0"): primitive->attributes[ATTR_COLOR] = NOM_INT(json, token); break;
|
||||||
|
case HASH16("TANGENT"): primitive->attributes[ATTR_TANGENT] = NOM_INT(json, token); break;
|
||||||
|
case HASH16("JOINTS_0"): primitive->attributes[ATTR_BONES] = NOM_INT(json, token); break;
|
||||||
|
case HASH16("WEIGHTS_0"): primitive->attributes[ATTR_WEIGHTS] = NOM_INT(json, token); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
token += nomValue(json, token, 1, 0); // Skip
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return token;
|
return token;
|
||||||
|
@ -341,20 +419,45 @@ static void parseMeshes(const char* json, jsmntok_t* token, ModelData* model) {
|
||||||
int primitiveIndex = 0;
|
int primitiveIndex = 0;
|
||||||
int count = (token++)->size; // Enter array
|
int count = (token++)->size; // Enter array
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
gltfString key;
|
|
||||||
ModelMesh* mesh = &model->meshes[i];
|
ModelMesh* mesh = &model->meshes[i];
|
||||||
|
|
||||||
int keyCount = (token++)->size; // Enter object
|
int keyCount = (token++)->size; // Enter object
|
||||||
for (int k = 0; k < keyCount; k++) {
|
for (int k = 0; k < keyCount; k++) {
|
||||||
token += nomString(json, token, &key);
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH16("primitives"):
|
||||||
|
mesh->primitives = &model->primitives[primitiveIndex];
|
||||||
|
mesh->primitiveCount = (token++)->size;
|
||||||
|
for (uint32_t j = 0; j < mesh->primitiveCount; j++) {
|
||||||
|
token = parsePrimitive(json, token, primitiveIndex++, model);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (KEY_EQ(key, "primitives")) {
|
static void parseSkins(const char* json, jsmntok_t* token, ModelData* model) {
|
||||||
mesh->primitives = &model->primitives[primitiveIndex];
|
if (!token) return;
|
||||||
mesh->primitiveCount = (token++)->size;
|
|
||||||
for (uint32_t j = 0; j < mesh->primitiveCount; j++) {
|
int jointIndex = 0;
|
||||||
token = parsePrimitive(json, token, primitiveIndex++, model);
|
int count = (token++)->size; // Enter array
|
||||||
}
|
for (int i = 0; i < count; i++) {
|
||||||
} else {
|
ModelSkin* skin = &model->skins[i];
|
||||||
token += nomValue(json, token, 1, 0); // Skip
|
|
||||||
|
int keyCount = (token++)->size;
|
||||||
|
for (int k = 0; k < keyCount; k++) {
|
||||||
|
switch (NOM_KEY(json, token)) {
|
||||||
|
case HASH64("inverseBindMatrices"): skin->inverseBindMatrices = NOM_INT(json, token); break;
|
||||||
|
case HASH16("skeleton"): skin->skeleton = NOM_INT(json, token); break;
|
||||||
|
case HASH16("joints"):
|
||||||
|
skin->joints = &model->skinJoints[jointIndex];
|
||||||
|
skin->jointCount = (token++)->size;
|
||||||
|
for (uint32_t j = 0; j < skin->jointCount; j++) {
|
||||||
|
model->skinJoints[jointIndex++] = NOM_INT(json, token);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: token += nomValue(json, token, 1, 0); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -391,38 +494,37 @@ ModelData* lovrModelDataInit(ModelData* model, Blob* blob, ModelDataIO io) {
|
||||||
|
|
||||||
jsmn_parser parser;
|
jsmn_parser parser;
|
||||||
jsmn_init(&parser);
|
jsmn_init(&parser);
|
||||||
|
|
||||||
jsmntok_t tokens[1024]; // TODO malloc or token queue
|
jsmntok_t tokens[1024]; // TODO malloc or token queue
|
||||||
int tokenCount = jsmn_parse(&parser, jsonData, jsonLength, tokens, 1024);
|
int tokenCount = jsmn_parse(&parser, jsonData, jsonLength, tokens, 1024);
|
||||||
lovrAssert(tokenCount >= 0, "Invalid JSON");
|
lovrAssert(tokenCount >= 0, "Invalid JSON");
|
||||||
lovrAssert(tokens[0].type == JSMN_OBJECT, "No root object");
|
lovrAssert(tokens[0].type == JSMN_OBJECT, "No root object");
|
||||||
|
|
||||||
gltfInfo info = { 0 };
|
gltfInfo info = { 0 };
|
||||||
size_t dataSize = 0;
|
preparse(jsonData, tokens, tokenCount, model, &info);
|
||||||
preparse(jsonData, tokens, tokenCount, &info, &dataSize);
|
|
||||||
|
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
model->data = calloc(1, dataSize);
|
model->data = calloc(1, info.totalSize);
|
||||||
model->glbBlob = glb ? blob : NULL;
|
model->binaryBlob = glb ? blob : NULL;
|
||||||
model->accessorCount = info.accessors.count;
|
model->accessors = (ModelAccessor*) (model->data + offset), offset += model->accessorCount * sizeof(ModelAccessor);
|
||||||
model->blobCount = info.blobs.count;
|
model->animationChannels = (ModelAnimationChannel*) (model->data + offset), offset += model->animationChannelCount * sizeof(ModelAnimationChannel);
|
||||||
model->viewCount = info.views.count;
|
model->animationSamplers = (ModelAnimationSampler*) (model->data + offset), offset += model->animationSamplerCount * sizeof(ModelAnimationSampler);
|
||||||
model->meshCount = info.meshes.count;
|
model->animations = (ModelAnimation*) (model->data + offset), offset += model->animationCount * sizeof(ModelAnimation);
|
||||||
model->nodeCount = info.nodes.count;
|
model->blobs = (ModelBlob*) (model->data + offset), offset += model->blobCount * sizeof(ModelBlob);
|
||||||
model->primitiveCount = info.primitiveCount;
|
model->views = (ModelView*) (model->data + offset), offset += model->viewCount * sizeof(ModelView);
|
||||||
model->accessors = (ModelAccessor*) (model->data + offset), offset += info.accessors.count * sizeof(ModelAccessor);
|
model->primitives = (ModelPrimitive*) (model->data + offset), offset += model->primitiveCount * sizeof(ModelPrimitive);
|
||||||
model->blobs = (ModelBlob*) (model->data + offset), offset += info.blobs.count * sizeof(ModelBlob);
|
model->meshes = (ModelMesh*) (model->data + offset), offset += model->meshCount * sizeof(ModelMesh);
|
||||||
model->views = (ModelView*) (model->data + offset), offset += info.views.count * sizeof(ModelView);
|
model->nodes = (ModelNode*) (model->data + offset), offset += model->nodeCount * sizeof(ModelNode);
|
||||||
model->meshes = (ModelMesh*) (model->data + offset), offset += info.meshes.count * sizeof(ModelMesh);
|
model->skins = (ModelSkin*) (model->data + offset), offset += model->skinCount * sizeof(ModelSkin);
|
||||||
model->nodes = (ModelNode*) (model->data + offset), offset += info.nodes.count * sizeof(ModelNode);
|
model->nodeChildren = (uint32_t*) (model->data + offset), offset += info.childCount * sizeof(uint32_t);
|
||||||
model->primitives = (ModelPrimitive*) (model->data + offset), offset += info.primitiveCount * sizeof(ModelPrimitive);
|
model->skinJoints = (uint32_t*) (model->data + offset), offset += info.jointCount * sizeof(uint32_t);
|
||||||
model->childMap = (uint32_t*) (model->data + offset), offset += info.childCount * sizeof(uint32_t);
|
|
||||||
|
|
||||||
parseAccessors(jsonData, info.accessors.token, model);
|
parseAccessors(jsonData, info.accessors, model);
|
||||||
parseBlobs(jsonData, info.blobs.token, model, io, (void*) binData);
|
parseAnimations(jsonData, info.animations, model);
|
||||||
parseViews(jsonData, info.views.token, model);
|
parseBlobs(jsonData, info.blobs, model, io, (void*) binData);
|
||||||
parseNodes(jsonData, info.nodes.token, model);
|
parseViews(jsonData, info.views, model);
|
||||||
parseMeshes(jsonData, info.meshes.token, model);
|
parseNodes(jsonData, info.nodes, model);
|
||||||
|
parseMeshes(jsonData, info.meshes, model);
|
||||||
|
parseSkins(jsonData, info.skins, model);
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,18 @@ typedef enum {
|
||||||
DRAW_TRIANGLE_FAN
|
DRAW_TRIANGLE_FAN
|
||||||
} DrawMode;
|
} DrawMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SMOOTH_STEP,
|
||||||
|
SMOOTH_LINEAR,
|
||||||
|
SMOOTH_CUBIC,
|
||||||
|
} SmoothMode;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PROP_TRANSLATION,
|
||||||
|
PROP_ROTATION,
|
||||||
|
PROP_SCALE,
|
||||||
|
} AnimationProperty;
|
||||||
|
|
||||||
typedef enum { I8, U8, I16, U16, I32, U32, F32 } AttributeType;
|
typedef enum { I8, U8, I16, U16, I32, U32, F32 } AttributeType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -41,11 +53,6 @@ typedef struct {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
} gltfChunkHeader;
|
} gltfChunkHeader;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char* data;
|
|
||||||
size_t length;
|
|
||||||
} gltfString;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int view;
|
int view;
|
||||||
int count;
|
int count;
|
||||||
|
@ -55,6 +62,25 @@ typedef struct {
|
||||||
int normalized : 1;
|
int normalized : 1;
|
||||||
} ModelAccessor;
|
} ModelAccessor;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int nodeIndex;
|
||||||
|
AnimationProperty property;
|
||||||
|
int sampler;
|
||||||
|
} ModelAnimationChannel;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int times;
|
||||||
|
int values;
|
||||||
|
SmoothMode smoothing;
|
||||||
|
} ModelAnimationSampler;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ModelAnimationChannel* channels;
|
||||||
|
ModelAnimationSampler* samplers;
|
||||||
|
int channelCount;
|
||||||
|
int samplerCount;
|
||||||
|
} ModelAnimation;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* data;
|
void* data;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
@ -84,8 +110,16 @@ typedef struct {
|
||||||
uint32_t* children;
|
uint32_t* children;
|
||||||
uint32_t childCount;
|
uint32_t childCount;
|
||||||
int mesh;
|
int mesh;
|
||||||
|
int skin;
|
||||||
} ModelNode;
|
} ModelNode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t* joints;
|
||||||
|
uint32_t jointCount;
|
||||||
|
int skeleton;
|
||||||
|
int inverseBindMatrices;
|
||||||
|
} ModelSkin;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
double time;
|
double time;
|
||||||
float data[4];
|
float data[4];
|
||||||
|
@ -112,20 +146,29 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Ref ref;
|
Ref ref;
|
||||||
uint8_t* data;
|
uint8_t* data;
|
||||||
Blob* glbBlob;
|
Blob* binaryBlob;
|
||||||
ModelAccessor* accessors;
|
ModelAccessor* accessors;
|
||||||
|
ModelAnimationChannel* animationChannels;
|
||||||
|
ModelAnimationSampler* animationSamplers;
|
||||||
|
ModelAnimation* animations;
|
||||||
ModelBlob* blobs;
|
ModelBlob* blobs;
|
||||||
ModelView* views;
|
ModelView* views;
|
||||||
|
ModelPrimitive* primitives;
|
||||||
ModelMesh* meshes;
|
ModelMesh* meshes;
|
||||||
ModelNode* nodes;
|
ModelNode* nodes;
|
||||||
ModelPrimitive* primitives;
|
ModelSkin* skins;
|
||||||
uint32_t* childMap;
|
|
||||||
int accessorCount;
|
int accessorCount;
|
||||||
|
int animationChannelCount;
|
||||||
|
int animationSamplerCount;
|
||||||
|
int animationCount;
|
||||||
int blobCount;
|
int blobCount;
|
||||||
int viewCount;
|
int viewCount;
|
||||||
|
int primitiveCount;
|
||||||
int meshCount;
|
int meshCount;
|
||||||
int nodeCount;
|
int nodeCount;
|
||||||
int primitiveCount;
|
int skinCount;
|
||||||
|
uint32_t* nodeChildren;
|
||||||
|
uint32_t* skinJoints;
|
||||||
} ModelData;
|
} ModelData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -36,6 +36,14 @@
|
||||||
#define CLAMP(x, min, max) MAX(min, MIN(max, x))
|
#define CLAMP(x, min, max) MAX(min, MIN(max, x))
|
||||||
#define ALIGN(p, n) ((uintptr_t) p & -n)
|
#define ALIGN(p, n) ((uintptr_t) p & -n)
|
||||||
|
|
||||||
|
#define STRLEN(s) (sizeof(s) / sizeof(s[0]) - 1)
|
||||||
|
#define H1(s, i, x) ((x * 65599u) + (uint8_t) s[(i) < STRLEN(s) ? STRLEN(s) - 1 - (i) : STRLEN(s)])
|
||||||
|
#define H4(s, i, x) H1(s, i + 0, H1(s, i + 1, H1(s, i + 2, H1(s, i + 3, x))))
|
||||||
|
#define H16(s, i, x) H4(s, i + 0, H4(s, i + 4, H4(s, i + 8, H4(s, i + 12, x))))
|
||||||
|
#define H64(s, i, x) H16(s, i + 0, H16(s, i + 16, H16(s, i + 32, H16(s, i + 48, x))))
|
||||||
|
#define HASH16(s) ((uint32_t) (H16(s, 0, 0) ^ (H16(s, 0, 0) >> 16)))
|
||||||
|
#define HASH64(s) ((uint32_t) (H64(s, 0, 0) ^ (H64(s, 0, 0) >> 16)))
|
||||||
|
|
||||||
typedef struct ref {
|
typedef struct ref {
|
||||||
void (*destructor)(void*);
|
void (*destructor)(void*);
|
||||||
const char* type;
|
const char* type;
|
||||||
|
|
Loading…
Reference in New Issue