Skip to content

Commit

Permalink
Impl. a GLB writer and revert 09bb33
Browse files Browse the repository at this point in the history
  • Loading branch information
sanghoon committed Jul 26, 2022
1 parent 09bb330 commit 7d3ea50
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 47 deletions.
3 changes: 3 additions & 0 deletions cgltf.h
Original file line number Diff line number Diff line change
Expand Up @@ -924,12 +924,15 @@ static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t
*/


#ifndef CGLTF_CONSTS
static const cgltf_size GlbHeaderSize = 12;
static const cgltf_size GlbChunkHeaderSize = 8;
static const uint32_t GlbVersion = 2;
static const uint32_t GlbMagic = 0x46546C67;
static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
static const uint32_t GlbMagicBinChunk = 0x004E4942;
#define CGLTF_CONSTS
#endif

#ifndef CGLTF_MALLOC
#define CGLTF_MALLOC(size) malloc(size)
Expand Down
110 changes: 63 additions & 47 deletions cgltf_write.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
*
* Reference:
* `cgltf_result cgltf_write_file(const cgltf_options* options, const char*
* path, const cgltf_data* data)` writes JSON to the given file path. Buffer
* files and external images are not written out. `data` is not deallocated.
* path, const cgltf_data* data)` writes a glTF data to the given file path.
* If `options->type` is `cgltf_file_type_glb`, both JSON content and binary
* buffer of the given glTF data will be written in a GLB format.
* Otherwise, only the JSON part will be written.
* External buffers and images are not written out. `data` is not deallocated.
*
* `cgltf_size cgltf_write(const cgltf_options* options, char* buffer,
* cgltf_size size, const cgltf_data* data)` writes JSON into the given memory
Expand Down Expand Up @@ -158,6 +161,16 @@ typedef struct {
cgltf_write_extras(context, &info.extras); \
cgltf_write_line(context, "}"); }

#ifndef CGLTF_CONSTS
static const cgltf_size GlbHeaderSize = 12;
static const cgltf_size GlbChunkHeaderSize = 8;
static const uint32_t GlbVersion = 2;
static const uint32_t GlbMagic = 0x46546C67;
static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
static const uint32_t GlbMagicBinChunk = 0x004E4942;
#define CGLTF_CONSTS
#endif

static void cgltf_write_indent(cgltf_write_context* context)
{
if (context->needs_comma)
Expand Down Expand Up @@ -295,40 +308,6 @@ static void cgltf_write_floatarrayprop(cgltf_write_context* context, const char*
context->needs_comma = 1;
}

static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static void cgltf_write_base64(cgltf_write_context* context, const unsigned char* data, cgltf_size len)
{
for (cgltf_size i = 0; i < len; i += 3) {
unsigned int buffer = 0;
cgltf_size n = 0;

// After running this loop, n contains the number of buffered plain characters (1 <= n <= 3)
for (n = 0; n < 3; ++n) {
if (i + n >= len) {
break;
}
buffer |= (unsigned int)data[i + n] << (8 * (2 - n));
}

CGLTF_SPRINTF("%c", base64_table[buffer >> 18]);
CGLTF_SPRINTF("%c", base64_table[(buffer >> 12) & 0x3f]);

if (n >= 2) {
CGLTF_SPRINTF("%c", base64_table[(buffer >> 6) & 0x3f]);
} else {
CGLTF_SPRINTF("=");
}

if (n >= 3) {
CGLTF_SPRINTF("%c", base64_table[buffer & 0x3f]);
} else {
CGLTF_SPRINTF("=");
}
}
}

static bool cgltf_check_floatarray(const float* vals, int dim, float val) {
while (dim--)
{
Expand Down Expand Up @@ -568,16 +547,7 @@ static void cgltf_write_buffer(cgltf_write_context* context, const cgltf_buffer*
{
cgltf_write_line(context, "{");
cgltf_write_strprop(context, "name", buffer->name);
if (!buffer->uri && buffer->data && buffer->size > 0) {
// if uri is missing (e.g. glb-embbeded), write the data w/ base64 encoding
cgltf_write_indent(context);
CGLTF_SPRINTF("\"uri\": \"data:application/octet-stream;base64,");
cgltf_write_base64(context, (const unsigned char*)buffer->data, buffer->size);
CGLTF_SPRINTF("\"");
context->needs_comma = 1;
} else {
cgltf_write_strprop(context, "uri", buffer->uri);
}
cgltf_write_strprop(context, "uri", buffer->uri);
cgltf_write_sizeprop(context, "byteLength", buffer->size, (cgltf_size)-1);
cgltf_write_extras(context, &buffer->extras);
cgltf_write_line(context, "}");
Expand Down Expand Up @@ -1140,6 +1110,47 @@ static void cgltf_write_variant(cgltf_write_context* context, const cgltf_materi
cgltf_write_line(context, "}");
}

static void cgltf_write_glb(FILE* file, const void* json_buf, const cgltf_size json_size, const void* bin_buf, const cgltf_size bin_size)
{
char header[GlbHeaderSize];
char chunk_header[GlbChunkHeaderSize];
char json_pad[3] = { 0x20, 0x20, 0x20 };
char bin_pad[3] = { 0, 0, 0 };

cgltf_size json_padsize = (json_size % 4 != 0) ? 4 - json_size % 4 : 0;
cgltf_size bin_padsize = (bin_size % 4 != 0) ? 4 - bin_size % 4 : 0;
cgltf_size total_size = GlbHeaderSize + GlbChunkHeaderSize + json_size + json_padsize;
if (bin_buf != NULL && bin_size > 0) {
total_size += GlbChunkHeaderSize + bin_size + bin_padsize;
}

// Write a GLB header
memcpy(header, &GlbMagic, 4);
memcpy(header + 4, &GlbVersion, 4);
memcpy(header + 8, &total_size, 4);
fwrite(header, 1, GlbHeaderSize, file);

// Write a JSON chunk (header & data)
uint32_t json_chunk_size = json_size + json_padsize;
memcpy(chunk_header, &json_chunk_size, 4);
memcpy(chunk_header + 4, &GlbMagicJsonChunk, 4);
fwrite(chunk_header, 1, GlbChunkHeaderSize, file);

fwrite(json_buf, 1, json_size, file);
fwrite(json_pad, 1, json_padsize, file);

if (bin_buf != NULL && bin_size > 0) {
// Write a binary chunk (header & data)
uint32_t bin_chunk_size = bin_size + bin_padsize;
memcpy(chunk_header, &bin_chunk_size, 4);
memcpy(chunk_header + 4, &GlbMagicBinChunk, 4);
fwrite(chunk_header, 1, GlbChunkHeaderSize, file);

fwrite(bin_buf, 1, bin_size, file);
fwrite(bin_pad, 1, bin_padsize, file);
}
}

cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data)
{
cgltf_size expected = cgltf_write(options, NULL, 0, data);
Expand All @@ -1154,7 +1165,12 @@ cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, co
return cgltf_result_file_not_found;
}
// Note that cgltf_write() includes a null terminator, which we omit from the file content.
fwrite(buffer, actual - 1, 1, file);
if (options->type == cgltf_file_type_glb) {
cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size);
} else {
// Write a plain JSON file.
fwrite(buffer, actual - 1, 1, file);
}
fclose(file);
free(buffer);
return cgltf_result_success;
Expand Down

0 comments on commit 7d3ea50

Please sign in to comment.