Unravel Engine C++ Reference
Loading...
Searching...
No Matches
asset_compiler.cpp
Go to the documentation of this file.
1#include "asset_compiler.h"
2#include "asset_writer.h"
3#include "asset_manifest.h"
5
6#include <bx/error.h>
7#include <bx/process.h>
8#include <bx/string.h>
9
10#include <graphics/shader.h>
11#include <graphics/texture.h>
12#include <logging/logging.h>
13#include <uuid/uuid.h>
14
17
19#include <engine/engine.h>
32
34
36
37#include <fstream>
38#include <monopp/mono_jit.h>
39#include <regex>
41
43
45{
46
47namespace
48{
49
50auto resolve_path(const std::string& key) -> fs::path
51{
52 return fs::absolute(fs::resolve_protocol(key));
53}
54
55auto resolve_input_file(const fs::path& key) -> fs::path
56{
57 fs::path absolute_path = fs::convert_to_protocol(key);
59 if(absolute_path.extension() == ".meta")
60 {
61 absolute_path.replace_extension();
62 }
63 return absolute_path;
64}
65
66auto escape_str(const std::string& str) -> std::string
67{
68 return "\"" + str + "\"";
69}
70
71auto run_process(const std::string& process,
72 const std::vector<std::string>& args_array,
73 bool chekc_retcode,
74 std::string& err) -> bool
75{
76 auto result = subprocess::call(process, args_array);
77 err = result.out_output;
78
79 if(!result.err_output.empty())
80 {
81 if(!err.empty())
82 {
83 err += "\n";
84 }
85
86 err += result.err_output;
87 }
88
89 if(err.find("error") != std::string::npos)
90 {
91 return false;
92 }
93
94 return result.retcode == 0;
95}
96
97void copy_compiled_file(const fs::path& from, const fs::path& to, const std::string& str_input)
98{
99 fs::error_code err;
100 asset_writer::atomic_copy_file(from, to, err);
101
102 if(!err)
103 {
104 //APPLOG_INFO("Successful compilation of {0} -> {1}", str_input, to.string());
105 }
106 else
107 {
108 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}", str_input, to.filename().string(), err.message());
109 }
110}
111
112auto select_compressed_format(gfx::texture_format input_format,
113 const fs::path& extension,
115{
117 {
118 return gfx::texture_format::Unknown;
119 }
120
121 auto info = gfx::get_format_info(input_format);
122
123 if(extension == ".hdr" || extension == ".exr")
124 {
125 info.is_hdr = true;
126 }
127
128 // 1) HDR? Use BC6H for color data, ignoring alpha (HDR with alpha is non-trivial).
129 if(info.is_hdr)
130 {
131 // BC6H: color (RGB) 16F
132 // No standard BC format for HDR alpha in the block-compression range.
133 return gfx::texture_format::BC6H;
134 }
135
136 // 2) Single channel => BC4
137 // e.g., for grayscale height map or single-channel mask
138 if(info.num_hannels == 1)
139 {
140 return gfx::texture_format::BC4;
141 }
142
143 // 3) Two channel => BC5
144 // e.g., typical for 2D vector data, normal map XY
145 if(info.num_hannels == 2)
146 {
147 return gfx::texture_format::BC5;
148 }
149
150 // 4) If we reach here, we have 3 or 4 channels in LDR.
151
152 // 4a) No alpha needed => choose BC1 or BC7, etc.
153 if(!info.has_alpha_channel)
154 {
155 switch(quality)
156 {
158 // BC1 is cheap and has no alpha
159 return gfx::texture_format::BC1;
161 // BC1 is standard for color w/out alpha
162 return gfx::texture_format::BC1;
164 // BC7 is higher quality for color, also supports alpha but not needed here.
165 // It is also really slow for encoding so don't use it for now.
166 return gfx::texture_format::BC1;
167 default:
168 break;
169 }
170 // fallback
171 return gfx::texture_format::BC1;
172 }
173 else
174 {
175 // 4b) We do have alpha => choose BC2, BC3, or BC7.
176 // BC2 (DXT3) is old and rarely used except for sharp alpha transitions.
177 // BC3 (DXT5) is the typical solution for alpha textures if BC7 is not an option.
178 // BC7 is better (but bigger decode cost).
179 switch(quality)
180 {
182 return gfx::texture_format::BC3;
184 return gfx::texture_format::BC3; // DXT5
186 // BC7 is best BC for RGBA
187 // It is also really slow for encoding so don't use it for now.
188 return gfx::texture_format::BC3;
189 default:
190 break;
191 }
192 // fallback
193 return gfx::texture_format::BC3;
194 }
195}
196
197auto compile_texture_to_file(const fs::path& input_path,
198 const fs::path& output_path,
199 const texture_importer_meta& importer,
200 const std::string& protocol) -> bool
201{
202 std::string str_input = input_path.string();
203 std::string str_output = output_path.string();
204
205 bool try_compress = protocol == "app";
206
207 auto quality = importer.quality;
209 {
210 auto& ctx = engine::context();
211 if(ctx.has<settings>())
212 {
213 auto& ss = ctx.get<settings>();
214 quality.compression = ss.assets.texture.default_compression;
215 }
216 }
217
219 {
220 auto& ctx = engine::context();
221 if(ctx.has<settings>())
222 {
223 auto& ss = ctx.get<settings>();
224 quality.max_size = ss.assets.texture.default_max_size;
225 }
226 }
227
228 // If still default, set to normal quality
230 {
232 }
233
234 // If still default, set to 2048
236 {
238 }
239
240 auto format =
241 select_compressed_format(gfx::texture_format::RGBA8, input_path.extension(), quality.compression);
242
243 std::vector<std::string> args_array = {
244 "-f",
245 str_input,
246 "-o",
247 str_output,
248 "--as",
249 "dds",
250 };
251
252 if(try_compress && format != gfx::texture_format::Unknown)
253 {
254 args_array.emplace_back("-t");
255 args_array.emplace_back(gfx::to_string(format));
256
257 if(format == gfx::texture_format::BC7 || format == gfx::texture_format::BC6H)
258 {
259 APPLOG_INFO("Compressing to {0}. May take a while.", gfx::to_string(format));
260
261 args_array.emplace_back("-q");
262 args_array.emplace_back("fastest");
263 }
265 {
266 args_array.emplace_back("-q");
267 args_array.emplace_back("highest");
268 }
269 }
270
271 if(importer.generate_mipmaps)
272 {
273 args_array.emplace_back("-m");
274 }
275
276 switch(quality.max_size)
277 {
279 {
280 break;
281 }
283 {
284 args_array.emplace_back("--max");
285 args_array.emplace_back("32");
286 break;
287 }
289 {
290 args_array.emplace_back("--max");
291 args_array.emplace_back("64");
292 break;
293 }
295 {
296 args_array.emplace_back("--max");
297 args_array.emplace_back("128");
298 break;
299 }
301 {
302 args_array.emplace_back("--max");
303 args_array.emplace_back("256");
304 break;
305 }
307 {
308 args_array.emplace_back("--max");
309 args_array.emplace_back("512");
310 break;
311 }
313 {
314 args_array.emplace_back("--max");
315 args_array.emplace_back("1024");
316 break;
317 }
319 {
320 args_array.emplace_back("--max");
321 args_array.emplace_back("2048");
322 break;
323 }
325 {
326 args_array.emplace_back("--max");
327 args_array.emplace_back("4096");
328 break;
329 }
331 {
332 args_array.emplace_back("--max");
333 args_array.emplace_back("8192");
334 break;
335 }
337 {
338 args_array.emplace_back("--max");
339 args_array.emplace_back("16384");
340 break;
341 }
342 }
343
344 switch(importer.type)
345 {
347 {
348 args_array.emplace_back("--equirect");
349 break;
350 }
351
353 {
354 args_array.emplace_back("--normalmap");
355 break;
356 }
357
358 default:
359 break;
360 }
361
362 std::string error;
363
364 // Create an empty file at the output location so the process can write to it
365 {
366 std::ofstream output_file(str_output);
367 (void)output_file;
368 }
369
370 auto texturec = fs::resolve_protocol("binary:/texturec");
371
372 // Run the texture compiler directly to the temporary output location
373 if(!run_process(texturec.string(), args_array, false, error))
374 {
375 APPLOG_ERROR("Failed compilation of {0} with error: {1}", str_input, error);
376 return false;
377 }
378
379 return true;
380}
381
382auto compile_shader_to_file(const fs::path& input_path,
383 const fs::path& output_path,
385{
386 std::string str_input = input_path.string();
387 std::string str_output = output_path.string();
388
389 std::string file = input_path.stem().string();
390 fs::path dir = input_path.parent_path();
391
392 fs::path include = fs::resolve_protocol("engine:/data/shaders");
393 std::string str_include = include.string();
394
395 fs::path varying = dir / (file + ".io");
396
397 fs::error_code err;
398 if(!fs::exists(varying, err))
399 {
400 varying = dir / "varying.def.io";
401 }
402 if(!fs::exists(varying, err))
403 {
404 varying = dir / "varying.def.sc";
405 }
406
407 std::string str_varying = varying.string();
408
409 std::string str_platform;
410 std::string str_profile;
411 std::string str_type;
412 std::string str_opt = "3";
413
414 bool vs = hpp::string_view(file).starts_with("vs_");
415 bool fs = hpp::string_view(file).starts_with("fs_");
416 bool cs = hpp::string_view(file).starts_with("cs_");
417
418 if(renderer == gfx::renderer_type::Vulkan)
419 {
420 str_platform = "windows";
421 str_profile = "spirv";
422 }
423
424 if(renderer == gfx::renderer_type::Direct3D11 || renderer == gfx::renderer_type::Direct3D12)
425 {
426 str_platform = "windows";
427
428 if(vs || fs)
429 {
430 str_profile = "s_5_0";
431 str_opt = "3";
432 }
433 else if(cs)
434 {
435 str_profile = "s_5_0";
436 str_opt = "1";
437 }
438 }
439 else if(renderer == gfx::renderer_type::OpenGLES)
440 {
441 str_platform = "android";
442 str_profile = "100_es";
443 }
444 else if(renderer == gfx::renderer_type::OpenGL)
445 {
446 str_platform = "linux";
447
448 if(vs || fs)
449 str_profile = "140";
450 else if(cs)
451 str_profile = "430";
452 }
453 else if(renderer == gfx::renderer_type::Metal)
454 {
455 str_platform = "osx";
456 str_profile = "metal";
457 }
458
459 if(vs)
460 str_type = "vertex";
461 else if(fs)
462 str_type = "fragment";
463 else if(cs)
464 str_type = "compute";
465 else
466 str_type = "unknown";
467
468 std::vector<std::string> args_array = {
469 "-f",
470 str_input,
471 "-o",
472 str_output,
473 "-i",
474 str_include,
475 "--varyingdef",
476 str_varying,
477 "--type",
478 str_type,
479 "--define",
480 "BGFX_CONFIG_MAX_BONES=" + std::to_string(gfx::get_max_blend_transforms())
481 // "--Werror"
482 };
483
484 if(!str_platform.empty())
485 {
486 args_array.emplace_back("--platform");
487 args_array.emplace_back(str_platform);
488 }
489
490 if(!str_profile.empty())
491 {
492 args_array.emplace_back("-p");
493 args_array.emplace_back(str_profile);
494 }
495
496 if(!str_opt.empty())
497 {
498 args_array.emplace_back("-O");
499 args_array.emplace_back(str_opt);
500 }
501
502 std::string error;
503
504 // Create an empty file at the output location
505 {
506 std::ofstream output_file(str_output);
507 (void)output_file;
508 }
509
510 auto shaderc = fs::resolve_protocol("binary:/shaderc");
511
512 if(!run_process(shaderc.string(), args_array, true, error))
513 {
514 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}", str_input, output_path.filename().string(), error);
515 return false;
516 }
517
518 return true;
519}
520
521auto write_manifest_file(const fs::path& input_path, const fs::path& output_path) -> bool
522{
523 // Generate manifest file for the compiled shader
524 asset_manifest manifest(input_path);
525 manifest.compute_source_sha();
526 auto manifest_path = get_manifest_path(output_path);
527
528 fs::error_code err;
529 bool success = true;
530 asset_writer::atomic_write_file(manifest_path, [&](const fs::path& temp_manifest_path)
531 {
532 success = save_manifest(temp_manifest_path, manifest);
533 }, err);
534
535 return !err && success;
536}
537
538} // namespace
539
540template<>
541auto compile<gfx::shader>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
542{
543 auto absolute_path = resolve_input_file(key);
544 std::string str_input = absolute_path.string();
545
546 auto extension = output.extension();
548
549 fs::error_code err;
550 // Use atomic_write_file to handle the temporary file creation and atomic rename
551 asset_writer::atomic_write_file(output, [&](const fs::path& temp_output)
552 {
553 compile_shader_to_file(
554 absolute_path,
555 temp_output,
557 );
558 }, err);
559
560 if(err)
561 {
562 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
563 str_input, output.filename().string(), err.message());
564 return false;
565 }
566
567 if(!write_manifest_file(absolute_path, output))
568 {
569 APPLOG_ERROR("Failed to write manifest for compiled shader: {0}", output.string());
570 return false;
571 }
572
573 return true;
574}
575
576template<>
577auto read_importer<gfx::texture>(asset_manager& am, const fs::path& key) -> std::shared_ptr<asset_importer_meta>
578{
579 auto absolute = fs::resolve_protocol(key).string();
580 asset_meta meta;
581 if(load_from_file(absolute, meta))
582 {
583 if(!meta.importer)
584 {
585 meta.importer = std::make_shared<texture_importer_meta>();
586
587 meta.uid = am.add_asset_info_for_path(resolve_input_file(key), meta, true);
588
589 fs::error_code err;
590 asset_writer::atomic_write_file(absolute, [&](const fs::path& temp)
591 {
592 save_to_file(temp.string(), meta);
593 }, err);
594
595 return nullptr;
596 }
597 }
598
599 return meta.importer;
600}
601
602template<>
603auto compile<gfx::texture>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
604{
605 auto base_importer = read_importer<gfx::texture>(am, key);
606
607 if(!base_importer)
608 {
609 return true;
610 }
611 auto importer = std::static_pointer_cast<texture_importer_meta>(base_importer);
612
613 auto protocol = fs::extract_protocol(fs::convert_to_protocol(key)).generic_string();
614 auto absolute_path = resolve_input_file(key);
615 std::string str_input = absolute_path.string();
616
617 fs::error_code err;
618
619 asset_writer::atomic_write_file(output, [&](const fs::path& temp_output)
620 {
621 compile_texture_to_file(
622 absolute_path,
623 temp_output,
624 *importer,
625 protocol
626 );
627 }, err);
628
629 if(err)
630 {
631 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
632 str_input, output.filename().string(), err.message());
633 return false;
634 }
635
636 if(!write_manifest_file(absolute_path, output))
637 {
638 APPLOG_ERROR("Failed to write manifest for compiled texture: {0}", output.string());
639 return false;
640 }
641
642 return true;
643}
644
645template<>
646auto compile<material>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
647{
648 auto absolute_path = resolve_input_file(key);
649
650 std::string str_input = absolute_path.string();
651
652 fs::error_code err;
653
654 std::shared_ptr<material> material;
655 {
656 load_from_file(str_input, material);
657
658 asset_writer::atomic_write_file(output, [&](const fs::path& temp)
659 {
660 save_to_file_bin(temp.string(), material);
661 }, err);
662 }
663
664 if(err)
665 {
666 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
667 str_input, output.filename().string(), err.message());
668 return false;
669 }
670
671 if(!write_manifest_file(absolute_path, output))
672 {
673 APPLOG_ERROR("Failed to write manifest for compiled material: {0}", output.string());
674 return false;
675 }
676
677 return true;
678}
679
680template<>
681auto read_importer<mesh>(asset_manager& am, const fs::path& key) -> std::shared_ptr<asset_importer_meta>
682{
683 auto absolute = fs::resolve_protocol(key).string();
684 asset_meta meta;
685 if(load_from_file(absolute, meta))
686 {
687 if(!meta.importer)
688 {
689 meta.importer = std::make_shared<mesh_importer_meta>();
690
691 meta.uid = am.add_asset_info_for_path(resolve_input_file(key), meta, true);
692
693 fs::error_code err;
694 asset_writer::atomic_write_file(absolute, [&](const fs::path& temp)
695 {
696 save_to_file(temp.string(), meta);
697 }, err);
698
699 return nullptr;
700 }
701 }
702
703 return meta.importer;
704}
705
706template<>
707auto compile<mesh>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
708{
709 // Try to import first.
710 auto base_importer = read_importer<mesh>(am, key);
711
712 if(!base_importer)
713 {
714 return true;
715 }
716
717 auto importer = std::static_pointer_cast<mesh_importer_meta>(base_importer);
718
719 auto absolute_path = resolve_input_file(key);
720
721 std::string str_input = absolute_path.string();
722
723 fs::error_code err;
724
725 fs::path file = absolute_path.stem();
726 fs::path dir = absolute_path.parent_path();
727
728 mesh::load_data data;
729 std::vector<animation_clip> animations;
730 std::vector<importer::imported_material> materials;
731 std::vector<importer::imported_texture> textures;
732
733 if(!importer::load_mesh_data_from_file(am, absolute_path, *importer, data, animations, materials, textures))
734 {
735 APPLOG_ERROR("Failed compilation of {0}", str_input);
736 return false;
737 }
738 if(!data.vertex_data.empty())
739 {
740 asset_writer::atomic_write_file(output, [&](const fs::path& temp)
741 {
742 save_to_file_bin(temp.string(), data);
743 }, err);
744 }
745
746 {
747 for(const auto& animation : animations)
748 {
749 fs::path anim_output;
750 if(animation.name.empty())
751 {
752 anim_output = (dir / file).string() + ".anim";
753 }
754 else
755 {
756 anim_output = dir / (animation.name + ".anim");
757 }
758
759 asset_writer::atomic_write_file(anim_output, [&](const fs::path& temp)
760 {
761 save_to_file(temp.string(), animation);
762 }, err);
763
764 // APPLOG_INFO("Successful compilation of animation {0}", animation.name);
765 }
766
767 for(const auto& material : materials)
768 {
769
770 fs::path mat_output;
771
772 if(material.name.empty())
773 {
774 mat_output = (dir / file).string() + ".mat";
775 }
776 else
777 {
778 mat_output = dir / (material.name + ".mat");
779 }
780
781 asset_writer::atomic_write_file(mat_output, [&](const fs::path& temp)
782 {
783 save_to_file(temp.string(), material.mat);
784 }, err);
785
786 // APPLOG_INFO("Successful compilation of material {0}", material.name);
787 }
788 }
789
790 if(err)
791 {
792 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
793 str_input, output.filename().string(), err.message());
794 return false;
795 }
796
797 if(!write_manifest_file(absolute_path, output))
798 {
799 APPLOG_ERROR("Failed to write manifest for compiled mesh: {0}", output.string());
800 return false;
801 }
802
803 return true;
804}
805
806template<>
807auto read_importer<animation_clip>(asset_manager& am, const fs::path& key) -> std::shared_ptr<asset_importer_meta>
808{
809 auto absolute = fs::resolve_protocol(key).string();
810 asset_meta meta;
811 if(load_from_file(absolute, meta))
812 {
813 if(!meta.importer)
814 {
815 meta.importer = std::make_shared<animation_importer_meta>();
816
817 meta.uid = am.add_asset_info_for_path(resolve_input_file(key), meta, true);
818
819 fs::error_code err;
820 asset_writer::atomic_write_file(absolute, [&](const fs::path& temp)
821 {
822 save_to_file(temp.string(), meta);
823 }, err);
824
825 return nullptr;
826 }
827 }
828
829 return meta.importer;
830}
831
832template<>
833auto compile<animation_clip>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
834{
835 // Try to import first.
836 auto base_importer = read_importer<animation_clip>(am, key);
837
838 if(!base_importer)
839 {
840 return true;
841 }
842
843 auto importer = std::static_pointer_cast<animation_importer_meta>(base_importer);
844
845 auto absolute_path = resolve_input_file(key);
846
847 std::string str_input = absolute_path.string();
848
849 fs::error_code err;
850
851 animation_clip anim;
852 {
853 load_from_file(str_input, anim);
854
855 anim.root_motion.keep_position_y = importer->root_motion.keep_position_y;
856 anim.root_motion.keep_position_xz = importer->root_motion.keep_position_xz;
857 anim.root_motion.keep_rotation = importer->root_motion.keep_rotation;
858 anim.root_motion.keep_in_place = importer->root_motion.keep_in_place;
859
860 fs::error_code err;
861 asset_writer::atomic_write_file(output, [&](const fs::path& temp)
862 {
863 save_to_file_bin(temp.string(), anim);
864 }, err);
865 }
866
867 if(err)
868 {
869 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
870 str_input, output.filename().string(), err.message());
871 return false;
872 }
873
874 if(!write_manifest_file(absolute_path, output))
875 {
876 APPLOG_ERROR("Failed to write manifest for compiled animation: {0}", output.string());
877 return false;
878 }
879
880 return true;
881}
882
883template<>
884auto compile<font>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
885{
886 auto absolute_path = resolve_input_file(key);
887 std::string str_input = absolute_path.string();
888
889 copy_compiled_file(absolute_path, output, str_input);
890
891 if(!write_manifest_file(absolute_path, output))
892 {
893 APPLOG_ERROR("Failed to write manifest for compiled font: {0}", output.string());
894 return false;
895 }
896
897 return true;
898}
899
900template<>
901auto compile<prefab>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
902{
903 auto absolute_path = resolve_input_file(key);
904 std::string str_input = absolute_path.string();
905
906 copy_compiled_file(absolute_path, output, str_input);
907
908 if(!write_manifest_file(absolute_path, output))
909 {
910 APPLOG_ERROR("Failed to write manifest for compiled prefab: {0}", output.string());
911 return false;
912 }
913
914 return true;
915}
916
917template<>
918auto compile<scene_prefab>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
919{
920 auto absolute_path = resolve_input_file(key);
921 std::string str_input = absolute_path.string();
922
923 copy_compiled_file(absolute_path, output, str_input);
924
925 if(!write_manifest_file(absolute_path, output))
926 {
927 APPLOG_ERROR("Failed to write manifest for compiled scene_prefab: {0}", output.string());
928 return false;
929 }
930
931 return true;
932}
933
934template<>
935auto compile<physics_material>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
936{
937 auto absolute_path = resolve_input_file(key);
938
939 std::string str_input = absolute_path.string();
940
941 fs::error_code err;
942
943 auto material = std::make_shared<physics_material>();
944 {
945 load_from_file(str_input, material);
946
947 asset_writer::atomic_write_file(output, [&](const fs::path& temp)
948 {
949 save_to_file_bin(temp.string(), material);
950 }, err);
951 }
952
953 if(err)
954 {
955 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
956 str_input, output.filename().string(), err.message());
957 return false;
958 }
959
960 if(!write_manifest_file(absolute_path, output))
961 {
962 APPLOG_ERROR("Failed to write manifest for compiled physics_material: {0}", output.string());
963 return false;
964 }
965
966 return true;
967}
968
969template<>
970auto compile<ui_tree>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
971{
972 auto absolute_path = resolve_input_file(key);
973 std::string str_input = absolute_path.string();
974 fs::error_code err;
975
976 auto tree = std::make_shared<ui_tree>();
977 {
978 // For ui_tree, we can load the HTML/RML content directly from file
979 std::ifstream file(absolute_path);
980 if (file.is_open())
981 {
982 std::stringstream buffer;
983 buffer << file.rdbuf();
984 tree->content = buffer.str();
985 file.close();
986 }
987
988 asset_writer::atomic_write_file(output, [&](const fs::path& temp)
989 {
990 save_to_file(temp.string(), tree);
991 }, err);
992 }
993
994 if(err)
995 {
996 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
997 str_input, output.filename().string(), err.message());
998 return false;
999 }
1000
1001 if(!write_manifest_file(absolute_path, output))
1002 {
1003 APPLOG_ERROR("Failed to write manifest for compiled ui_tree: {0}", output.string());
1004 return false;
1005 }
1006
1007 return true;
1008}
1009
1010template<>
1011auto compile<style_sheet>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1012{
1013 auto absolute_path = resolve_input_file(key);
1014 std::string str_input = absolute_path.string();
1015 fs::error_code err;
1016
1017 auto sheet = std::make_shared<style_sheet>();
1018 {
1019 // For style_sheet, we can load the CSS/RCSS content directly from file
1020 std::ifstream file(absolute_path);
1021 if (file.is_open())
1022 {
1023 std::stringstream buffer;
1024 buffer << file.rdbuf();
1025 sheet->content = buffer.str();
1026 file.close();
1027 }
1028
1029 asset_writer::atomic_write_file(output, [&](const fs::path& temp)
1030 {
1031 save_to_file(temp.string(), sheet);
1032 }, err);
1033 }
1034
1035 if(err)
1036 {
1037 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1038 str_input, output.filename().string(), err.message());
1039 return false;
1040 }
1041
1042 if(!write_manifest_file(absolute_path, output))
1043 {
1044 APPLOG_ERROR("Failed to write manifest for compiled style_sheet: {0}", output.string());
1045 return false;
1046 }
1047
1048 return true;
1049}
1050
1051template<>
1052auto read_importer<audio_clip>(asset_manager& am, const fs::path& key) -> std::shared_ptr<asset_importer_meta>
1053{
1054 auto absolute = fs::resolve_protocol(key).string();
1055 asset_meta meta;
1056 if(load_from_file(absolute, meta))
1057 {
1058 if(!meta.importer)
1059 {
1060 meta.importer = std::make_shared<audio_importer_meta>();
1061
1062 meta.uid = am.add_asset_info_for_path(resolve_input_file(key), meta, true);
1063
1064 fs::error_code err;
1065 asset_writer::atomic_write_file(absolute, [&](const fs::path& temp)
1066 {
1067 save_to_file(temp.string(), meta);
1068 }, err);
1069
1070 return nullptr;
1071 }
1072 }
1073
1074 return meta.importer;
1075}
1076
1077template<>
1078auto compile<audio_clip>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1079{
1080 // Try to import first.
1081 auto base_importer = read_importer<audio_clip>(am, key);
1082
1083 if(!base_importer)
1084 {
1085 return true;
1086 }
1087
1088 auto importer = std::static_pointer_cast<audio_importer_meta>(base_importer);
1089
1090 auto absolute_path = resolve_input_file(key);
1091
1092 std::string str_input = absolute_path.string();
1093
1094 fs::error_code err;
1095
1096 audio::sound_data clip;
1097 {
1098 std::string error;
1099 if(!load_from_file(str_input, clip, error))
1100 {
1101 APPLOG_ERROR("Failed compilation of {0} with error: {1}", str_input, error);
1102 return false;
1103 }
1104
1105 if(importer->force_to_mono)
1106 {
1107 clip.convert_to_mono();
1108 }
1109
1110 asset_writer::atomic_write_file(output, [&](const fs::path& temp)
1111 {
1112 save_to_file_bin(temp.string(), clip);
1113 }, err);
1114 }
1115
1116 if(err)
1117 {
1118 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1119 str_input, output.filename().string(), err.message());
1120 return false;
1121 }
1122
1123 if(!write_manifest_file(absolute_path, output))
1124 {
1125 APPLOG_ERROR("Failed to write manifest for compiled audio_clip: {0}", output.string());
1126 return false;
1127 }
1128
1129 return true;
1130}
1131// Struct to hold the parsed error details
1133{
1134 std::string file{}; // Path to the file
1135 int line{}; // Line number of the error
1136 std::string msg{}; // Full error line
1137};
1138
1139// Function to parse all compilation errors
1140auto parse_compilation_errors(const std::string& log) -> std::vector<script_compilation_entry>
1141{
1142 // Regular expression to extract the warning details
1143 std::regex warning_regex(R"((.*)\‍((\d+),\d+\): error .*)");
1144 std::vector<script_compilation_entry> entries;
1145
1146 // Use std::sregex_iterator to find all matches
1147 auto begin = std::sregex_iterator(log.begin(), log.end(), warning_regex);
1148 auto end = std::sregex_iterator();
1149
1150 for(auto it = begin; it != end; ++it)
1151 {
1152 const std::smatch& match = *it;
1153 if(match.size() >= 3)
1154 {
1156 entry.file = match[1].str(); // Extract file path
1157 entry.line = std::stoi(match[2].str()); // Extract line number
1158 entry.msg = match[0].str(); // Extract full warning line
1159 entries.emplace_back(std::move(entry));
1160 }
1161 }
1162
1163 return entries;
1164}
1165
1166// Function to parse all compilation warnings
1167auto parse_compilation_warnings(const std::string& log) -> std::vector<script_compilation_entry>
1168{
1169 // Regular expression to extract the warning details
1170 std::regex warning_regex(R"((.*)\‍((\d+),\d+\): error .*)");
1171 std::vector<script_compilation_entry> entries;
1172
1173 // Use std::sregex_iterator to find all matches
1174 auto begin = std::sregex_iterator(log.begin(), log.end(), warning_regex);
1175 auto end = std::sregex_iterator();
1176
1177 for(auto it = begin; it != end; ++it)
1178 {
1179 const std::smatch& match = *it;
1180 if(match.size() >= 3)
1181 {
1183 entry.file = match[1].str(); // Extract file path
1184 entry.line = std::stoi(match[2].str()); // Extract line number
1185 entry.msg = match[0].str(); // Extract full warning line
1186 entries.emplace_back(std::move(entry));
1187 }
1188
1189 }
1190
1191 return entries;
1192}
1193
1194template<>
1195auto compile<script_library>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1196{
1197 bool result = true;
1198 fs::error_code err;
1199 fs::path temp = fs::temp_directory_path(err);
1200
1201 mono::compiler_params params;
1202
1203 auto protocol = fs::extract_protocol(fs::convert_to_protocol(key)).generic_string();
1204
1205 if(protocol != "engine")
1206 {
1207 auto lib_compiled_key = fs::resolve_protocol(script_system::get_lib_compiled_key("engine"));
1208
1209 params.references.emplace_back(lib_compiled_key.filename().string());
1210
1211 params.references_locations.emplace_back(lib_compiled_key.parent_path().string());
1212 }
1213
1214 auto assets = am.get_assets<script>(protocol);
1215 for(const auto& asset : assets)
1216 {
1217 if(asset)
1218 {
1219 params.files.emplace_back(fs::resolve_protocol(asset.id()).string());
1220 }
1221 }
1222
1223 temp /= script_system::get_lib_name(protocol);
1224
1225 auto temp_xml = temp;
1226 temp_xml.replace_extension(".xml");
1227 auto output_xml = output;
1228 output_xml.replace_extension(".xml");
1229
1230 auto temp_mdb = temp;
1231 temp_mdb.concat(".mdb");
1232 auto output_mdb = output;
1233 output_mdb.concat(".mdb");
1234
1235 std::string str_output = temp.string();
1236
1237 params.output_name = str_output;
1238 params.output_doc_name = temp_xml.string();
1239 if(params.files.empty())
1240 {
1241 fs::remove(output, err);
1242 fs::remove(output_mdb, err);
1243
1244 if(protocol == "engine")
1245 {
1246 return false;
1247 }
1248
1249 return result;
1250 }
1251
1252 params.debug = flags & script_library::compilation_flags::debug;
1253
1254 std::string error;
1255 // auto cmd = mono::create_compile_command_detailed(params);
1256 auto cmd = mono::create_compile_command_detailed_rsp(params, temp.string() + ".rsp");
1257
1258 // APPLOG_TRACE("Script Compile : \n {0} {1}", cmd.cmd, cmd.args);
1259
1260 fs::remove(temp, err);
1261 fs::remove(temp_mdb, err);
1262 fs::remove(temp_xml, err);
1263
1264 if(!run_process(cmd.cmd, cmd.args, true, error))
1265 {
1266 auto parsed_errors = parse_compilation_errors(error);
1267
1268 if(!parsed_errors.empty())
1269 {
1270 for(const auto& error : parsed_errors)
1271 {
1272 APPLOG_ERROR_LOC(error.file.c_str(), error.line, "", error.msg);
1273 }
1274 }
1275 else
1276 {
1277 APPLOG_ERROR("Failed compilation of {0} with error: {1}", output.string(), error);
1278 }
1279 result = false;
1280 }
1281 else
1282 {
1283 if(!params.debug)
1284 {
1285 fs::remove(output_mdb, err);
1286 }
1287
1288 fs::create_directories(output.parent_path(), err);
1289
1290 if(protocol != "engine")
1291 {
1292 auto parsed_warnings = parse_compilation_warnings(error);
1293
1294 for(const auto& warning : parsed_warnings)
1295 {
1296 APPLOG_WARNING_LOC(warning.file.c_str(), warning.line, "", warning.msg);
1297 }
1298 }
1299
1300 // mono::compile_cmd aot_cmd;
1301 // aot_cmd.cmd = "mono";
1302 // aot_cmd.args.emplace_back("--aot=full");
1303 // aot_cmd.args.emplace_back(temp.string());
1304 // error = {};
1305 // bool ok = run_process(aot_cmd.cmd, aot_cmd.args, true, error);
1306
1307 //APPLOG_INFO("Successful compilation of {0}", fs::replace(output, "temp-", "").string());
1308
1310 }
1311
1312 return result;
1313}
1314
1315template<>
1316auto compile<script>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1317{
1318 auto absolute_path = resolve_input_file(key);
1319
1320 fs::error_code er;
1321 asset_writer::atomic_copy_file(absolute_path, output, er);
1322
1323 if(er)
1324 {
1325 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1326 absolute_path.string(), output.filename().string(), er.message());
1327 return false;
1328 }
1329
1330 if(!write_manifest_file(absolute_path, output))
1331 {
1332 APPLOG_ERROR("Failed to write manifest for compiled script: {0}", output.string());
1333 return false;
1334 }
1335
1336
1337 return true;
1338}
1339
1340} // namespace unravel::asset_compiler
1341
Manages assets, including loading, unloading, and storage.
Base class for materials used in rendering.
Definition material.h:32
#define APPLOG_ERROR(...)
Definition logging.h:20
#define APPLOG_INFO(...)
Definition logging.h:18
#define APPLOG_WARNING_LOC(FILE_LOC, LINE_LOC, FUNC_LOC,...)
Definition logging.h:36
#define APPLOG_ERROR_LOC(FILE_LOC, LINE_LOC, FUNC_LOC,...)
Definition logging.h:38
auto get_data_directory(const std::string &prefix={}) -> std::string
auto get_meta_directory(const std::string &prefix={}) -> std::string
Definition cache.hpp:11
path extract_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
path replace(const path &_path, const path &_sequence, const path &_new_sequence)
Replacing any occurences of the specified path sequence with another.
path convert_to_protocol(const path &_path)
Oposite of the resolve_protocol this function tries to convert to protocol path from an absolute one.
auto to_string(texture_format fmt) -> std::string
Definition format.cpp:378
bgfx::TextureFormat::Enum texture_format
Definition format.h:10
auto get_format_info(texture_format fmt) -> format_details
Definition format.cpp:296
auto get_renderer_based_on_filename_extension(const std::string &_type) -> renderer_type
auto get_max_blend_transforms() -> uint32_t
bgfx::RendererType::Enum renderer_type
Definition graphics.h:21
auto call(const std::vector< std::string > &args_array) -> call_result
auto get_manifest_path(const fs::path &compiled_asset_path) -> fs::path
Generate manifest file path from compiled asset path.
auto compile< style_sheet >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto compile< script >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto compile< material >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto read_importer< animation_clip >(asset_manager &am, const fs::path &key) -> std::shared_ptr< asset_importer_meta >
auto compile< gfx::texture >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto compile< gfx::shader >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto parse_compilation_warnings(const std::string &log) -> std::vector< script_compilation_entry >
auto compile< audio_clip >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto compile< font >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto save_manifest(const fs::path &manifest_path, const asset_manifest &manifest) -> bool
Save manifest to file.
auto read_importer< audio_clip >(asset_manager &am, const fs::path &key) -> std::shared_ptr< asset_importer_meta >
auto compile< prefab >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto parse_compilation_errors(const std::string &log) -> std::vector< script_compilation_entry >
auto compile< animation_clip >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto read_importer< mesh >(asset_manager &am, const fs::path &key) -> std::shared_ptr< asset_importer_meta >
auto read_importer< gfx::texture >(asset_manager &am, const fs::path &key) -> std::shared_ptr< asset_importer_meta >
auto compile< ui_tree >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto compile< physics_material >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto compile< mesh >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto compile< script_library >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto compile< scene_prefab >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto atomic_copy_file(const fs::path &src, const fs::path &dst, fs::error_code &ec) noexcept -> bool
void atomic_write_file(const fs::path &dst, const std::function< void(const fs::path &)> &callback, fs::error_code &ec) noexcept
auto load_mesh_data_from_file(asset_manager &am, const fs::path &path, const mesh_importer_meta &import_meta, mesh::load_data &load_data, std::vector< animation_clip > &animations, std::vector< imported_material > &materials, std::vector< imported_texture > &textures) -> bool
void save_to_file_bin(const std::string &absolute_path, const animation_clip &obj)
void load_from_file(const std::string &absolute_path, animation_clip &obj)
void save_to_file(const std::string &absolute_path, const animation_clip &obj)
Struct representing an animation.
Definition animation.h:99
root_motion_params root_motion
Definition animation.h:111
Manifest data for compiled assets.
std::string file
std::string msg
int line
Metadata for an asset, including its UUID and type.
std::shared_ptr< asset_importer_meta > importer
Importer meta.
hpp::uuid uid
Unique identifier for the asset.
static auto context() -> rtti::context &
Definition engine.cpp:116
Struct used for mesh construction.
Definition mesh.h:388
std::vector< uint8_t > vertex_data
Total number of vertices.
Definition mesh.h:392
static auto get_lib_name(const std::string &protocol) -> std::string
static auto get_lib_compiled_key(const std::string &protocol) -> std::string
static void copy_compiled_lib(const fs::path &from, const fs::path &to)
texture_importer_meta::compression_quality default_compression
Definition settings.h:33
texture_importer_meta::texture_size default_max_size
Definition settings.h:32
struct unravel::settings::asset_settings::texture_importer_settings texture
struct unravel::settings::asset_settings assets
struct unravel::texture_importer_meta::quality_meta quality