2018-02-11 23:22:04 +00:00
|
|
|
#include "data/modelData.h"
|
2018-11-15 16:03:51 +00:00
|
|
|
#include "lib/math.h"
|
2018-09-26 00:10:09 +00:00
|
|
|
#include "lib/jsmn/jsmn.h"
|
|
|
|
#include <stdbool.h>
|
2018-12-19 04:57:47 +00:00
|
|
|
#include <stdlib.h>
|
2018-09-26 00:10:09 +00:00
|
|
|
|
|
|
|
#define MAGIC_glTF 0x46546c67
|
|
|
|
#define MAGIC_JSON 0x4e4f534a
|
|
|
|
#define MAGIC_BIN 0x004e4942
|
|
|
|
|
|
|
|
#define KEY_EQ(k, s) !strncmp(k.data, s, k.length)
|
2018-12-19 04:57:47 +00:00
|
|
|
#define NOM_VALUE(j, t) nomValue(j, t, 1, 0)
|
2018-12-16 01:27:23 +00:00
|
|
|
#define NOM_KEY(j, t) hashKey((char*) j + (t++)->start)
|
|
|
|
#define NOM_INT(j, t) strtol(j + (t++)->start, NULL, 10)
|
|
|
|
#define NOM_BOOL(j, t) (*(j + (t++)->start) == 't')
|
|
|
|
#define NOM_FLOAT(j, t) strtof(j + (t++)->start, NULL)
|
2018-09-26 00:10:09 +00:00
|
|
|
|
|
|
|
typedef struct {
|
2018-12-16 01:27:23 +00:00
|
|
|
size_t totalSize;
|
|
|
|
jsmntok_t* accessors;
|
|
|
|
jsmntok_t* animations;
|
|
|
|
jsmntok_t* blobs;
|
|
|
|
jsmntok_t* views;
|
2018-12-17 22:40:07 +00:00
|
|
|
jsmntok_t* images;
|
2018-12-18 22:27:46 +00:00
|
|
|
jsmntok_t* samplers;
|
2018-12-19 03:36:16 +00:00
|
|
|
jsmntok_t* textures;
|
2018-12-19 03:57:52 +00:00
|
|
|
jsmntok_t* materials;
|
2018-12-16 01:27:23 +00:00
|
|
|
jsmntok_t* meshes;
|
2018-12-19 03:57:52 +00:00
|
|
|
jsmntok_t* nodes;
|
2018-12-16 01:27:23 +00:00
|
|
|
jsmntok_t* skins;
|
2018-09-26 00:10:09 +00:00
|
|
|
int childCount;
|
2018-12-16 01:27:23 +00:00
|
|
|
int jointCount;
|
2018-09-26 00:10:09 +00:00
|
|
|
} gltfInfo;
|
|
|
|
|
2018-12-19 04:57:47 +00:00
|
|
|
typedef struct {
|
|
|
|
uint32_t magic;
|
|
|
|
uint32_t version;
|
|
|
|
uint32_t length;
|
|
|
|
} gltfHeader;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint32_t length;
|
|
|
|
uint32_t type;
|
|
|
|
} gltfChunkHeader;
|
|
|
|
|
2018-12-16 01:27:23 +00:00
|
|
|
static uint32_t hashKey(char* key) {
|
|
|
|
uint32_t hash = 0;
|
|
|
|
for (int i = 0; key[i] != '"'; i++) {
|
|
|
|
hash = (hash * 65599) + key[i];
|
|
|
|
}
|
|
|
|
return hash ^ (hash >> 16);
|
2018-01-30 05:44:32 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
static int nomValue(const char* data, jsmntok_t* token, int count, int sum) {
|
|
|
|
if (count == 0) { return sum; }
|
|
|
|
switch (token->type) {
|
|
|
|
case JSMN_OBJECT: return nomValue(data, token + 1, count - 1 + 2 * token->size, sum + 1);
|
|
|
|
case JSMN_ARRAY: return nomValue(data, token + 1, count - 1 + token->size, sum + 1);
|
|
|
|
default: return nomValue(data, token + 1, count - 1, sum + 1);
|
2017-11-21 05:19:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-16 01:27:23 +00:00
|
|
|
// Kinda like total += sum(map(arr, obj => #obj[key]))
|
|
|
|
static jsmntok_t* aggregate(const char* json, jsmntok_t* token, uint32_t hash, int* total) {
|
2018-09-26 00:10:09 +00:00
|
|
|
int size = (token++)->size;
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
if (token->size > 0) {
|
|
|
|
int keys = (token++)->size;
|
|
|
|
for (int k = 0; k < keys; k++) {
|
2018-12-16 01:27:23 +00:00
|
|
|
if (NOM_KEY(json, token) == hash) {
|
2018-09-26 00:10:09 +00:00
|
|
|
*total += token->size;
|
|
|
|
}
|
2018-12-19 04:57:47 +00:00
|
|
|
token += NOM_VALUE(json, token);
|
2018-09-21 22:31:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-26 00:10:09 +00:00
|
|
|
return token;
|
2018-01-30 05:44:32 +00:00
|
|
|
}
|
2017-10-22 00:35:50 +00:00
|
|
|
|
2018-12-16 01:27:23 +00:00
|
|
|
static void preparse(const char* json, jsmntok_t* tokens, int tokenCount, ModelData* model, gltfInfo* info) {
|
2018-09-26 00:10:09 +00:00
|
|
|
for (jsmntok_t* token = tokens + 1; token < tokens + tokenCount;) { // +1 to skip root object
|
2018-12-16 01:27:23 +00:00
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("accessors"):
|
|
|
|
info->accessors = token;
|
|
|
|
model->accessorCount = token->size;
|
|
|
|
info->totalSize += token->size * sizeof(ModelAccessor);
|
2018-12-19 04:57:47 +00:00
|
|
|
token += NOM_VALUE(json, token);
|
2018-12-16 01:27:23 +00:00
|
|
|
break;
|
|
|
|
case HASH16("animations"):
|
|
|
|
info->animations = token;
|
|
|
|
model->animationCount = token->size;
|
|
|
|
info->totalSize += token->size * sizeof(ModelAnimation);
|
|
|
|
// Almost like aggregate, but we gotta aggregate 2 keys in a single pass
|
|
|
|
int size = (token++)->size;
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
if (token->size > 0) {
|
|
|
|
int keyCount = (token++)->size;
|
|
|
|
for (int k = 0; k < keyCount; k++) {
|
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("channels"): model->animationChannelCount += token->size; break;
|
|
|
|
case HASH16("samplers"): model->animationSamplerCount += token->size; break;
|
|
|
|
default: break;
|
|
|
|
}
|
2018-12-19 04:57:47 +00:00
|
|
|
token += NOM_VALUE(json, token);
|
2018-12-16 01:27:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HASH16("buffers"):
|
|
|
|
info->blobs = token;
|
|
|
|
model->blobCount = token->size;
|
|
|
|
info->totalSize += token->size * sizeof(ModelBlob);
|
2018-12-19 04:57:47 +00:00
|
|
|
token += NOM_VALUE(json, token);
|
2018-12-16 01:27:23 +00:00
|
|
|
break;
|
|
|
|
case HASH16("bufferViews"):
|
|
|
|
info->views = token;
|
|
|
|
model->viewCount = token->size;
|
|
|
|
info->totalSize += token->size * sizeof(ModelView);
|
2018-12-19 04:57:47 +00:00
|
|
|
token += NOM_VALUE(json, token);
|
2018-12-16 01:27:23 +00:00
|
|
|
break;
|
2018-12-17 22:40:07 +00:00
|
|
|
case HASH16("images"):
|
|
|
|
info->images = token;
|
|
|
|
model->imageCount = token->size;
|
|
|
|
info->totalSize += token->size * sizeof(TextureData);
|
2018-12-19 04:57:47 +00:00
|
|
|
token += NOM_VALUE(json, token);
|
2018-12-16 01:27:23 +00:00
|
|
|
break;
|
2018-12-19 03:57:52 +00:00
|
|
|
case HASH16("samplers"):
|
|
|
|
info->samplers = token;
|
|
|
|
model->samplerCount = token->size;
|
|
|
|
info->totalSize += token->size * sizeof(ModelSampler);
|
2018-12-19 04:57:47 +00:00
|
|
|
token += NOM_VALUE(json, token);
|
2018-12-19 03:57:52 +00:00
|
|
|
break;
|
|
|
|
case HASH16("textures"):
|
|
|
|
info->samplers = token;
|
|
|
|
model->textureCount = token->size;
|
|
|
|
info->totalSize += token->size * sizeof(ModelTexture);
|
2018-12-19 04:57:47 +00:00
|
|
|
token += NOM_VALUE(json, token);
|
2018-12-19 03:57:52 +00:00
|
|
|
break;
|
|
|
|
case HASH16("materials"):
|
|
|
|
info->materials = token;
|
|
|
|
model->materialCount = token->size;
|
|
|
|
info->totalSize += token->size * sizeof(ModelMaterial);
|
2018-12-19 04:57:47 +00:00
|
|
|
token += NOM_VALUE(json, token);
|
2018-12-19 03:57:52 +00:00
|
|
|
break;
|
2018-12-16 01:27:23 +00:00
|
|
|
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;
|
2018-12-17 22:40:07 +00:00
|
|
|
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;
|
2018-12-16 01:27:23 +00:00
|
|
|
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;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-09-26 00:10:09 +00:00
|
|
|
}
|
2018-01-30 05:44:32 +00:00
|
|
|
}
|
2017-10-21 07:19:05 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
static void parseAccessors(const char* json, jsmntok_t* token, ModelData* model) {
|
|
|
|
if (!token) return;
|
|
|
|
|
|
|
|
int count = (token++)->size;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
ModelAccessor* accessor = &model->accessors[i];
|
|
|
|
|
2018-12-16 01:27:23 +00:00
|
|
|
int keyCount = (token++)->size;
|
2018-09-26 00:10:09 +00:00
|
|
|
for (int k = 0; k < keyCount; k++) {
|
2018-12-16 01:27:23 +00:00
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("bufferView"): accessor->view = NOM_INT(json, token); break;
|
|
|
|
case HASH16("count"): accessor->count = NOM_INT(json, token); break;
|
|
|
|
case HASH16("byteOffset"): accessor->offset = NOM_INT(json, token); break;
|
|
|
|
case HASH16("normalized"): accessor->normalized = NOM_BOOL(json, token); break;
|
|
|
|
case HASH16("componentType"):
|
|
|
|
switch (NOM_INT(json, token)) {
|
|
|
|
case 5120: accessor->type = I8; break;
|
|
|
|
case 5121: accessor->type = U8; break;
|
|
|
|
case 5122: accessor->type = I16; break;
|
|
|
|
case 5123: accessor->type = U16; break;
|
|
|
|
case 5125: accessor->type = U32; 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;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-12-16 01:27:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-12-16 01:27:23 +00:00
|
|
|
}
|
2018-09-26 00:10:09 +00:00
|
|
|
}
|
2018-12-16 01:27:23 +00:00
|
|
|
}
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-12-16 01:27:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
static jsmntok_t* parseAnimationSampler(const char* json, jsmntok_t* token, int index, ModelData* model) {
|
|
|
|
ModelAnimationSampler* sampler = &model->animationSamplers[index];
|
|
|
|
int keyCount = (token++)->size;
|
|
|
|
for (int k = 0; k < keyCount; k++) {
|
|
|
|
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;
|
2018-09-26 00:10:09 +00:00
|
|
|
}
|
2018-12-16 01:27:23 +00:00
|
|
|
break;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token);
|
2018-12-16 01:27:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-09-26 00:10:09 +00:00
|
|
|
}
|
|
|
|
}
|
2017-10-22 00:35:50 +00:00
|
|
|
}
|
2017-10-21 07:19:05 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
static void parseBlobs(const char* json, jsmntok_t* token, ModelData* model, ModelDataIO io, void* binData) {
|
|
|
|
if (!token) return;
|
|
|
|
|
|
|
|
int count = (token++)->size;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
ModelBlob* blob = &model->blobs[i];
|
|
|
|
size_t bytesRead = 0;
|
|
|
|
bool hasUri = false;
|
|
|
|
|
2018-12-16 01:27:23 +00:00
|
|
|
int keyCount = (token++)->size;
|
2018-09-26 00:10:09 +00:00
|
|
|
for (int k = 0; k < keyCount; k++) {
|
2018-12-16 01:27:23 +00:00
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("byteLength"): blob->size = NOM_INT(json, token); break;
|
|
|
|
case HASH16("uri"):
|
|
|
|
hasUri = true;
|
|
|
|
char* filename = (char*) json + token->start;
|
|
|
|
size_t length = token->end - token->start;
|
|
|
|
filename[length] = '\0'; // Change the quote into a terminator (I'll be b0k)
|
|
|
|
blob->data = io.read(filename, &bytesRead);
|
|
|
|
lovrAssert(blob->data, "Unable to read %s", filename);
|
|
|
|
filename[length] = '"';
|
|
|
|
break;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-09-26 00:10:09 +00:00
|
|
|
}
|
2017-10-22 00:35:50 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
if (hasUri) {
|
|
|
|
lovrAssert(bytesRead == blob->size, "Couldn't read all of buffer data");
|
|
|
|
} else {
|
|
|
|
lovrAssert(binData && i == 0, "Buffer is missing URI");
|
|
|
|
blob->data = binData;
|
|
|
|
}
|
2017-10-21 07:19:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
static void parseViews(const char* json, jsmntok_t* token, ModelData* model) {
|
|
|
|
if (!token) return;
|
|
|
|
|
|
|
|
int count = (token++)->size;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
ModelView* view = &model->views[i];
|
|
|
|
|
2018-12-16 01:27:23 +00:00
|
|
|
int keyCount = (token++)->size;
|
2018-09-26 00:10:09 +00:00
|
|
|
for (int k = 0; k < keyCount; k++) {
|
2018-12-16 01:27:23 +00:00
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("buffer"): view->blob = NOM_INT(json, token); break;
|
|
|
|
case HASH16("byteOffset"): view->offset = NOM_INT(json, token); break;
|
|
|
|
case HASH16("byteLength"): view->length = NOM_INT(json, token); break;
|
|
|
|
case HASH16("byteStride"): view->stride = NOM_INT(json, token); break;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-09-26 00:10:09 +00:00
|
|
|
}
|
|
|
|
}
|
2017-10-21 07:19:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 22:40:07 +00:00
|
|
|
static void parseImages(const char* json, jsmntok_t* token, ModelData* model, ModelDataIO io) {
|
2018-09-26 00:10:09 +00:00
|
|
|
if (!token) return;
|
|
|
|
|
2018-12-17 22:40:07 +00:00
|
|
|
int count = (token++)->size;
|
2018-09-26 00:10:09 +00:00
|
|
|
for (int i = 0; i < count; i++) {
|
2018-12-17 22:40:07 +00:00
|
|
|
TextureData** image = &model->images[i];
|
|
|
|
int keyCount = (token++)->size;
|
2018-09-26 00:10:09 +00:00
|
|
|
for (int k = 0; k < keyCount; k++) {
|
2018-12-16 01:27:23 +00:00
|
|
|
switch (NOM_KEY(json, token)) {
|
2018-12-17 22:40:07 +00:00
|
|
|
case HASH16("bufferView"): {
|
|
|
|
int viewIndex = NOM_INT(json, token);
|
|
|
|
ModelView* view = &model->views[viewIndex];
|
|
|
|
void* data = (uint8_t*) model->blobs[view->blob].data + view->offset;
|
|
|
|
Blob* blob = lovrBlobCreate(data, view->length, NULL);
|
|
|
|
*image = lovrTextureDataCreateFromBlob(blob, true);
|
|
|
|
blob->data = NULL; // FIXME
|
|
|
|
lovrRelease(blob);
|
2018-12-16 01:27:23 +00:00
|
|
|
break;
|
2018-12-17 22:40:07 +00:00
|
|
|
}
|
|
|
|
case HASH16("uri"): {
|
|
|
|
size_t size = 0;
|
|
|
|
char* uri = (char*) json + token->start;
|
|
|
|
size_t length = token->end - token->start;
|
|
|
|
uri[length] = '\0'; // Change the quote into a terminator (I'll be b0k)
|
|
|
|
void* data = io.read(uri, &size);
|
|
|
|
lovrAssert(data && size > 0, "Unable to read image at '%s'", uri);
|
|
|
|
uri[length] = '"';
|
|
|
|
Blob* blob = lovrBlobCreate(data, size, NULL);
|
|
|
|
*image = lovrTextureDataCreateFromBlob(blob, true);
|
|
|
|
lovrRelease(blob);
|
2018-12-16 01:27:23 +00:00
|
|
|
break;
|
2018-12-17 22:40:07 +00:00
|
|
|
}
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2016-10-29 22:18:10 +00:00
|
|
|
}
|
|
|
|
}
|
2018-09-26 00:10:09 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-02 06:10:21 +00:00
|
|
|
|
2018-12-18 22:27:46 +00:00
|
|
|
static void parseSamplers(const char* json, jsmntok_t* token, ModelData* model) {
|
|
|
|
if (!token) return;
|
|
|
|
|
|
|
|
int count = (token++)->size;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
ModelSampler* sampler = &model->samplers[i];
|
|
|
|
sampler->wrap.s = sampler->wrap.t = sampler->wrap.r = WRAP_REPEAT;
|
|
|
|
int min = -1;
|
|
|
|
int mag = -1;
|
|
|
|
|
|
|
|
int keyCount = (token++)->size;
|
|
|
|
for (int k = 0; k < keyCount; k++) {
|
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("magFilter"): min = NOM_INT(json, token); break;
|
|
|
|
case HASH16("minFilter"): mag = NOM_INT(json, token); break;
|
|
|
|
case HASH16("wrapS"):
|
|
|
|
switch (NOM_INT(json, token)) {
|
|
|
|
case 33071: sampler->wrap.s = WRAP_CLAMP;
|
|
|
|
case 33648: sampler->wrap.s = WRAP_MIRRORED_REPEAT;
|
|
|
|
case 10497: sampler->wrap.s = WRAP_REPEAT;
|
|
|
|
default: lovrThrow("Unknown sampler wrapS mode for sampler %d", i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HASH16("wrapT"):
|
|
|
|
switch (NOM_INT(json, token)) {
|
|
|
|
case 33071: sampler->wrap.t = WRAP_CLAMP;
|
|
|
|
case 33648: sampler->wrap.t = WRAP_MIRRORED_REPEAT;
|
|
|
|
case 10497: sampler->wrap.t = WRAP_REPEAT;
|
|
|
|
default: lovrThrow("Unknown sampler wrapT mode for sampler %d", i);
|
|
|
|
}
|
|
|
|
break;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-12-18 22:27:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (min == 9728 || min == 9984 || min == 9986 || mag == 9728) {
|
|
|
|
sampler->filter.mode = FILTER_NEAREST;
|
|
|
|
} else {
|
|
|
|
switch (min) {
|
|
|
|
case 9729: sampler->filter.mode = FILTER_BILINEAR, sampler->mipmaps = false; break;
|
|
|
|
case 9985: sampler->filter.mode = FILTER_BILINEAR, sampler->mipmaps = true; break;
|
|
|
|
case 9987: sampler->filter.mode = FILTER_TRILINEAR, sampler->mipmaps = true; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-19 03:36:16 +00:00
|
|
|
static void parseTextures(const char* json, jsmntok_t* token, ModelData* model) {
|
|
|
|
if (!token) return;
|
|
|
|
|
|
|
|
int count = (token++)->size;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
ModelTexture* texture = &model->textures[i];
|
|
|
|
int keyCount = (token++)->size;
|
|
|
|
for (int k = 0; k < keyCount; k++) {
|
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("source"): texture->image = NOM_INT(json, token); break;
|
|
|
|
case HASH16("sampler"): texture->sampler = NOM_INT(json, token); break;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-12-19 03:36:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-19 03:57:52 +00:00
|
|
|
static jsmntok_t* parseTextureInfo(const char* json, jsmntok_t* token, int* dest) {
|
|
|
|
int keyCount = (token++)->size;
|
|
|
|
for (int k = 0; k < keyCount; k++) {
|
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("index"): *dest = NOM_INT(json, token); break;
|
|
|
|
case HASH16("texCoord"): lovrAssert(NOM_INT(json, token) == 0, "Only one set of texture coordinates is supported"); break;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-12-19 03:57:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseMaterials(const char* json, jsmntok_t* token, ModelData* model) {
|
|
|
|
if (!token) return;
|
|
|
|
|
|
|
|
int count = (token++)->size;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
ModelMaterial* material = &model->materials[i];
|
|
|
|
int keyCount = (token++)->size;
|
|
|
|
for (int k = 0; k < keyCount; k++) {
|
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH64("pbrMetallicRoughness"): {
|
|
|
|
int pbrKeyCount = (token++)->size;
|
|
|
|
for (int j = 0; j < pbrKeyCount; j++) {
|
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("baseColorFactor"):
|
|
|
|
token++; // Enter array
|
|
|
|
material->colors[COLOR_DIFFUSE].r = NOM_FLOAT(json, token);
|
|
|
|
material->colors[COLOR_DIFFUSE].g = NOM_FLOAT(json, token);
|
|
|
|
material->colors[COLOR_DIFFUSE].b = NOM_FLOAT(json, token);
|
|
|
|
material->colors[COLOR_DIFFUSE].a = NOM_FLOAT(json, token);
|
|
|
|
break;
|
|
|
|
case HASH16("baseColorTexture"):
|
|
|
|
token = parseTextureInfo(json, token, &material->textures[TEXTURE_DIFFUSE]);
|
|
|
|
break;
|
|
|
|
case HASH16("metallicFactor"): material->scalars[SCALAR_METALNESS] = NOM_FLOAT(json, token); break;
|
|
|
|
case HASH16("roughnessFactor"): material->scalars[SCALAR_ROUGHNESS] = NOM_FLOAT(json, token); break;
|
|
|
|
case HASH64("metallicRoughnessTexture"):
|
|
|
|
token = parseTextureInfo(json, token, &material->textures[TEXTURE_METALNESS]);
|
|
|
|
material->textures[TEXTURE_ROUGHNESS] = material->textures[TEXTURE_METALNESS];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case HASH16("normalTexture"): token = parseTextureInfo(json, token, &material->textures[TEXTURE_NORMAL]); break;
|
|
|
|
case HASH16("occlusionTexture"): token = parseTextureInfo(json, token, &material->textures[TEXTURE_OCCLUSION]); break;
|
|
|
|
case HASH16("emissiveTexture"): token = parseTextureInfo(json, token, &material->textures[TEXTURE_EMISSIVE]); break;
|
|
|
|
case HASH16("emissiveFactor"):
|
|
|
|
token++; // Enter array
|
|
|
|
material->colors[COLOR_EMISSIVE].r = NOM_FLOAT(json, token);
|
|
|
|
material->colors[COLOR_EMISSIVE].g = NOM_FLOAT(json, token);
|
|
|
|
material->colors[COLOR_EMISSIVE].b = NOM_FLOAT(json, token);
|
|
|
|
material->colors[COLOR_EMISSIVE].a = NOM_FLOAT(json, token);
|
|
|
|
break;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-12-19 03:57:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
static jsmntok_t* parsePrimitive(const char* json, jsmntok_t* token, int index, ModelData* model) {
|
|
|
|
ModelPrimitive* primitive = &model->primitives[index];
|
|
|
|
memset(primitive->attributes, 0xff, sizeof(primitive->attributes));
|
|
|
|
primitive->indices = -1;
|
|
|
|
primitive->mode = DRAW_TRIANGLES;
|
|
|
|
|
2018-12-16 01:27:23 +00:00
|
|
|
int keyCount = (token++)->size; // Enter object
|
2018-09-26 00:10:09 +00:00
|
|
|
for (int k = 0; k < keyCount; k++) {
|
2018-12-16 01:27:23 +00:00
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("material"): primitive->material = NOM_INT(json, token); break;
|
|
|
|
case HASH16("indices"): primitive->indices = NOM_INT(json, token); break;
|
|
|
|
case HASH16("mode"):
|
|
|
|
switch (NOM_INT(json, token)) {
|
|
|
|
case 0: primitive->mode = DRAW_POINTS; break;
|
|
|
|
case 1: primitive->mode = DRAW_LINES; break;
|
|
|
|
case 2: primitive->mode = DRAW_LINE_LOOP; break;
|
|
|
|
case 3: primitive->mode = DRAW_LINE_STRIP; break;
|
|
|
|
case 4: primitive->mode = DRAW_TRIANGLES; break;
|
|
|
|
case 5: primitive->mode = DRAW_TRIANGLE_STRIP; break;
|
|
|
|
case 6: primitive->mode = DRAW_TRIANGLE_FAN; break;
|
|
|
|
default: lovrThrow("Unknown primitive mode");
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2017-11-02 06:10:21 +00:00
|
|
|
}
|
2018-12-16 01:27:23 +00:00
|
|
|
break;
|
2017-11-02 06:10:21 +00:00
|
|
|
}
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2017-11-02 06:10:21 +00:00
|
|
|
}
|
2016-10-09 04:52:58 +00:00
|
|
|
}
|
2018-09-26 00:10:09 +00:00
|
|
|
return token;
|
|
|
|
}
|
2016-10-29 22:18:10 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
static void parseMeshes(const char* json, jsmntok_t* token, ModelData* model) {
|
|
|
|
if (!token) return;
|
|
|
|
|
|
|
|
int primitiveIndex = 0;
|
|
|
|
int count = (token++)->size; // Enter array
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
ModelMesh* mesh = &model->meshes[i];
|
2018-12-16 01:27:23 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
int keyCount = (token++)->size; // Enter object
|
|
|
|
for (int k = 0; k < keyCount; k++) {
|
2018-12-16 01:27:23 +00:00
|
|
|
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;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-12-16 01:27:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-26 00:10:09 +00:00
|
|
|
|
2018-12-17 22:40:07 +00:00
|
|
|
static void parseNodes(const char* json, jsmntok_t* token, ModelData* model) {
|
|
|
|
if (!token) return;
|
|
|
|
|
|
|
|
int childIndex = 0;
|
|
|
|
int count = (token++)->size; // Enter array
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
ModelNode* node = &model->nodes[i];
|
|
|
|
float translation[3] = { 0, 0, 0 };
|
|
|
|
float rotation[4] = { 0, 0, 0, 0 };
|
|
|
|
float scale[3] = { 1, 1, 1 };
|
|
|
|
bool matrix = false;
|
|
|
|
|
|
|
|
int keyCount = (token++)->size; // Enter object
|
|
|
|
for (int k = 0; k < keyCount; k++) {
|
|
|
|
switch (NOM_KEY(json, token)) {
|
|
|
|
case HASH16("mesh"): node->mesh = NOM_INT(json, token); break;
|
|
|
|
case HASH16("skin"): node->skin = NOM_INT(json, token); break;
|
|
|
|
case HASH16("children"):
|
|
|
|
node->children = &model->nodeChildren[childIndex];
|
|
|
|
node->childCount = (token++)->size;
|
|
|
|
for (uint32_t j = 0; j < node->childCount; j++) {
|
|
|
|
model->nodeChildren[childIndex++] = NOM_INT(json, token);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HASH16("matrix"):
|
|
|
|
lovrAssert(token->size == 16, "Node matrix needs 16 elements");
|
|
|
|
matrix = true;
|
|
|
|
for (int j = 0; j < token->size; j++) {
|
|
|
|
node->transform[j] = NOM_FLOAT(json, token);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HASH16("translation"):
|
|
|
|
lovrAssert(token->size == 3, "Node translation needs 3 elements");
|
|
|
|
translation[0] = NOM_FLOAT(json, token);
|
|
|
|
translation[1] = NOM_FLOAT(json, token);
|
|
|
|
translation[2] = NOM_FLOAT(json, token);
|
|
|
|
break;
|
|
|
|
case HASH16("rotation"):
|
|
|
|
lovrAssert(token->size == 4, "Node rotation needs 4 elements");
|
|
|
|
rotation[0] = NOM_FLOAT(json, token);
|
|
|
|
rotation[1] = NOM_FLOAT(json, token);
|
|
|
|
rotation[2] = NOM_FLOAT(json, token);
|
|
|
|
rotation[3] = NOM_FLOAT(json, token);
|
|
|
|
break;
|
|
|
|
case HASH16("scale"):
|
|
|
|
lovrAssert(token->size == 3, "Node scale needs 3 elements");
|
|
|
|
scale[0] = NOM_FLOAT(json, token);
|
|
|
|
scale[1] = NOM_FLOAT(json, token);
|
|
|
|
scale[2] = NOM_FLOAT(json, token);
|
|
|
|
break;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2018-12-17 22:40:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fix it in post
|
|
|
|
if (!matrix) {
|
|
|
|
mat4_identity(node->transform);
|
|
|
|
mat4_translate(node->transform, translation[0], translation[1], translation[2]);
|
|
|
|
mat4_rotateQuat(node->transform, rotation);
|
|
|
|
mat4_scale(node->transform, scale[0], scale[1], scale[2]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-16 01:27:23 +00:00
|
|
|
static void parseSkins(const char* json, jsmntok_t* token, ModelData* model) {
|
|
|
|
if (!token) return;
|
|
|
|
|
|
|
|
int jointIndex = 0;
|
|
|
|
int count = (token++)->size; // Enter array
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
ModelSkin* skin = &model->skins[i];
|
|
|
|
|
|
|
|
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;
|
2018-12-19 04:57:47 +00:00
|
|
|
default: token += NOM_VALUE(json, token); break;
|
2017-11-02 06:10:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-21 22:31:07 +00:00
|
|
|
}
|
2016-10-04 04:54:27 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
ModelData* lovrModelDataInit(ModelData* model, Blob* blob, ModelDataIO io) {
|
|
|
|
uint8_t* data = blob->data;
|
|
|
|
gltfHeader* header = (gltfHeader*) data;
|
|
|
|
bool glb = header->magic == MAGIC_glTF;
|
|
|
|
const char *jsonData, *binData;
|
|
|
|
size_t jsonLength, binLength;
|
2018-01-23 02:24:39 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
if (glb) {
|
|
|
|
gltfChunkHeader* jsonHeader = (gltfChunkHeader*) &header[1];
|
|
|
|
lovrAssert(jsonHeader->type == MAGIC_JSON, "Invalid JSON header");
|
2017-02-19 09:54:58 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
jsonData = (char*) &jsonHeader[1];
|
|
|
|
jsonLength = jsonHeader->length;
|
2017-11-25 19:59:27 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
gltfChunkHeader* binHeader = (gltfChunkHeader*) &jsonData[jsonLength];
|
|
|
|
lovrAssert(binHeader->type == MAGIC_BIN, "Invalid BIN header");
|
2017-10-21 22:20:16 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
binData = (char*) &binHeader[1];
|
|
|
|
binLength = binHeader->length;
|
2017-11-21 05:19:46 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
// Hang onto the data since it's already here rather than make a copy of it
|
|
|
|
lovrRetain(blob);
|
|
|
|
} else {
|
|
|
|
jsonData = (char*) data;
|
|
|
|
jsonLength = blob->size;
|
|
|
|
binData = NULL;
|
|
|
|
binLength = 0;
|
|
|
|
}
|
2018-02-11 01:27:29 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
jsmn_parser parser;
|
|
|
|
jsmn_init(&parser);
|
2018-12-19 04:57:47 +00:00
|
|
|
int tokenCount = jsmn_parse(&parser, jsonData, jsonLength, NULL, 0);
|
|
|
|
jsmntok_t* tokens = malloc(tokenCount * sizeof(jsmntok_t));
|
|
|
|
jsmn_init(&parser);
|
|
|
|
tokenCount = jsmn_parse(&parser, jsonData, jsonLength, tokens, tokenCount);
|
2018-09-26 00:10:09 +00:00
|
|
|
lovrAssert(tokenCount >= 0, "Invalid JSON");
|
|
|
|
lovrAssert(tokens[0].type == JSMN_OBJECT, "No root object");
|
|
|
|
|
|
|
|
gltfInfo info = { 0 };
|
2018-12-16 01:27:23 +00:00
|
|
|
preparse(jsonData, tokens, tokenCount, model, &info);
|
2018-09-26 00:10:09 +00:00
|
|
|
|
|
|
|
size_t offset = 0;
|
2018-12-16 01:27:23 +00:00
|
|
|
model->data = calloc(1, info.totalSize);
|
|
|
|
model->binaryBlob = glb ? blob : NULL;
|
|
|
|
model->accessors = (ModelAccessor*) (model->data + offset), offset += model->accessorCount * sizeof(ModelAccessor);
|
|
|
|
model->animationChannels = (ModelAnimationChannel*) (model->data + offset), offset += model->animationChannelCount * sizeof(ModelAnimationChannel);
|
|
|
|
model->animationSamplers = (ModelAnimationSampler*) (model->data + offset), offset += model->animationSamplerCount * sizeof(ModelAnimationSampler);
|
|
|
|
model->animations = (ModelAnimation*) (model->data + offset), offset += model->animationCount * sizeof(ModelAnimation);
|
|
|
|
model->blobs = (ModelBlob*) (model->data + offset), offset += model->blobCount * sizeof(ModelBlob);
|
|
|
|
model->views = (ModelView*) (model->data + offset), offset += model->viewCount * sizeof(ModelView);
|
2018-12-17 22:40:07 +00:00
|
|
|
model->images = (TextureData**) (model->data + offset), offset += model->imageCount * sizeof(TextureData*);
|
2018-12-18 22:27:46 +00:00
|
|
|
model->samplers = (ModelSampler*) (model->data + offset), offset += model->samplerCount * sizeof(ModelSampler);
|
2018-12-19 03:36:16 +00:00
|
|
|
model->textures = (ModelTexture*) (model->data + offset), offset += model->textureCount * sizeof(ModelTexture);
|
2018-12-19 03:57:52 +00:00
|
|
|
model->materials = (ModelMaterial*) (model->data + offset), offset += model->materialCount * sizeof(ModelMaterial);
|
2018-12-16 01:27:23 +00:00
|
|
|
model->primitives = (ModelPrimitive*) (model->data + offset), offset += model->primitiveCount * sizeof(ModelPrimitive);
|
|
|
|
model->meshes = (ModelMesh*) (model->data + offset), offset += model->meshCount * sizeof(ModelMesh);
|
|
|
|
model->nodes = (ModelNode*) (model->data + offset), offset += model->nodeCount * sizeof(ModelNode);
|
|
|
|
model->skins = (ModelSkin*) (model->data + offset), offset += model->skinCount * sizeof(ModelSkin);
|
|
|
|
model->nodeChildren = (uint32_t*) (model->data + offset), offset += info.childCount * sizeof(uint32_t);
|
|
|
|
model->skinJoints = (uint32_t*) (model->data + offset), offset += info.jointCount * sizeof(uint32_t);
|
|
|
|
|
|
|
|
parseAccessors(jsonData, info.accessors, model);
|
|
|
|
parseAnimations(jsonData, info.animations, model);
|
|
|
|
parseBlobs(jsonData, info.blobs, model, io, (void*) binData);
|
|
|
|
parseViews(jsonData, info.views, model);
|
2018-12-17 22:40:07 +00:00
|
|
|
parseImages(jsonData, info.images, model, io);
|
2018-12-18 22:27:46 +00:00
|
|
|
parseSamplers(jsonData, info.samplers, model);
|
2018-12-19 03:36:16 +00:00
|
|
|
parseTextures(jsonData, info.textures, model);
|
2018-12-19 03:57:52 +00:00
|
|
|
parseMaterials(jsonData, info.materials, model);
|
2018-12-16 01:27:23 +00:00
|
|
|
parseMeshes(jsonData, info.meshes, model);
|
2018-12-17 22:40:07 +00:00
|
|
|
parseNodes(jsonData, info.nodes, model);
|
2018-12-16 01:27:23 +00:00
|
|
|
parseSkins(jsonData, info.skins, model);
|
2018-09-26 00:10:09 +00:00
|
|
|
|
2018-12-19 04:57:47 +00:00
|
|
|
free(tokens);
|
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
return model;
|
2017-02-19 09:54:58 +00:00
|
|
|
}
|
2017-11-02 02:27:58 +00:00
|
|
|
|
2018-09-26 00:10:09 +00:00
|
|
|
void lovrModelDataDestroy(void* ref) {
|
|
|
|
//
|
2017-11-02 02:27:58 +00:00
|
|
|
}
|