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