cgltf_write.h 44 KB

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