0/**
1 * cgltf_write - a single-file glTF 2.0 writer written in C99.
2 *
3 * Version: 1.15
4 *
5 * Website: https://github.com/jkuhlmann/cgltf
6 *
7 * Distributed under the MIT License, see notice at the end of this file.
8 *
9 * Building:
10 * Include this file where you need the struct and function
11 * declarations. Have exactly one source file where you define
12 * `CGLTF_WRITE_IMPLEMENTATION` before including this file to get the
13 * function definitions.
14 *
15 * Reference:
16 * `cgltf_result cgltf_write_file(const cgltf_options* options, const char*
17 * path, const cgltf_data* data)` writes a glTF data to the given file path.
18 * If `options->type` is `cgltf_file_type_glb`, both JSON content and binary
19 * buffer of the given glTF data will be written in a GLB format.
20 * Otherwise, only the JSON part will be written.
21 * External buffers and images are not written out. `data` is not deallocated.
22 *
23 * `cgltf_size cgltf_write(const cgltf_options* options, char* buffer,
24 * cgltf_size size, const cgltf_data* data)` writes JSON into the given memory
25 * buffer. Returns the number of bytes written to `buffer`, including a null
26 * terminator. If buffer is null, returns the number of bytes that would have
27 * been written. `data` is not deallocated.
28 */
29#ifndef CGLTF_WRITE_H_INCLUDED__
30#define CGLTF_WRITE_H_INCLUDED__
32#include "cgltf.h"
34#include <stddef.h>
35#include <stdbool.h>
37#ifdef __cplusplus
38extern "C" {
39#endif
41cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data);
42cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data);
44#ifdef __cplusplus
45}
46#endif
48#endif /* #ifndef CGLTF_WRITE_H_INCLUDED__ */
50/*
51 *
52 * Stop now, if you are only interested in the API.
53 * Below, you find the implementation.
54 *
55 */
57#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__)
58/* This makes MSVC/CLion intellisense work. */
59#define CGLTF_WRITE_IMPLEMENTATION
60#endif
62#ifdef CGLTF_WRITE_IMPLEMENTATION
64#include <assert.h>
65#include <stdio.h>
66#include <stdint.h>
67#include <stdlib.h>
68#include <string.h>
69#include <float.h>
71#define CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM (1 << 0)
72#define CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT (1 << 1)
73#define CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS (1 << 2)
74#define CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL (1 << 3)
75#define CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION (1 << 4)
76#define CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT (1 << 5)
77#define CGLTF_EXTENSION_FLAG_MATERIALS_IOR (1 << 6)
78#define CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR (1 << 7)
79#define CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION (1 << 8)
80#define CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN (1 << 9)
81#define CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS (1 << 10)
82#define CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME (1 << 11)
83#define CGLTF_EXTENSION_FLAG_TEXTURE_BASISU (1 << 12)
84#define CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH (1 << 13)
85#define CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING (1 << 14)
86#define CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE (1 << 15)
87#define CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY (1 << 16)
88#define CGLTF_EXTENSION_FLAG_MATERIALS_DISPERSION (1 << 17)
89#define CGLTF_EXTENSION_FLAG_TEXTURE_WEBP (1 << 18)
90#define CGLTF_EXTENSION_FLAG_MATERIALS_DIFFUSE_TRANSMISSION (1 << 19)
92typedef struct {
93 char* buffer;
94 cgltf_size buffer_size;
95 cgltf_size remaining;
96 char* cursor;
97 cgltf_size tmp;
98 cgltf_size chars_written;
99 const cgltf_data* data;
100 int depth;
101 const char* indent;
102 int needs_comma;
103 uint32_t extension_flags;
104 uint32_t required_extension_flags;
105} cgltf_write_context;
107#define CGLTF_MIN(a, b) (a < b ? a : b)
109#ifdef FLT_DECIMAL_DIG
110 // FLT_DECIMAL_DIG is C11
111 #define CGLTF_DECIMAL_DIG (FLT_DECIMAL_DIG)
112#else
113 #define CGLTF_DECIMAL_DIG 9
114#endif
116#define CGLTF_SPRINTF(...) { \
117 assert(context->cursor || (!context->cursor && context->remaining == 0)); \
118 context->tmp = snprintf ( context->cursor, context->remaining, __VA_ARGS__ ); \
119 context->chars_written += context->tmp; \
120 if (context->cursor) { \
121 context->cursor += context->tmp; \
122 context->remaining -= context->tmp; \
123 } }
125#define CGLTF_SNPRINTF(length, ...) { \
126 assert(context->cursor || (!context->cursor && context->remaining == 0)); \
127 context->tmp = snprintf ( context->cursor, CGLTF_MIN(length + 1, context->remaining), __VA_ARGS__ ); \
128 context->chars_written += length; \
129 if (context->cursor) { \
130 context->cursor += length; \
131 context->remaining -= length; \
132 } }
134#define CGLTF_WRITE_IDXPROP(label, val, start) if (val) { \
135 cgltf_write_indent(context); \
136 CGLTF_SPRINTF("\"%s\": %d", label, (int) (val - start)); \
137 context->needs_comma = 1; }
139#define CGLTF_WRITE_IDXARRPROP(label, dim, vals, start) if (vals) { \
140 cgltf_write_indent(context); \
141 CGLTF_SPRINTF("\"%s\": [", label); \
142 for (int i = 0; i < (int)(dim); ++i) { \
143 int idx = (int) (vals[i] - start); \
144 if (i != 0) CGLTF_SPRINTF(","); \
145 CGLTF_SPRINTF(" %d", idx); \
146 } \
147 CGLTF_SPRINTF(" ]"); \
148 context->needs_comma = 1; }
150#define CGLTF_WRITE_TEXTURE_INFO(label, info) if (info.texture) { \
151 cgltf_write_line(context, "\"" label "\": {"); \
152 CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
153 cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
154 if (info.has_transform) { \
155 context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
156 cgltf_write_texture_transform(context, &info.transform); \
157 } \
158 cgltf_write_line(context, "}"); }
160#define CGLTF_WRITE_NORMAL_TEXTURE_INFO(label, info) if (info.texture) { \
161 cgltf_write_line(context, "\"" label "\": {"); \
162 CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
163 cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
164 cgltf_write_floatprop(context, "scale", info.scale, 1.0f); \
165 if (info.has_transform) { \
166 context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
167 cgltf_write_texture_transform(context, &info.transform); \
168 } \
169 cgltf_write_line(context, "}"); }
171#define CGLTF_WRITE_OCCLUSION_TEXTURE_INFO(label, info) if (info.texture) { \
172 cgltf_write_line(context, "\"" label "\": {"); \
173 CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
174 cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
175 cgltf_write_floatprop(context, "strength", info.scale, 1.0f); \
176 if (info.has_transform) { \
177 context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
178 cgltf_write_texture_transform(context, &info.transform); \
179 } \
180 cgltf_write_line(context, "}"); }
182#ifndef CGLTF_CONSTS
183#define GlbHeaderSize 12
184#define GlbChunkHeaderSize 8
185static const uint32_t GlbVersion = 2;
186static const uint32_t GlbMagic = 0x46546C67;
187static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
188static const uint32_t GlbMagicBinChunk = 0x004E4942;
189#define CGLTF_CONSTS
190#endif
192static void cgltf_write_indent(cgltf_write_context* context)
193{
194 if (context->needs_comma)
195 {
196 CGLTF_SPRINTF(",\n");
197 context->needs_comma = 0;
198 }
199 else
200 {
201 CGLTF_SPRINTF("\n");
202 }
203 for (int i = 0; i < context->depth; ++i)
204 {
205 CGLTF_SPRINTF("%s", context->indent);
206 }
207}
209static void cgltf_write_line(cgltf_write_context* context, const char* line)
210{
211 if (line[0] == ']' || line[0] == '}')
212 {
213 --context->depth;
214 context->needs_comma = 0;
215 }
216 cgltf_write_indent(context);
217 CGLTF_SPRINTF("%s", line);
218 cgltf_size last = (cgltf_size)(strlen(line) - 1);
219 if (line[0] == ']' || line[0] == '}')
220 {
221 context->needs_comma = 1;
222 }
223 if (line[last] == '[' || line[last] == '{')
224 {
225 ++context->depth;
226 context->needs_comma = 0;
227 }
228}
230static void cgltf_write_strprop(cgltf_write_context* context, const char* label, const char* val)
231{
232 if (val)
233 {
234 cgltf_write_indent(context);
235 CGLTF_SPRINTF("\"%s\": \"%s\"", label, val);
236 context->needs_comma = 1;
237 }
238}
240static void cgltf_write_extras(cgltf_write_context* context, const cgltf_extras* extras)
241{
242 if (extras->data)
243 {
244 cgltf_write_indent(context);
245 CGLTF_SPRINTF("\"extras\": %s", extras->data);
246 context->needs_comma = 1;
247 }
248 else
249 {
250 cgltf_size length = extras->end_offset - extras->start_offset;
251 if (length > 0 && context->data->json)
252 {
253 char* json_string = ((char*) context->data->json) + extras->start_offset;
254 cgltf_write_indent(context);
255 CGLTF_SPRINTF("%s", "\"extras\": ");
256 CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string);
257 context->needs_comma = 1;
258 }
259 }
260}
262static void cgltf_write_stritem(cgltf_write_context* context, const char* item)
263{
264 cgltf_write_indent(context);
265 CGLTF_SPRINTF("\"%s\"", item);
266 context->needs_comma = 1;
267}
269static void cgltf_write_intprop(cgltf_write_context* context, const char* label, int val, int def)
270{
271 if (val != def)
272 {
273 cgltf_write_indent(context);
274 CGLTF_SPRINTF("\"%s\": %d", label, val);
275 context->needs_comma = 1;
276 }
277}
279static void cgltf_write_sizeprop(cgltf_write_context* context, const char* label, cgltf_size val, cgltf_size def)
280{
281 if (val != def)
282 {
283 cgltf_write_indent(context);
284 CGLTF_SPRINTF("\"%s\": %zu", label, val);
285 context->needs_comma = 1;
286 }
287}
289static void cgltf_write_floatprop(cgltf_write_context* context, const char* label, float val, float def)
290{
291 if (val != def)
292 {
293 cgltf_write_indent(context);
294 CGLTF_SPRINTF("\"%s\": ", label);
295 CGLTF_SPRINTF("%.*g", CGLTF_DECIMAL_DIG, val);
296 context->needs_comma = 1;
298 if (context->cursor)
299 {
300 char *decimal_comma = strchr(context->cursor - context->tmp, ',');
301 if (decimal_comma)
302 {
303 *decimal_comma = '.';
304 }
305 }
306 }
307}
309static void cgltf_write_boolprop_optional(cgltf_write_context* context, const char* label, bool val, bool def)
310{
311 if (val != def)
312 {
313 cgltf_write_indent(context);
314 CGLTF_SPRINTF("\"%s\": %s", label, val ? "true" : "false");
315 context->needs_comma = 1;
316 }
317}
319static void cgltf_write_floatarrayprop(cgltf_write_context* context, const char* label, const cgltf_float* vals, cgltf_size dim)
320{
321 cgltf_write_indent(context);
322 CGLTF_SPRINTF("\"%s\": [", label);
323 for (cgltf_size i = 0; i < dim; ++i)
324 {
325 if (i != 0)
326 {
327 CGLTF_SPRINTF(", %.*g", CGLTF_DECIMAL_DIG, vals[i]);
328 }
329 else
330 {
331 CGLTF_SPRINTF("%.*g", CGLTF_DECIMAL_DIG, vals[i]);
332 }
333 }
334 CGLTF_SPRINTF("]");
335 context->needs_comma = 1;
336}
338static bool cgltf_check_floatarray(const float* vals, int dim, float val) {
339 while (dim--)
340 {
341 if (vals[dim] != val)
342 {
343 return true;
344 }
345 }
346 return false;
347}
349static int cgltf_int_from_component_type(cgltf_component_type ctype)
350{
351 switch (ctype)
352 {
353 case cgltf_component_type_r_8: return 5120;
354 case cgltf_component_type_r_8u: return 5121;
355 case cgltf_component_type_r_16: return 5122;
356 case cgltf_component_type_r_16u: return 5123;
357 case cgltf_component_type_r_32u: return 5125;
358 case cgltf_component_type_r_32f: return 5126;
359 default: return 0;
360 }
361}
363static int cgltf_int_from_primitive_type(cgltf_primitive_type ctype)
364{
365 switch (ctype)
366 {
367 case cgltf_primitive_type_points: return 0;
368 case cgltf_primitive_type_lines: return 1;
369 case cgltf_primitive_type_line_loop: return 2;
370 case cgltf_primitive_type_line_strip: return 3;
371 case cgltf_primitive_type_triangles: return 4;
372 case cgltf_primitive_type_triangle_strip: return 5;
373 case cgltf_primitive_type_triangle_fan: return 6;
374 default: return -1;
375 }
376}
378static const char* cgltf_str_from_alpha_mode(cgltf_alpha_mode alpha_mode)
379{
380 switch (alpha_mode)
381 {
382 case cgltf_alpha_mode_mask: return "MASK";
383 case cgltf_alpha_mode_blend: return "BLEND";
384 default: return NULL;
385 }
386}
388static const char* cgltf_str_from_type(cgltf_type type)
389{
390 switch (type)
391 {
392 case cgltf_type_scalar: return "SCALAR";
393 case cgltf_type_vec2: return "VEC2";
394 case cgltf_type_vec3: return "VEC3";
395 case cgltf_type_vec4: return "VEC4";
396 case cgltf_type_mat2: return "MAT2";
397 case cgltf_type_mat3: return "MAT3";
398 case cgltf_type_mat4: return "MAT4";
399 default: return NULL;
400 }
401}
403static cgltf_size cgltf_dim_from_type(cgltf_type type)
404{
405 switch (type)
406 {
407 case cgltf_type_scalar: return 1;
408 case cgltf_type_vec2: return 2;
409 case cgltf_type_vec3: return 3;
410 case cgltf_type_vec4: return 4;
411 case cgltf_type_mat2: return 4;
412 case cgltf_type_mat3: return 9;
413 case cgltf_type_mat4: return 16;
414 default: return 0;
415 }
416}
418static const char* cgltf_str_from_camera_type(cgltf_camera_type camera_type)
419{
420 switch (camera_type)
421 {
422 case cgltf_camera_type_perspective: return "perspective";
423 case cgltf_camera_type_orthographic: return "orthographic";
424 default: return NULL;
425 }
426}
428static const char* cgltf_str_from_light_type(cgltf_light_type light_type)
429{
430 switch (light_type)
431 {
432 case cgltf_light_type_directional: return "directional";
433 case cgltf_light_type_point: return "point";
434 case cgltf_light_type_spot: return "spot";
435 default: return NULL;
436 }
437}
439static void cgltf_write_texture_transform(cgltf_write_context* context, const cgltf_texture_transform* transform)
440{
441 cgltf_write_line(context, "\"extensions\": {");
442 cgltf_write_line(context, "\"KHR_texture_transform\": {");
443 if (cgltf_check_floatarray(transform->offset, 2, 0.0f))
444 {
445 cgltf_write_floatarrayprop(context, "offset", transform->offset, 2);
446 }
447 cgltf_write_floatprop(context, "rotation", transform->rotation, 0.0f);
448 if (cgltf_check_floatarray(transform->scale, 2, 1.0f))
449 {
450 cgltf_write_floatarrayprop(context, "scale", transform->scale, 2);
451 }
452 if (transform->has_texcoord)
453 {
454 cgltf_write_intprop(context, "texCoord", transform->texcoord, -1);
455 }
456 cgltf_write_line(context, "}");
457 cgltf_write_line(context, "}");
458}
460static void cgltf_write_asset(cgltf_write_context* context, const cgltf_asset* asset)
461{
462 cgltf_write_line(context, "\"asset\": {");
463 cgltf_write_strprop(context, "copyright", asset->copyright);
464 cgltf_write_strprop(context, "generator", asset->generator);
465 cgltf_write_strprop(context, "version", asset->version);
466 cgltf_write_strprop(context, "min_version", asset->min_version);
467 cgltf_write_extras(context, &asset->extras);
468 cgltf_write_line(context, "}");
469}
471static void cgltf_write_primitive(cgltf_write_context* context, const cgltf_primitive* prim)
472{
473 cgltf_write_intprop(context, "mode", cgltf_int_from_primitive_type(prim->type), 4);
474 CGLTF_WRITE_IDXPROP("indices", prim->indices, context->data->accessors);
475 CGLTF_WRITE_IDXPROP("material", prim->material, context->data->materials);
476 cgltf_write_line(context, "\"attributes\": {");
477 for (cgltf_size i = 0; i < prim->attributes_count; ++i)
478 {
479 const cgltf_attribute* attr = prim->attributes + i;
480 CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
481 }
482 cgltf_write_line(context, "}");
484 if (prim->targets_count)
485 {
486 cgltf_write_line(context, "\"targets\": [");
487 for (cgltf_size i = 0; i < prim->targets_count; ++i)
488 {
489 cgltf_write_line(context, "{");
490 for (cgltf_size j = 0; j < prim->targets[i].attributes_count; ++j)
491 {
492 const cgltf_attribute* attr = prim->targets[i].attributes + j;
493 CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
494 }
495 cgltf_write_line(context, "}");
496 }
497 cgltf_write_line(context, "]");
498 }
499 cgltf_write_extras(context, &prim->extras);
501 if (prim->has_draco_mesh_compression || prim->mappings_count > 0)
502 {
503 cgltf_write_line(context, "\"extensions\": {");
505 if (prim->has_draco_mesh_compression)
506 {
507 context->extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION;
508 if (prim->attributes_count == 0 || prim->indices == 0)
509 {
510 context->required_extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION;
511 }
513 cgltf_write_line(context, "\"KHR_draco_mesh_compression\": {");
514 CGLTF_WRITE_IDXPROP("bufferView", prim->draco_mesh_compression.buffer_view, context->data->buffer_views);
515 cgltf_write_line(context, "\"attributes\": {");
516 for (cgltf_size i = 0; i < prim->draco_mesh_compression.attributes_count; ++i)
517 {
518 const cgltf_attribute* attr = prim->draco_mesh_compression.attributes + i;
519 CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
520 }
521 cgltf_write_line(context, "}");
522 cgltf_write_line(context, "}");
523 }
525 if (prim->mappings_count > 0)
526 {
527 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS;
528 cgltf_write_line(context, "\"KHR_materials_variants\": {");
529 cgltf_write_line(context, "\"mappings\": [");
530 for (cgltf_size i = 0; i < prim->mappings_count; ++i)
531 {
532 const cgltf_material_mapping* map = prim->mappings + i;
533 cgltf_write_line(context, "{");
534 CGLTF_WRITE_IDXPROP("material", map->material, context->data->materials);
536 cgltf_write_indent(context);
537 CGLTF_SPRINTF("\"variants\": [%d]", (int)map->variant);
538 context->needs_comma = 1;
540 cgltf_write_extras(context, &map->extras);
541 cgltf_write_line(context, "}");
542 }
543 cgltf_write_line(context, "]");
544 cgltf_write_line(context, "}");
545 }
547 cgltf_write_line(context, "}");
548 }
549}
551static void cgltf_write_mesh(cgltf_write_context* context, const cgltf_mesh* mesh)
552{
553 cgltf_write_line(context, "{");
554 cgltf_write_strprop(context, "name", mesh->name);
556 cgltf_write_line(context, "\"primitives\": [");
557 for (cgltf_size i = 0; i < mesh->primitives_count; ++i)
558 {
559 cgltf_write_line(context, "{");
560 cgltf_write_primitive(context, mesh->primitives + i);
561 cgltf_write_line(context, "}");
562 }
563 cgltf_write_line(context, "]");
565 if (mesh->weights_count > 0)
566 {
567 cgltf_write_floatarrayprop(context, "weights", mesh->weights, mesh->weights_count);
568 }
570 cgltf_write_extras(context, &mesh->extras);
571 cgltf_write_line(context, "}");
572}
574static void cgltf_write_buffer_view(cgltf_write_context* context, const cgltf_buffer_view* view)
575{
576 cgltf_write_line(context, "{");
577 cgltf_write_strprop(context, "name", view->name);
578 CGLTF_WRITE_IDXPROP("buffer", view->buffer, context->data->buffers);
579 cgltf_write_sizeprop(context, "byteLength", view->size, (cgltf_size)-1);
580 cgltf_write_sizeprop(context, "byteOffset", view->offset, 0);
581 cgltf_write_sizeprop(context, "byteStride", view->stride, 0);
582 // NOTE: We skip writing "target" because the spec says its usage can be inferred.
583 cgltf_write_extras(context, &view->extras);
584 cgltf_write_line(context, "}");
585}
588static void cgltf_write_buffer(cgltf_write_context* context, const cgltf_buffer* buffer)
589{
590 cgltf_write_line(context, "{");
591 cgltf_write_strprop(context, "name", buffer->name);
592 cgltf_write_strprop(context, "uri", buffer->uri);
593 cgltf_write_sizeprop(context, "byteLength", buffer->size, (cgltf_size)-1);
594 cgltf_write_extras(context, &buffer->extras);
595 cgltf_write_line(context, "}");
596}
598static void cgltf_write_material(cgltf_write_context* context, const cgltf_material* material)
599{
600 cgltf_write_line(context, "{");
601 cgltf_write_strprop(context, "name", material->name);
602 if (material->alpha_mode == cgltf_alpha_mode_mask)
603 {
604 cgltf_write_floatprop(context, "alphaCutoff", material->alpha_cutoff, 0.5f);
605 }
606 cgltf_write_boolprop_optional(context, "doubleSided", (bool)material->double_sided, false);
607 // cgltf_write_boolprop_optional(context, "unlit", material->unlit, false);
609 if (material->unlit)
610 {
611 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT;
612 }
614 if (material->has_pbr_specular_glossiness)
615 {
616 context->extension_flags |= CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS;
617 }
619 if (material->has_clearcoat)
620 {
621 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT;
622 }
624 if (material->has_transmission)
625 {
626 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION;
627 }
629 if (material->has_volume)
630 {
631 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME;
632 }
634 if (material->has_ior)
635 {
636 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IOR;
637 }
639 if (material->has_specular)
640 {
641 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR;
642 }
644 if (material->has_sheen)
645 {
646 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN;
647 }
649 if (material->has_emissive_strength)
650 {
651 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH;
652 }
654 if (material->has_iridescence)
655 {
656 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE;
657 }
659 if (material->has_diffuse_transmission)
660 {
661 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_DIFFUSE_TRANSMISSION;
662 }
664 if (material->has_anisotropy)
665 {
666 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY;
667 }
669 if (material->has_dispersion)
670 {
671 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_DISPERSION;
672 }
674 if (material->has_pbr_metallic_roughness)
675 {
676 const cgltf_pbr_metallic_roughness* params = &material->pbr_metallic_roughness;
677 cgltf_write_line(context, "\"pbrMetallicRoughness\": {");
678 CGLTF_WRITE_TEXTURE_INFO("baseColorTexture", params->base_color_texture);
679 CGLTF_WRITE_TEXTURE_INFO("metallicRoughnessTexture", params->metallic_roughness_texture);
680 cgltf_write_floatprop(context, "metallicFactor", params->metallic_factor, 1.0f);
681 cgltf_write_floatprop(context, "roughnessFactor", params->roughness_factor, 1.0f);
682 if (cgltf_check_floatarray(params->base_color_factor, 4, 1.0f))
683 {
684 cgltf_write_floatarrayprop(context, "baseColorFactor", params->base_color_factor, 4);
685 }
686 cgltf_write_line(context, "}");
687 }
689 if (material->unlit || material->has_pbr_specular_glossiness || material->has_clearcoat || material->has_ior || material->has_specular || material->has_transmission || material->has_sheen || material->has_volume || material->has_emissive_strength || material->has_iridescence || material->has_anisotropy || material->has_dispersion || material->has_diffuse_transmission)
690 {
691 cgltf_write_line(context, "\"extensions\": {");
692 if (material->has_clearcoat)
693 {
694 const cgltf_clearcoat* params = &material->clearcoat;
695 cgltf_write_line(context, "\"KHR_materials_clearcoat\": {");
696 CGLTF_WRITE_TEXTURE_INFO("clearcoatTexture", params->clearcoat_texture);
697 CGLTF_WRITE_TEXTURE_INFO("clearcoatRoughnessTexture", params->clearcoat_roughness_texture);
698 CGLTF_WRITE_NORMAL_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture);
699 cgltf_write_floatprop(context, "clearcoatFactor", params->clearcoat_factor, 0.0f);
700 cgltf_write_floatprop(context, "clearcoatRoughnessFactor", params->clearcoat_roughness_factor, 0.0f);
701 cgltf_write_line(context, "}");
702 }
703 if (material->has_ior)
704 {
705 const cgltf_ior* params = &material->ior;
706 cgltf_write_line(context, "\"KHR_materials_ior\": {");
707 cgltf_write_floatprop(context, "ior", params->ior, 1.5f);
708 cgltf_write_line(context, "}");
709 }
710 if (material->has_specular)
711 {
712 const cgltf_specular* params = &material->specular;
713 cgltf_write_line(context, "\"KHR_materials_specular\": {");
714 CGLTF_WRITE_TEXTURE_INFO("specularTexture", params->specular_texture);
715 CGLTF_WRITE_TEXTURE_INFO("specularColorTexture", params->specular_color_texture);
716 cgltf_write_floatprop(context, "specularFactor", params->specular_factor, 1.0f);
717 if (cgltf_check_floatarray(params->specular_color_factor, 3, 1.0f))
718 {
719 cgltf_write_floatarrayprop(context, "specularColorFactor", params->specular_color_factor, 3);
720 }
721 cgltf_write_line(context, "}");
722 }
723 if (material->has_transmission)
724 {
725 const cgltf_transmission* params = &material->transmission;
726 cgltf_write_line(context, "\"KHR_materials_transmission\": {");
727 CGLTF_WRITE_TEXTURE_INFO("transmissionTexture", params->transmission_texture);
728 cgltf_write_floatprop(context, "transmissionFactor", params->transmission_factor, 0.0f);
729 cgltf_write_line(context, "}");
730 }
731 if (material->has_volume)
732 {
733 const cgltf_volume* params = &material->volume;
734 cgltf_write_line(context, "\"KHR_materials_volume\": {");
735 CGLTF_WRITE_TEXTURE_INFO("thicknessTexture", params->thickness_texture);
736 cgltf_write_floatprop(context, "thicknessFactor", params->thickness_factor, 0.0f);
737 if (cgltf_check_floatarray(params->attenuation_color, 3, 1.0f))
738 {
739 cgltf_write_floatarrayprop(context, "attenuationColor", params->attenuation_color, 3);
740 }
741 if (params->attenuation_distance < FLT_MAX)
742 {
743 cgltf_write_floatprop(context, "attenuationDistance", params->attenuation_distance, FLT_MAX);
744 }
745 cgltf_write_line(context, "}");
746 }
747 if (material->has_sheen)
748 {
749 const cgltf_sheen* params = &material->sheen;
750 cgltf_write_line(context, "\"KHR_materials_sheen\": {");
751 CGLTF_WRITE_TEXTURE_INFO("sheenColorTexture", params->sheen_color_texture);
752 CGLTF_WRITE_TEXTURE_INFO("sheenRoughnessTexture", params->sheen_roughness_texture);
753 if (cgltf_check_floatarray(params->sheen_color_factor, 3, 0.0f))
754 {
755 cgltf_write_floatarrayprop(context, "sheenColorFactor", params->sheen_color_factor, 3);
756 }
757 cgltf_write_floatprop(context, "sheenRoughnessFactor", params->sheen_roughness_factor, 0.0f);
758 cgltf_write_line(context, "}");
759 }
760 if (material->has_pbr_specular_glossiness)
761 {
762 const cgltf_pbr_specular_glossiness* params = &material->pbr_specular_glossiness;
763 cgltf_write_line(context, "\"KHR_materials_pbrSpecularGlossiness\": {");
764 CGLTF_WRITE_TEXTURE_INFO("diffuseTexture", params->diffuse_texture);
765 CGLTF_WRITE_TEXTURE_INFO("specularGlossinessTexture", params->specular_glossiness_texture);
766 if (cgltf_check_floatarray(params->diffuse_factor, 4, 1.0f))
767 {
768 cgltf_write_floatarrayprop(context, "diffuseFactor", params->diffuse_factor, 4);
769 }
770 if (cgltf_check_floatarray(params->specular_factor, 3, 1.0f))
771 {
772 cgltf_write_floatarrayprop(context, "specularFactor", params->specular_factor, 3);
773 }
774 cgltf_write_floatprop(context, "glossinessFactor", params->glossiness_factor, 1.0f);
775 cgltf_write_line(context, "}");
776 }
777 if (material->unlit)
778 {
779 cgltf_write_line(context, "\"KHR_materials_unlit\": {}");
780 }
781 if (material->has_emissive_strength)
782 {
783 cgltf_write_line(context, "\"KHR_materials_emissive_strength\": {");
784 const cgltf_emissive_strength* params = &material->emissive_strength;
785 cgltf_write_floatprop(context, "emissiveStrength", params->emissive_strength, 1.f);
786 cgltf_write_line(context, "}");
787 }
788 if (material->has_iridescence)
789 {
790 cgltf_write_line(context, "\"KHR_materials_iridescence\": {");
791 const cgltf_iridescence* params = &material->iridescence;
792 cgltf_write_floatprop(context, "iridescenceFactor", params->iridescence_factor, 0.f);
793 CGLTF_WRITE_TEXTURE_INFO("iridescenceTexture", params->iridescence_texture);
794 cgltf_write_floatprop(context, "iridescenceIor", params->iridescence_ior, 1.3f);
795 cgltf_write_floatprop(context, "iridescenceThicknessMinimum", params->iridescence_thickness_min, 100.f);
796 cgltf_write_floatprop(context, "iridescenceThicknessMaximum", params->iridescence_thickness_max, 400.f);
797 CGLTF_WRITE_TEXTURE_INFO("iridescenceThicknessTexture", params->iridescence_thickness_texture);
798 cgltf_write_line(context, "}");
799 }
800 if (material->has_diffuse_transmission)
801 {
802 const cgltf_diffuse_transmission* params = &material->diffuse_transmission;
803 cgltf_write_line(context, "\"KHR_materials_diffuse_transmission\": {");
804 CGLTF_WRITE_TEXTURE_INFO("diffuseTransmissionTexture", params->diffuse_transmission_texture);
805 cgltf_write_floatprop(context, "diffuseTransmissionFactor", params->diffuse_transmission_factor, 0.f);
806 if (cgltf_check_floatarray(params->diffuse_transmission_color_factor, 3, 1.f))
807 {
808 cgltf_write_floatarrayprop(context, "diffuseTransmissionColorFactor", params->diffuse_transmission_color_factor, 3);
809 }
810 CGLTF_WRITE_TEXTURE_INFO("diffuseTransmissionColorTexture", params->diffuse_transmission_color_texture);
811 cgltf_write_line(context, "}");
812 }
813 if (material->has_anisotropy)
814 {
815 cgltf_write_line(context, "\"KHR_materials_anisotropy\": {");
816 const cgltf_anisotropy* params = &material->anisotropy;
817 cgltf_write_floatprop(context, "anisotropyStrength", params->anisotropy_strength, 0.f);
818 cgltf_write_floatprop(context, "anisotropyRotation", params->anisotropy_rotation, 0.f);
819 CGLTF_WRITE_TEXTURE_INFO("anisotropyTexture", params->anisotropy_texture);
820 cgltf_write_line(context, "}");
821 }
822 if (material->has_dispersion)
823 {
824 cgltf_write_line(context, "\"KHR_materials_dispersion\": {");
825 const cgltf_dispersion* params = &material->dispersion;
826 cgltf_write_floatprop(context, "dispersion", params->dispersion, 0.f);
827 cgltf_write_line(context, "}");
828 }
829 cgltf_write_line(context, "}");
830 }
832 CGLTF_WRITE_NORMAL_TEXTURE_INFO("normalTexture", material->normal_texture);
833 CGLTF_WRITE_OCCLUSION_TEXTURE_INFO("occlusionTexture", material->occlusion_texture);
834 CGLTF_WRITE_TEXTURE_INFO("emissiveTexture", material->emissive_texture);
835 if (cgltf_check_floatarray(material->emissive_factor, 3, 0.0f))
836 {
837 cgltf_write_floatarrayprop(context, "emissiveFactor", material->emissive_factor, 3);
838 }
839 cgltf_write_strprop(context, "alphaMode", cgltf_str_from_alpha_mode(material->alpha_mode));
840 cgltf_write_extras(context, &material->extras);
841 cgltf_write_line(context, "}");
842}
844static void cgltf_write_image(cgltf_write_context* context, const cgltf_image* image)
845{
846 cgltf_write_line(context, "{");
847 cgltf_write_strprop(context, "name", image->name);
848 cgltf_write_strprop(context, "uri", image->uri);
849 CGLTF_WRITE_IDXPROP("bufferView", image->buffer_view, context->data->buffer_views);
850 cgltf_write_strprop(context, "mimeType", image->mime_type);
851 cgltf_write_extras(context, &image->extras);
852 cgltf_write_line(context, "}");
853}
855static void cgltf_write_texture(cgltf_write_context* context, const cgltf_texture* texture)
856{
857 cgltf_write_line(context, "{");
858 cgltf_write_strprop(context, "name", texture->name);
859 CGLTF_WRITE_IDXPROP("source", texture->image, context->data->images);
860 CGLTF_WRITE_IDXPROP("sampler", texture->sampler, context->data->samplers);
862 if (texture->has_basisu || texture->has_webp)
863 {
864 cgltf_write_line(context, "\"extensions\": {");
865 if (texture->has_basisu)
866 {
867 context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_BASISU;
868 cgltf_write_line(context, "\"KHR_texture_basisu\": {");
869 CGLTF_WRITE_IDXPROP("source", texture->basisu_image, context->data->images);
870 cgltf_write_line(context, "}");
871 }
872 if (texture->has_webp)
873 {
874 context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_WEBP;
875 cgltf_write_line(context, "\"EXT_texture_webp\": {");
876 CGLTF_WRITE_IDXPROP("source", texture->webp_image, context->data->images);
877 cgltf_write_line(context, "}");
878 }
879 cgltf_write_line(context, "}");
880 }
881 cgltf_write_extras(context, &texture->extras);
882 cgltf_write_line(context, "}");
883}
885static void cgltf_write_skin(cgltf_write_context* context, const cgltf_skin* skin)
886{
887 cgltf_write_line(context, "{");
888 CGLTF_WRITE_IDXPROP("skeleton", skin->skeleton, context->data->nodes);
889 CGLTF_WRITE_IDXPROP("inverseBindMatrices", skin->inverse_bind_matrices, context->data->accessors);
890 CGLTF_WRITE_IDXARRPROP("joints", skin->joints_count, skin->joints, context->data->nodes);
891 cgltf_write_strprop(context, "name", skin->name);
892 cgltf_write_extras(context, &skin->extras);
893 cgltf_write_line(context, "}");
894}
896static const char* cgltf_write_str_path_type(cgltf_animation_path_type path_type)
897{
898 switch (path_type)
899 {
900 case cgltf_animation_path_type_translation:
901 return "translation";
902 case cgltf_animation_path_type_rotation:
903 return "rotation";
904 case cgltf_animation_path_type_scale:
905 return "scale";
906 case cgltf_animation_path_type_weights:
907 return "weights";
908 default:
909 break;
910 }
911 return "invalid";
912}
914static const char* cgltf_write_str_interpolation_type(cgltf_interpolation_type interpolation_type)
915{
916 switch (interpolation_type)
917 {
918 case cgltf_interpolation_type_linear:
919 return "LINEAR";
920 case cgltf_interpolation_type_step:
921 return "STEP";
922 case cgltf_interpolation_type_cubic_spline:
923 return "CUBICSPLINE";
924 default:
925 break;
926 }
927 return "invalid";
928}
930static void cgltf_write_path_type(cgltf_write_context* context, const char *label, cgltf_animation_path_type path_type)
931{
932 cgltf_write_strprop(context, label, cgltf_write_str_path_type(path_type));
933}
935static void cgltf_write_interpolation_type(cgltf_write_context* context, const char *label, cgltf_interpolation_type interpolation_type)
936{
937 cgltf_write_strprop(context, label, cgltf_write_str_interpolation_type(interpolation_type));
938}
940static void cgltf_write_animation_sampler(cgltf_write_context* context, const cgltf_animation_sampler* animation_sampler)
941{
942 cgltf_write_line(context, "{");
943 cgltf_write_interpolation_type(context, "interpolation", animation_sampler->interpolation);
944 CGLTF_WRITE_IDXPROP("input", animation_sampler->input, context->data->accessors);
945 CGLTF_WRITE_IDXPROP("output", animation_sampler->output, context->data->accessors);
946 cgltf_write_extras(context, &animation_sampler->extras);
947 cgltf_write_line(context, "}");
948}
950static void cgltf_write_animation_channel(cgltf_write_context* context, const cgltf_animation* animation, const cgltf_animation_channel* animation_channel)
951{
952 cgltf_write_line(context, "{");
953 CGLTF_WRITE_IDXPROP("sampler", animation_channel->sampler, animation->samplers);
954 cgltf_write_line(context, "\"target\": {");
955 CGLTF_WRITE_IDXPROP("node", animation_channel->target_node, context->data->nodes);
956 cgltf_write_path_type(context, "path", animation_channel->target_path);
957 cgltf_write_line(context, "}");
958 cgltf_write_extras(context, &animation_channel->extras);
959 cgltf_write_line(context, "}");
960}
962static void cgltf_write_animation(cgltf_write_context* context, const cgltf_animation* animation)
963{
964 cgltf_write_line(context, "{");
965 cgltf_write_strprop(context, "name", animation->name);
967 if (animation->samplers_count > 0)
968 {
969 cgltf_write_line(context, "\"samplers\": [");
970 for (cgltf_size i = 0; i < animation->samplers_count; ++i)
971 {
972 cgltf_write_animation_sampler(context, animation->samplers + i);
973 }
974 cgltf_write_line(context, "]");
975 }
976 if (animation->channels_count > 0)
977 {
978 cgltf_write_line(context, "\"channels\": [");
979 for (cgltf_size i = 0; i < animation->channels_count; ++i)
980 {
981 cgltf_write_animation_channel(context, animation, animation->channels + i);
982 }
983 cgltf_write_line(context, "]");
984 }
985 cgltf_write_extras(context, &animation->extras);
986 cgltf_write_line(context, "}");
987}
989static void cgltf_write_sampler(cgltf_write_context* context, const cgltf_sampler* sampler)
990{
991 cgltf_write_line(context, "{");
992 cgltf_write_strprop(context, "name", sampler->name);
993 cgltf_write_intprop(context, "magFilter", sampler->mag_filter, 0);
994 cgltf_write_intprop(context, "minFilter", sampler->min_filter, 0);
995 cgltf_write_intprop(context, "wrapS", sampler->wrap_s, 10497);
996 cgltf_write_intprop(context, "wrapT", sampler->wrap_t, 10497);
997 cgltf_write_extras(context, &sampler->extras);
998 cgltf_write_line(context, "}");
999}
1001static void cgltf_write_node(cgltf_write_context* context, const cgltf_node* node)
1002{
1003 cgltf_write_line(context, "{");
1004 CGLTF_WRITE_IDXARRPROP("children", node->children_count, node->children, context->data->nodes);
1005 CGLTF_WRITE_IDXPROP("mesh", node->mesh, context->data->meshes);
1006 cgltf_write_strprop(context, "name", node->name);
1007 if (node->has_matrix)
1008 {
1009 cgltf_write_floatarrayprop(context, "matrix", node->matrix, 16);
1010 }
1011 if (node->has_translation)
1012 {
1013 cgltf_write_floatarrayprop(context, "translation", node->translation, 3);
1014 }
1015 if (node->has_rotation)
1016 {
1017 cgltf_write_floatarrayprop(context, "rotation", node->rotation, 4);
1018 }
1019 if (node->has_scale)
1020 {
1021 cgltf_write_floatarrayprop(context, "scale", node->scale, 3);
1022 }
1023 if (node->skin)
1024 {
1025 CGLTF_WRITE_IDXPROP("skin", node->skin, context->data->skins);
1026 }
1028 bool has_extension = node->light || (node->has_mesh_gpu_instancing && node->mesh_gpu_instancing.attributes_count > 0);
1029 if(has_extension)
1030 cgltf_write_line(context, "\"extensions\": {");
1032 if (node->light)
1033 {
1034 context->extension_flags |= CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL;
1035 cgltf_write_line(context, "\"KHR_lights_punctual\": {");
1036 CGLTF_WRITE_IDXPROP("light", node->light, context->data->lights);
1037 cgltf_write_line(context, "}");
1038 }
1040 if (node->has_mesh_gpu_instancing && node->mesh_gpu_instancing.attributes_count > 0)
1041 {
1042 context->extension_flags |= CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING;
1043 context->required_extension_flags |= CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING;
1045 cgltf_write_line(context, "\"EXT_mesh_gpu_instancing\": {");
1046 {
1047 cgltf_write_line(context, "\"attributes\": {");
1048 {
1049 for (cgltf_size i = 0; i < node->mesh_gpu_instancing.attributes_count; ++i)
1050 {
1051 const cgltf_attribute* attr = node->mesh_gpu_instancing.attributes + i;
1052 CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
1053 }
1054 }
1055 cgltf_write_line(context, "}");
1056 }
1057 cgltf_write_line(context, "}");
1058 }
1060 if (has_extension)
1061 cgltf_write_line(context, "}");
1063 if (node->weights_count > 0)
1064 {
1065 cgltf_write_floatarrayprop(context, "weights", node->weights, node->weights_count);
1066 }
1068 if (node->camera)
1069 {
1070 CGLTF_WRITE_IDXPROP("camera", node->camera, context->data->cameras);
1071 }
1073 cgltf_write_extras(context, &node->extras);
1074 cgltf_write_line(context, "}");
1075}
1077static void cgltf_write_scene(cgltf_write_context* context, const cgltf_scene* scene)
1078{
1079 cgltf_write_line(context, "{");
1080 cgltf_write_strprop(context, "name", scene->name);
1081 CGLTF_WRITE_IDXARRPROP("nodes", scene->nodes_count, scene->nodes, context->data->nodes);
1082 cgltf_write_extras(context, &scene->extras);
1083 cgltf_write_line(context, "}");
1084}
1086static void cgltf_write_accessor(cgltf_write_context* context, const cgltf_accessor* accessor)
1087{
1088 cgltf_write_line(context, "{");
1089 cgltf_write_strprop(context, "name", accessor->name);
1090 CGLTF_WRITE_IDXPROP("bufferView", accessor->buffer_view, context->data->buffer_views);
1091 cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->component_type), 0);
1092 cgltf_write_strprop(context, "type", cgltf_str_from_type(accessor->type));
1093 cgltf_size dim = cgltf_dim_from_type(accessor->type);
1094 cgltf_write_boolprop_optional(context, "normalized", (bool)accessor->normalized, false);
1095 cgltf_write_sizeprop(context, "byteOffset", (int)accessor->offset, 0);
1096 cgltf_write_intprop(context, "count", (int)accessor->count, -1);
1097 if (accessor->has_min)
1098 {
1099 cgltf_write_floatarrayprop(context, "min", accessor->min, dim);
1100 }
1101 if (accessor->has_max)
1102 {
1103 cgltf_write_floatarrayprop(context, "max", accessor->max, dim);
1104 }
1105 if (accessor->is_sparse)
1106 {
1107 cgltf_write_line(context, "\"sparse\": {");
1108 cgltf_write_intprop(context, "count", (int)accessor->sparse.count, 0);
1109 cgltf_write_line(context, "\"indices\": {");
1110 cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.indices_byte_offset, 0);
1111 CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.indices_buffer_view, context->data->buffer_views);
1112 cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->sparse.indices_component_type), 0);
1113 cgltf_write_line(context, "}");
1114 cgltf_write_line(context, "\"values\": {");
1115 cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.values_byte_offset, 0);
1116 CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.values_buffer_view, context->data->buffer_views);
1117 cgltf_write_line(context, "}");
1118 cgltf_write_line(context, "}");
1119 }
1120 cgltf_write_extras(context, &accessor->extras);
1121 cgltf_write_line(context, "}");
1122}
1124static void cgltf_write_camera(cgltf_write_context* context, const cgltf_camera* camera)
1125{
1126 cgltf_write_line(context, "{");
1127 cgltf_write_strprop(context, "type", cgltf_str_from_camera_type(camera->type));
1128 if (camera->name)
1129 {
1130 cgltf_write_strprop(context, "name", camera->name);
1131 }
1133 if (camera->type == cgltf_camera_type_orthographic)
1134 {
1135 cgltf_write_line(context, "\"orthographic\": {");
1136 cgltf_write_floatprop(context, "xmag", camera->data.orthographic.xmag, -1.0f);
1137 cgltf_write_floatprop(context, "ymag", camera->data.orthographic.ymag, -1.0f);
1138 cgltf_write_floatprop(context, "zfar", camera->data.orthographic.zfar, -1.0f);
1139 cgltf_write_floatprop(context, "znear", camera->data.orthographic.znear, -1.0f);
1140 cgltf_write_extras(context, &camera->data.orthographic.extras);
1141 cgltf_write_line(context, "}");
1142 }
1143 else if (camera->type == cgltf_camera_type_perspective)
1144 {
1145 cgltf_write_line(context, "\"perspective\": {");
1147 if (camera->data.perspective.has_aspect_ratio) {
1148 cgltf_write_floatprop(context, "aspectRatio", camera->data.perspective.aspect_ratio, -1.0f);
1149 }
1151 cgltf_write_floatprop(context, "yfov", camera->data.perspective.yfov, -1.0f);
1153 if (camera->data.perspective.has_zfar) {
1154 cgltf_write_floatprop(context, "zfar", camera->data.perspective.zfar, -1.0f);
1155 }
1157 cgltf_write_floatprop(context, "znear", camera->data.perspective.znear, -1.0f);
1158 cgltf_write_extras(context, &camera->data.perspective.extras);
1159 cgltf_write_line(context, "}");
1160 }
1161 cgltf_write_extras(context, &camera->extras);
1162 cgltf_write_line(context, "}");
1163}
1165static void cgltf_write_light(cgltf_write_context* context, const cgltf_light* light)
1166{
1167 context->extension_flags |= CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL;
1169 cgltf_write_line(context, "{");
1170 cgltf_write_strprop(context, "type", cgltf_str_from_light_type(light->type));
1171 if (light->name)
1172 {
1173 cgltf_write_strprop(context, "name", light->name);
1174 }
1175 if (cgltf_check_floatarray(light->color, 3, 1.0f))
1176 {
1177 cgltf_write_floatarrayprop(context, "color", light->color, 3);
1178 }
1179 cgltf_write_floatprop(context, "intensity", light->intensity, 1.0f);
1180 cgltf_write_floatprop(context, "range", light->range, 0.0f);
1182 if (light->type == cgltf_light_type_spot)
1183 {
1184 cgltf_write_line(context, "\"spot\": {");
1185 cgltf_write_floatprop(context, "innerConeAngle", light->spot_inner_cone_angle, 0.0f);
1186 cgltf_write_floatprop(context, "outerConeAngle", light->spot_outer_cone_angle, 3.14159265358979323846f/4.0f);
1187 cgltf_write_line(context, "}");
1188 }
1189 cgltf_write_extras( context, &light->extras );
1190 cgltf_write_line(context, "}");
1191}
1193static void cgltf_write_variant(cgltf_write_context* context, const cgltf_material_variant* variant)
1194{
1195 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS;
1197 cgltf_write_line(context, "{");
1198 cgltf_write_strprop(context, "name", variant->name);
1199 cgltf_write_extras(context, &variant->extras);
1200 cgltf_write_line(context, "}");
1201}
1203static void cgltf_write_glb(FILE* file, const void* json_buf, const cgltf_size json_size, const void* bin_buf, const cgltf_size bin_size)
1204{
1205 char header[GlbHeaderSize];
1206 char chunk_header[GlbChunkHeaderSize];
1207 char json_pad[3] = { 0x20, 0x20, 0x20 };
1208 char bin_pad[3] = { 0, 0, 0 };
1210 cgltf_size json_padsize = (json_size % 4 != 0) ? 4 - json_size % 4 : 0;
1211 cgltf_size bin_padsize = (bin_size % 4 != 0) ? 4 - bin_size % 4 : 0;
1212 cgltf_size total_size = GlbHeaderSize + GlbChunkHeaderSize + json_size + json_padsize;
1213 if (bin_buf != NULL && bin_size > 0) {
1214 total_size += GlbChunkHeaderSize + bin_size + bin_padsize;
1215 }
1217 // Write a GLB header
1218 memcpy(header, &GlbMagic, 4);
1219 memcpy(header + 4, &GlbVersion, 4);
1220 memcpy(header + 8, &total_size, 4);
1221 fwrite(header, 1, GlbHeaderSize, file);
1223 // Write a JSON chunk (header & data)
1224 uint32_t json_chunk_size = (uint32_t)(json_size + json_padsize);
1225 memcpy(chunk_header, &json_chunk_size, 4);
1226 memcpy(chunk_header + 4, &GlbMagicJsonChunk, 4);
1227 fwrite(chunk_header, 1, GlbChunkHeaderSize, file);
1229 fwrite(json_buf, 1, json_size, file);
1230 fwrite(json_pad, 1, json_padsize, file);
1232 if (bin_buf != NULL && bin_size > 0) {
1233 // Write a binary chunk (header & data)
1234 uint32_t bin_chunk_size = (uint32_t)(bin_size + bin_padsize);
1235 memcpy(chunk_header, &bin_chunk_size, 4);
1236 memcpy(chunk_header + 4, &GlbMagicBinChunk, 4);
1237 fwrite(chunk_header, 1, GlbChunkHeaderSize, file);
1239 fwrite(bin_buf, 1, bin_size, file);
1240 fwrite(bin_pad, 1, bin_padsize, file);
1241 }
1242}
1244cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data)
1245{
1246 cgltf_size expected = cgltf_write(options, NULL, 0, data);
1247 char* buffer = (char*) malloc(expected);
1248 cgltf_size actual = cgltf_write(options, buffer, expected, data);
1249 if (expected != actual) {
1250 fprintf(stderr, "Error: expected %zu bytes but wrote %zu bytes.\n", expected, actual);
1251 }
1252 FILE* file = fopen(path, "wb");
1253 if (!file)
1254 {
1255 return cgltf_result_file_not_found;
1256 }
1257 // Note that cgltf_write() includes a null terminator, which we omit from the file content.
1258 if (options->type == cgltf_file_type_glb) {
1259 cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size);
1260 } else {
1261 // Write a plain JSON file.
1262 fwrite(buffer, actual - 1, 1, file);
1263 }
1264 fclose(file);
1265 free(buffer);
1266 return cgltf_result_success;
1267}
1269static void cgltf_write_extensions(cgltf_write_context* context, uint32_t extension_flags)
1270{
1271 if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM) {
1272 cgltf_write_stritem(context, "KHR_texture_transform");
1273 }
1274 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT) {
1275 cgltf_write_stritem(context, "KHR_materials_unlit");
1276 }
1277 if (extension_flags & CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS) {
1278 cgltf_write_stritem(context, "KHR_materials_pbrSpecularGlossiness");
1279 }
1280 if (extension_flags & CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL) {
1281 cgltf_write_stritem(context, "KHR_lights_punctual");
1282 }
1283 if (extension_flags & CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION) {
1284 cgltf_write_stritem(context, "KHR_draco_mesh_compression");
1285 }
1286 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT) {
1287 cgltf_write_stritem(context, "KHR_materials_clearcoat");
1288 }
1289 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IOR) {
1290 cgltf_write_stritem(context, "KHR_materials_ior");
1291 }
1292 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR) {
1293 cgltf_write_stritem(context, "KHR_materials_specular");
1294 }
1295 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION) {
1296 cgltf_write_stritem(context, "KHR_materials_transmission");
1297 }
1298 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN) {
1299 cgltf_write_stritem(context, "KHR_materials_sheen");
1300 }
1301 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS) {
1302 cgltf_write_stritem(context, "KHR_materials_variants");
1303 }
1304 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME) {
1305 cgltf_write_stritem(context, "KHR_materials_volume");
1306 }
1307 if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_BASISU) {
1308 cgltf_write_stritem(context, "KHR_texture_basisu");
1309 }
1310 if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_WEBP) {
1311 cgltf_write_stritem(context, "EXT_texture_webp");
1312 }
1313 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH) {
1314 cgltf_write_stritem(context, "KHR_materials_emissive_strength");
1315 }
1316 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE) {
1317 cgltf_write_stritem(context, "KHR_materials_iridescence");
1318 }
1319 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_DIFFUSE_TRANSMISSION) {
1320 cgltf_write_stritem(context, "KHR_materials_diffuse_transmission");
1321 }
1322 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_ANISOTROPY) {
1323 cgltf_write_stritem(context, "KHR_materials_anisotropy");
1324 }
1325 if (extension_flags & CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING) {
1326 cgltf_write_stritem(context, "EXT_mesh_gpu_instancing");
1327 }
1328 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_DISPERSION) {
1329 cgltf_write_stritem(context, "KHR_materials_dispersion");
1330 }
1331}
1333cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data)
1334{
1335 (void)options;
1336 cgltf_write_context ctx;
1337 ctx.buffer = buffer;
1338 ctx.buffer_size = size;
1339 ctx.remaining = size;
1340 ctx.cursor = buffer;
1341 ctx.chars_written = 0;
1342 ctx.data = data;
1343 ctx.depth = 1;
1344 ctx.indent = " ";
1345 ctx.needs_comma = 0;
1346 ctx.extension_flags = 0;
1347 ctx.required_extension_flags = 0;
1349 cgltf_write_context* context = &ctx;
1351 CGLTF_SPRINTF("{");
1353 if (data->accessors_count > 0)
1354 {
1355 cgltf_write_line(context, "\"accessors\": [");
1356 for (cgltf_size i = 0; i < data->accessors_count; ++i)
1357 {
1358 cgltf_write_accessor(context, data->accessors + i);
1359 }
1360 cgltf_write_line(context, "]");
1361 }
1363 cgltf_write_asset(context, &data->asset);
1365 if (data->buffer_views_count > 0)
1366 {
1367 cgltf_write_line(context, "\"bufferViews\": [");
1368 for (cgltf_size i = 0; i < data->buffer_views_count; ++i)
1369 {
1370 cgltf_write_buffer_view(context, data->buffer_views + i);
1371 }
1372 cgltf_write_line(context, "]");
1373 }
1375 if (data->buffers_count > 0)
1376 {
1377 cgltf_write_line(context, "\"buffers\": [");
1378 for (cgltf_size i = 0; i < data->buffers_count; ++i)
1379 {
1380 cgltf_write_buffer(context, data->buffers + i);
1381 }
1382 cgltf_write_line(context, "]");
1383 }
1385 if (data->images_count > 0)
1386 {
1387 cgltf_write_line(context, "\"images\": [");
1388 for (cgltf_size i = 0; i < data->images_count; ++i)
1389 {
1390 cgltf_write_image(context, data->images + i);
1391 }
1392 cgltf_write_line(context, "]");
1393 }
1395 if (data->meshes_count > 0)
1396 {
1397 cgltf_write_line(context, "\"meshes\": [");
1398 for (cgltf_size i = 0; i < data->meshes_count; ++i)
1399 {
1400 cgltf_write_mesh(context, data->meshes + i);
1401 }
1402 cgltf_write_line(context, "]");
1403 }
1405 if (data->materials_count > 0)
1406 {
1407 cgltf_write_line(context, "\"materials\": [");
1408 for (cgltf_size i = 0; i < data->materials_count; ++i)
1409 {
1410 cgltf_write_material(context, data->materials + i);
1411 }
1412 cgltf_write_line(context, "]");
1413 }
1415 if (data->nodes_count > 0)
1416 {
1417 cgltf_write_line(context, "\"nodes\": [");
1418 for (cgltf_size i = 0; i < data->nodes_count; ++i)
1419 {
1420 cgltf_write_node(context, data->nodes + i);
1421 }
1422 cgltf_write_line(context, "]");
1423 }
1425 if (data->samplers_count > 0)
1426 {
1427 cgltf_write_line(context, "\"samplers\": [");
1428 for (cgltf_size i = 0; i < data->samplers_count; ++i)
1429 {
1430 cgltf_write_sampler(context, data->samplers + i);
1431 }
1432 cgltf_write_line(context, "]");
1433 }
1435 CGLTF_WRITE_IDXPROP("scene", data->scene, data->scenes);
1437 if (data->scenes_count > 0)
1438 {
1439 cgltf_write_line(context, "\"scenes\": [");
1440 for (cgltf_size i = 0; i < data->scenes_count; ++i)
1441 {
1442 cgltf_write_scene(context, data->scenes + i);
1443 }
1444 cgltf_write_line(context, "]");
1445 }
1447 if (data->textures_count > 0)
1448 {
1449 cgltf_write_line(context, "\"textures\": [");
1450 for (cgltf_size i = 0; i < data->textures_count; ++i)
1451 {
1452 cgltf_write_texture(context, data->textures + i);
1453 }
1454 cgltf_write_line(context, "]");
1455 }
1457 if (data->skins_count > 0)
1458 {
1459 cgltf_write_line(context, "\"skins\": [");
1460 for (cgltf_size i = 0; i < data->skins_count; ++i)
1461 {
1462 cgltf_write_skin(context, data->skins + i);
1463 }
1464 cgltf_write_line(context, "]");
1465 }
1467 if (data->animations_count > 0)
1468 {
1469 cgltf_write_line(context, "\"animations\": [");
1470 for (cgltf_size i = 0; i < data->animations_count; ++i)
1471 {
1472 cgltf_write_animation(context, data->animations + i);
1473 }
1474 cgltf_write_line(context, "]");
1475 }
1477 if (data->cameras_count > 0)
1478 {
1479 cgltf_write_line(context, "\"cameras\": [");
1480 for (cgltf_size i = 0; i < data->cameras_count; ++i)
1481 {
1482 cgltf_write_camera(context, data->cameras + i);
1483 }
1484 cgltf_write_line(context, "]");
1485 }
1487 if (data->lights_count > 0 || data->variants_count > 0)
1488 {
1489 cgltf_write_line(context, "\"extensions\": {");
1491 if (data->lights_count > 0)
1492 {
1493 cgltf_write_line(context, "\"KHR_lights_punctual\": {");
1494 cgltf_write_line(context, "\"lights\": [");
1495 for (cgltf_size i = 0; i < data->lights_count; ++i)
1496 {
1497 cgltf_write_light(context, data->lights + i);
1498 }
1499 cgltf_write_line(context, "]");
1500 cgltf_write_line(context, "}");
1501 }
1503 if (data->variants_count)
1504 {
1505 cgltf_write_line(context, "\"KHR_materials_variants\": {");
1506 cgltf_write_line(context, "\"variants\": [");
1507 for (cgltf_size i = 0; i < data->variants_count; ++i)
1508 {
1509 cgltf_write_variant(context, data->variants + i);
1510 }
1511 cgltf_write_line(context, "]");
1512 cgltf_write_line(context, "}");
1513 }
1515 cgltf_write_line(context, "}");
1516 }
1518 if (context->extension_flags != 0)
1519 {
1520 cgltf_write_line(context, "\"extensionsUsed\": [");
1521 cgltf_write_extensions(context, context->extension_flags);
1522 cgltf_write_line(context, "]");
1523 }
1525 if (context->required_extension_flags != 0)
1526 {
1527 cgltf_write_line(context, "\"extensionsRequired\": [");
1528 cgltf_write_extensions(context, context->required_extension_flags);
1529 cgltf_write_line(context, "]");
1530 }
1532 cgltf_write_extras(context, &data->extras);
1534 CGLTF_SPRINTF("\n}\n");
1536 // snprintf does not include the null terminator in its return value, so be sure to include it
1537 // in the returned byte count.
1538 return 1 + ctx.chars_written;
1539}
1541#endif /* #ifdef CGLTF_WRITE_IMPLEMENTATION */
1543/* cgltf is distributed under MIT license:
1544 *
1545 * Copyright (c) 2019-2021 Philip Rideout
1547 * Permission is hereby granted, free of charge, to any person obtaining a copy
1548 * of this software and associated documentation files (the "Software"), to deal
1549 * in the Software without restriction, including without limitation the rights
1550 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1551 * copies of the Software, and to permit persons to whom the Software is
1552 * furnished to do so, subject to the following conditions:
1554 * The above copyright notice and this permission notice shall be included in all
1555 * copies or substantial portions of the Software.
1557 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1558 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1559 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1560 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1561 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1562 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1563 * SOFTWARE.
1564 */
index : raylib-jai
---