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