cgltf_write.h 52 KB

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