Unravel Engine C++ Reference
Loading...
Searching...
No Matches
asset_compiler.cpp
Go to the documentation of this file.
1#include "asset_compiler.h"
3#include "asset_writer.h"
4#include "asset_manifest.h"
5#include "bimg/bimg.h"
7
8#include <bx/error.h>
9#include <bx/process.h>
10#include <bx/string.h>
11
12#include <graphics/shader.h>
13#include <graphics/texture.h>
15#include <logging/logging.h>
16#include <uuid/uuid.h>
17
20
22#include <engine/engine.h>
35
37
40#include <fstream>
41#include <dotnetpp/dotnetpp.h>
42#include <regex>
44
46
48{
49
50namespace
51{
52
53auto resolve_path(const std::string& key) -> fs::path
54{
55 return fs::absolute(fs::resolve_protocol(key));
56}
57
58auto resolve_input_file(const fs::path& key) -> fs::path
59{
60 fs::path absolute_path = fs::convert_to_protocol(key);
62 if(absolute_path.extension() == ".meta")
63 {
64 absolute_path.replace_extension();
65 }
66 return absolute_path;
67}
68
69auto escape_str(const std::string& str) -> std::string
70{
71 return "\"" + str + "\"";
72}
73
74auto run_process(const std::string& process,
75 const std::vector<std::string>& args_array,
76 bool check_retcode,
77 std::string& err) -> bool
78{
79 auto result = subprocess::call(process, args_array);
80 err = result.out_output;
81
82 if(!result.err_output.empty())
83 {
84 if(!err.empty())
85 {
86 err += "\n";
87 }
88
89 err += result.err_output;
90 }
91
92 if(err.find("error") != std::string::npos)
93 {
94 return false;
95 }
96
97 return result.retcode == 0;
98}
99
100struct input_texture_info
101{
102 gfx::texture_format format{gfx::texture_format::RGBA8};
103 uint32_t width{};
104 uint32_t height{};
105 bool fits_max_size{true};
106};
107
108auto texture_size_to_pixel_limit(texture_importer_meta::texture_size size) -> uint32_t
109{
110 switch(size)
111 {
113 return 32;
115 return 64;
117 return 128;
119 return 256;
121 return 512;
123 return 1024;
125 return 2048;
127 return 4096;
129 return 8192;
131 return 16384;
133 default:
134 return 0;
135 }
136}
137
138auto append_texture_max_size_args(std::vector<std::string>& args, texture_importer_meta::texture_size max_size) -> void
139{
140 const uint32_t limit = texture_size_to_pixel_limit(max_size);
141 if(limit > 0)
142 {
143 args.emplace_back("--max");
144 args.emplace_back(std::to_string(limit));
145 }
146}
147
148auto fill_input_texture_info_from_container(const bimg::ImageContainer& info, input_texture_info& out) -> void
149{
150 out.format = static_cast<gfx::texture_format>(info.m_format);
151 out.width = info.m_width;
152 out.height = info.m_height;
153}
154
155auto get_input_texture_info(const fs::path& input_path, texture_importer_meta::texture_size max_size) -> input_texture_info
156{
157 input_texture_info result{};
158 const bx::FilePath file_path(input_path.string().c_str());
159
160 bimg::ImageContainer header{};
161 if(imageParseInfo(file_path, header))
162 {
163 fill_input_texture_info_from_container(header, result);
164 }
165 else
166 {
167 bimg::ImageContainer* image = imageLoad(file_path, bgfx::TextureFormat::Count);
168 if(image == nullptr)
169 {
170 return result;
171 }
172
173 fill_input_texture_info_from_container(*image, result);
174 bimg::imageFree(image);
175 }
176
177 const uint32_t limit = texture_size_to_pixel_limit(max_size);
178 if(limit > 0)
179 {
180 const uint32_t largest_dimension = std::max(result.width, result.height);
181 result.fits_max_size = largest_dimension <= limit;
182 }
183
184 return result;
185}
186
187// auto run_process(const std::string& process, const std::vector<std::string>& args_array, bool check_retcode, std::string& err) -> bool
188// {
189// auto now = std::chrono::high_resolution_clock::now();
190
191// std::string args;
192// size_t i = 0;
193// for(const auto& arg : args_array)
194// {
195// if(arg.front() == '-')
196// {
197// args += arg;
198// }
199// else
200// {
201// args += escape_str(arg);
202// }
203
204// if(i++ != args_array.size() - 1)
205// args += " ";
206// }
207
208// bx::Error error;
209// bx::ProcessReader process_reader;
210
211// #if UNRAVEL_PLATFORM_WINDOWS
212// process_reader.open((process + " " + args).c_str(), "", &error);
213// #else
214// process_reader.open(process.c_str(), args.c_str(), &error);
215// #endif
216
217// bool ok = true;
218// if(!error.isOk())
219// {
220// err = std::string(error.getMessage().getCPtr());
221// ok = false;
222// }
223// else
224// {
225// std::array<char, 2048 * 32> buffer;
226// buffer.fill(0);
227// int32_t sz = process_reader.read(buffer.data(), static_cast<std::int32_t>(buffer.size()), &error);
228
229// process_reader.close();
230// int32_t result = process_reader.getExitCode();
231
232
233// if(0 != result)
234// {
235// err = std::string(error.getMessage().getCPtr());
236// if(sz > 0)
237// {
238// err += " " + std::string(buffer.data());
239// }
240// ok = false;
241// }
242
243// }
244
245
246// auto end = std::chrono::high_resolution_clock::now();
247// auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
248// APPLOG_TRACE("Process {} took {} ", process, duration);
249// return ok;
250// }
251
252bool copy_compiled_file(const fs::path& from, const fs::path& to)
253{
254 fs::error_code err;
255 asset_writer::atomic_copy_file(from, to, err);
256
257 if(err)
258 {
259 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}", from.string(), to.filename().string(), err.message());
260 }
261
262 return !err;
263}
264
265auto select_compressed_format(gfx::texture_format input_format,
266 const fs::path& extension,
268{
270 {
271 return input_format;
272 }
273
274 if(input_format == gfx::texture_format::BC1)
275 {
276 return gfx::texture_format::BC3;
277 }
278
279 if(gfx::is_compressed_format(input_format))
280 {
281 return input_format;
282 }
283
284 auto info = gfx::get_format_info(input_format);
285
286 if(extension == ".hdr" || extension == ".exr")
287 {
288 info.is_hdr = true;
289 }
290
291 // 1) HDR? Use BC6H for color data, ignoring alpha (HDR with alpha is non-trivial).
292 if(info.is_hdr)
293 {
294 // BC6H: color (RGB) 16F
295 // No standard BC format for HDR alpha in the block-compression range.
296 return gfx::texture_format::BC6H;
297 }
298
299 // 2) Single channel => BC4
300 // e.g., for grayscale height map or single-channel mask
301 if(info.num_channels == 1)
302 {
303 return gfx::texture_format::BC4;
304 }
305
306 // 3) Two channel => BC5
307 // e.g., typical for 2D vector data, normal map XY
308 if(info.num_channels == 2)
309 {
310 return gfx::texture_format::BC5;
311 }
312
313 // 4) If we reach here, we have 3 or 4 channels in LDR.
314
315 // 4a) No alpha needed => choose BC1 or BC7, etc.
316 if(!info.has_alpha_channel)
317 {
318 switch(quality)
319 {
321 // BC1 is cheap and has no alpha
322 return gfx::texture_format::BC1;
324 // BC1 is standard for color w/out alpha
325 return gfx::texture_format::BC1;
327 // BC7 is higher quality for color, also supports alpha but not needed here.
328 // It is also really slow for encoding so don't use it for now.
329 return gfx::texture_format::BC1;
330 default:
331 break;
332 }
333 // fallback
334 return gfx::texture_format::BC1;
335 }
336 else
337 {
338 // 4b) We do have alpha => choose BC2, BC3, or BC7.
339 // BC2 (DXT3) is old and rarely used except for sharp alpha transitions.
340 // BC3 (DXT5) is the typical solution for alpha textures if BC7 is not an option.
341 // BC7 is better (but bigger decode cost).
342 switch(quality)
343 {
345 return gfx::texture_format::BC3;
347 return gfx::texture_format::BC3; // DXT5
349 // BC7 is best BC for RGBA
350 // It is also really slow for encoding so don't use it for now.
351 return gfx::texture_format::BC3;
352 default:
353 break;
354 }
355 // fallback
356 return gfx::texture_format::BC3;
357 }
358
359 return input_format;
360}
361
362auto bake_normal_map_input_if_needed(const fs::path& input_path,
363 const texture_importer_meta& importer,
364 fs::path& temp_baked_path) -> fs::path
365{
366 temp_baked_path.clear();
367
368 if(!importer.invert_normal_y)
369 {
370 return input_path;
371 }
372
373 bimg::ImageContainer* image = imageLoad(bx::FilePath(input_path.string().c_str()));
374 if(nullptr == image)
375 {
376 APPLOG_ERROR("Failed to load texture for normal Y bake: {0}", input_path.string());
377 return input_path;
378 }
379
381 {
382 bimg::imageFree(image);
383 APPLOG_ERROR("Failed to flip normal map Y for: {0}", input_path.string());
384 return input_path;
385 }
386
388 {
389 bimg::imageFree(image);
390 APPLOG_ERROR("Failed to prepare baked normal map PNG for: {0}", input_path.string());
391 return input_path;
392 }
393
394 fs::error_code err;
395 // Always feed texturec a lossless RGBA8 PNG (avoids broken BC re-encode and zero-alpha PNG previews).
396 temp_baked_path = fs::temp_directory_path(err) / (input_path.stem().string() + "_normal_y.png");
397 if(err || temp_baked_path.empty())
398 {
399 bimg::imageFree(image);
400 APPLOG_ERROR("Failed to resolve temp path for normal Y bake: {0}", input_path.string());
401 return input_path;
402 }
403
404 if(!imageSave(temp_baked_path.string().c_str(), image))
405 {
406 bimg::imageFree(image);
407 fs::remove(temp_baked_path, err);
408 temp_baked_path.clear();
409 APPLOG_ERROR("Failed to write baked normal map: {0}", input_path.string());
410 return input_path;
411 }
412
413 bimg::imageFree(image);
414 APPLOG_INFO("Baked invert normal Y for {0} -> {1}", input_path.filename().string(), temp_baked_path.filename().string());
415 return temp_baked_path;
416}
417
418auto compile_texture_to_file(const fs::path& input_path,
419 const fs::path& output_path,
420 const texture_importer_meta& importer,
421 const std::string& protocol) -> bool
422{
423 fs::path temp_baked_path;
424 const fs::path compile_input = bake_normal_map_input_if_needed(input_path, importer, temp_baked_path);
425 const bool using_temp_input = !temp_baked_path.empty();
426
427 std::string str_input = compile_input.string();
428 std::string str_output = output_path.string();
429
430 bool try_compress = protocol == "app";
431
432 auto quality = importer.quality;
434 {
435 auto& ctx = engine::context();
436 if(ctx.has<settings>())
437 {
438 auto& ss = ctx.get<settings>();
439 quality.compression = ss.assets.texture.default_compression;
440 }
441 }
442
444 {
445 auto& ctx = engine::context();
446 if(ctx.has<settings>())
447 {
448 auto& ss = ctx.get<settings>();
449 quality.max_size = ss.assets.texture.default_max_size;
450 }
451 }
452
453 // If still default, set to normal quality
455 {
457 }
458
459 // If still default, set to 2048
461 {
463 }
464
465 const auto input_info = get_input_texture_info(compile_input, quality.max_size);
466 auto format = select_compressed_format(input_info.format, compile_input.extension(), quality.compression);
467
468 const bool needs_format_conversion = input_info.format != format;
469 const bool needs_downscale = !input_info.fits_max_size;
470
471 if(needs_format_conversion || needs_downscale)
472 {
473 if(needs_downscale && !needs_format_conversion)
474 {
475 APPLOG_INFO("Downscaling {0} ({1}x{2}) to fit max size {3}",
476 compile_input.filename().string(),
477 input_info.width,
478 input_info.height,
479 texture_size_to_pixel_limit(quality.max_size));
480 }
481
482 std::vector<std::string> args_array = {
483 "-f",
484 str_input,
485 "-o",
486 str_output,
487 "--as",
488 "dds",
489 };
490
491 if(try_compress)
492 {
493 args_array.emplace_back("-t");
494 args_array.emplace_back(gfx::to_string(format));
495
496 if(format == gfx::texture_format::BC7 || format == gfx::texture_format::BC6H)
497 {
498 APPLOG_INFO("Compressing to {0}. May take a while.", gfx::to_string(format));
499
500 args_array.emplace_back("-q");
501 args_array.emplace_back("fastest");
502 }
503 else if(needs_format_conversion
505 {
506 args_array.emplace_back("-q");
507 args_array.emplace_back("highest");
508 }
509 }
510
511 if(importer.generate_mipmaps)
512 {
513 args_array.emplace_back("-m");
514 }
515
516 append_texture_max_size_args(args_array, quality.max_size);
517
518 switch(importer.type)
519 {
521 {
522 args_array.emplace_back("--equirect");
523 break;
524 }
525
527 {
528 args_array.emplace_back("--normalmap");
529 break;
530 }
531
532 default:
533 break;
534 }
535
536 std::string error;
537
538 // Create an empty file at the output location so the process can write to it
539 {
540 std::ofstream output_file(str_output);
541 (void)output_file;
542 }
543
544 auto texturec = fs::resolve_protocol("binary:/texturec");
545
546 // Run the texture compiler directly to the temporary output location
547 bool compiled = run_process(texturec.string(), args_array, false, error);
548 if(!compiled)
549 {
550 APPLOG_ERROR("Failed compilation of {0} with error: {1}", str_input, error);
551 fs::remove(str_output);
552 return false;
553 }
554 }
555 else
556 {
557 copy_compiled_file(compile_input, output_path);
558 }
559
560 if(using_temp_input)
561 {
562 fs::error_code remove_err;
563 fs::remove(temp_baked_path, remove_err);
564 }
565
566
567 return true;
568}
569
570auto compile_shader_to_file(const fs::path& input_path,
571 const fs::path& output_path,
572 gfx::renderer_type renderer) -> bool
573{
574
575 std::string str_input = input_path.string();
576 std::string str_output = output_path.string();
577
578 std::string file = input_path.stem().string();
579 fs::path dir = input_path.parent_path();
580
581 fs::path include = fs::resolve_protocol("engine:/data/shaders");
582 std::string str_include = include.string();
583
584 fs::path varying = dir / (file + ".io");
585
586 fs::error_code err;
587 if(!fs::exists(varying, err))
588 {
589 varying = dir / "varying.def.io";
590 }
591 if(!fs::exists(varying, err))
592 {
593 varying = dir / "varying.def.sc";
594 }
595
596 std::string str_varying = varying.string();
597
598 std::string str_platform;
599 std::string str_profile;
600 std::string str_type;
601
602 bool optimize = true;
603// #if UNRAVEL_DEBUG
604// optimize = false;
605// #endif
606
607 std::string str_opt = optimize ? "3" : "0";
608
609 bool vs = hpp::string_view(file).starts_with("vs_");
610 bool fs = hpp::string_view(file).starts_with("fs_");
611 bool cs = hpp::string_view(file).starts_with("cs_");
612
613 // Vertex/fragment shaders that reference compute-style read/write buffers
614 // (BUFFER_RO / BUFFER_RW / BUFFER_WO) need SSBO support, which requires a
615 // higher GLSL profile. Detect that up-front by scanning the shader source
616 // so the correct OpenGL profile is selected below.
617 bool needs_compute_buffers = false;
618 if(vs || fs)
619 {
620 std::ifstream shader_file(str_input);
621 if(shader_file.is_open())
622 {
623 std::stringstream buffer;
624 buffer << shader_file.rdbuf();
625 const std::string source = buffer.str();
626 needs_compute_buffers = source.find("BUFFER_RO(") != std::string::npos
627 || source.find("BUFFER_RW(") != std::string::npos
628 || source.find("BUFFER_WO(") != std::string::npos;
629 }
630 }
631
632 if(renderer == gfx::renderer_type::Vulkan)
633 {
634 str_platform = "windows";
635 str_profile = "spirv";
636 }
637
638 if(renderer == gfx::renderer_type::Direct3D11 || renderer == gfx::renderer_type::Direct3D12)
639 {
640 str_platform = "windows";
641
642 if(vs || fs)
643 {
644 str_profile = "s_5_0";
645 if(renderer == gfx::renderer_type::Direct3D12)
646 {
647 str_profile = "s_6_0";
648 }
649 }
650 else if(cs)
651 {
652 str_profile = "s_5_0";
653 if(renderer == gfx::renderer_type::Direct3D12)
654 {
655 str_profile = "s_6_0";
656 }
657 str_opt = optimize ? "1" : "0";
658 }
659 }
660 else if(renderer == gfx::renderer_type::OpenGLES)
661 {
662 str_platform = "android";
663 str_profile = "100_es";
664 }
665 else if(renderer == gfx::renderer_type::OpenGL)
666 {
667 str_platform = "linux";
668
669 if(vs || fs)
670 {
671 // GLSL 4.30 is needed to expose SSBOs (compute-style buffers) in
672 // vertex/fragment stages. Otherwise stick with the more portable 1.40.
673 str_profile = needs_compute_buffers ? "430" : "140";
674 }
675 else if(cs)
676 {
677 str_profile = "430";
678 }
679 }
680 else if(renderer == gfx::renderer_type::Metal)
681 {
682 str_platform = "osx";
683 str_profile = "metal";
684 }
685
686 if(vs)
687 str_type = "vertex";
688 else if(fs)
689 str_type = "fragment";
690 else if(cs)
691 str_type = "compute";
692 else
693 str_type = "unknown";
694
695 std::vector<std::string> args_array = {
696 "-f",
697 str_input,
698 "-o",
699 str_output,
700 "-i",
701 str_include,
702 "--varyingdef",
703 str_varying,
704 "--type",
705 str_type,
706 "--define",
707 "BGFX_CONFIG_MAX_BONES=" + std::to_string(gfx::get_max_blend_transforms())
708 // "--Werror"
709 };
710
711 if(!str_platform.empty())
712 {
713 args_array.emplace_back("--platform");
714 args_array.emplace_back(str_platform);
715 }
716
717 if(!str_profile.empty())
718 {
719 args_array.emplace_back("-p");
720 args_array.emplace_back(str_profile);
721 }
722
723 if(!str_opt.empty())
724 {
725 args_array.emplace_back("-O");
726 args_array.emplace_back(str_opt);
727 }
728
729 std::string error;
730
731 // Create an empty file at the output location
732 {
733 std::ofstream output_file(str_output);
734 (void)output_file;
735 }
736
737 auto shaderc = fs::resolve_protocol("binary:/shaderc");
738
739 if(!run_process(shaderc.string(), args_array, true, error))
740 {
741 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}", str_input, output_path.filename().string(), error);
742 fs::remove(str_output);
743 return false;
744 }
745 return true;
746}
747
748template<typename T>
749auto write_manifest_file(const fs::path& input_path, const fs::path& output_path) -> bool
750{
751 APP_SCOPE_PERF("Write Manifest File");
752 std::vector<fs::path> deps;
753 resolve_dependencies<T>(input_path, deps);
754
755 asset_manifest manifest(input_path);
756 manifest.compute_source_fingerprint(input_path, deps);
757 auto manifest_path = get_manifest_path(output_path);
758
759 bool ok = false;
760
761 int attempts = 0;
762 while(!ok && attempts < 3)
763 {
764 fs::error_code err;
765 bool success = true;
766 asset_writer::atomic_write_file(manifest_path, [&](const fs::path& temp_manifest_path)
767 {
768 success = save_manifest(temp_manifest_path, manifest);
769 }, err);
770 ok = !err && success;
771 attempts++;
772 }
773
774 return ok;
775}
776
777auto write_minified_file(const fs::path& input_path, const fs::path& output_path) -> bool
778{
779
780#if SER20_ASSOCIATIVE_ARCHIVE == SER20_ASSOCIATIVE_ARCHIVE_SIMDJSON
781
782 std::string str_input = input_path.string();
783 simdjson::dom::parser parser;
784 auto doc = parser.load(str_input);
785 if(doc.error())
786 {
787 APPLOG_ERROR("Failed to parse {0}: {1}", input_path.string(), simdjson::error_message(doc.error()));
788 return false;
789 }
790
791 auto minified = simdjson::minify(doc);
792
793
794 fs::error_code err;
795 bool success = true;
796 asset_writer::atomic_write_file(output_path, [&](const fs::path& temp_manifest_path)
797 {
798 std::ofstream file(temp_manifest_path);
799 if(file.is_open())
800 {
801 file << minified;
802 file.close();
803 }
804 }, err);
805 return !err;
806#else
807
808 copy_compiled_file(input_path, output_path);
809
810 return true;
811#endif
812
813}
814
815} // namespace
816
817template<>
818auto compile<gfx::shader>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
819{
820 auto absolute_path = resolve_input_file(key);
821 std::string str_input = absolute_path.string();
822
823 auto extension = output.extension();
825
826 fs::error_code err;
827 // Use atomic_write_file to handle the temporary file creation and atomic rename
828 asset_writer::atomic_write_file(output, [&](const fs::path& temp_output) -> void
829 {
830 compile_shader_to_file(
831 absolute_path,
832 temp_output,
834 );
835 }, err);
836
837 if(err)
838 {
839 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
840 str_input, output.filename().string(), err.message());
841 return false;
842 }
843
844 if(!write_manifest_file<gfx::shader>(absolute_path, output))
845 {
846 APPLOG_ERROR("Failed to write manifest for compiled shader: {0}", output.string());
847 return false;
848 }
849
850 return true;
851}
852
853template<>
854auto read_importer<gfx::texture>(asset_manager& am, const fs::path& key) -> std::shared_ptr<asset_importer_meta>
855{
856 auto absolute = fs::resolve_protocol(key).string();
857 asset_meta meta;
858 if(load_from_file(absolute, meta))
859 {
860 if(!meta.importer)
861 {
862 meta.importer = std::make_shared<texture_importer_meta>();
863
864 meta.uid = am.add_asset_info_for_path(resolve_input_file(key), meta, true);
865
866 fs::error_code err;
867 asset_writer::atomic_write_file(absolute, [&](const fs::path& temp) -> void
868 {
869 save_to_file(temp.string(), meta);
870 }, err);
871
872 return nullptr;
873 }
874 }
875
876 return meta.importer;
877}
878
879template<>
880auto compile<gfx::texture>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
881{
882 APP_SCOPE_PERF("Compile Texture");
883 auto base_importer = read_importer<gfx::texture>(am, key);
884
885 if(!base_importer)
886 {
887 return true;
888 }
889 auto importer = std::static_pointer_cast<texture_importer_meta>(base_importer);
890
891 auto protocol = fs::extract_protocol(fs::convert_to_protocol(key)).generic_string();
892 auto absolute_path = resolve_input_file(key);
893 std::string str_input = absolute_path.string();
894
895 fs::error_code err;
896
897 asset_writer::atomic_write_file(output, [&](const fs::path& temp_output) -> void
898 {
899 compile_texture_to_file(
900 absolute_path,
901 temp_output,
902 *importer,
903 protocol
904 );
905 }, err);
906
907 if(err)
908 {
909 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
910 str_input, output.filename().string(), err.message());
911 return false;
912 }
913
914 if(!write_manifest_file<gfx::texture>(absolute_path, output))
915 {
916 APPLOG_ERROR("Failed to write manifest for compiled texture: {0}", output.string());
917 return false;
918 }
919
920 return true;
921}
922
923template<>
924auto compile<material>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
925{
926 APP_SCOPE_PERF("Compile Material");
927 auto absolute_path = resolve_input_file(key);
928
929 std::string str_input = absolute_path.string();
930
931 fs::error_code err;
932
933 std::shared_ptr<material> material;
934 {
935 load_from_file(str_input, material);
936
937 asset_writer::atomic_write_file(output, [&](const fs::path& temp) -> void
938 {
939 save_to_file_bin(temp.string(), material);
940 }, err);
941 }
942
943 if(err)
944 {
945 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
946 str_input, output.filename().string(), err.message());
947 return false;
948 }
949
950 if(!write_manifest_file<unravel::material>(absolute_path, output))
951 {
952 APPLOG_ERROR("Failed to write manifest for compiled material: {0}", output.string());
953 return false;
954 }
955
956 return true;
957}
958
959template<>
960auto read_importer<mesh>(asset_manager& am, const fs::path& key) -> std::shared_ptr<asset_importer_meta>
961{
962 APP_SCOPE_PERF("Read Mesh Importer");
963 auto absolute = fs::resolve_protocol(key).string();
964 asset_meta meta;
965 if(load_from_file(absolute, meta))
966 {
967 if(!meta.importer)
968 {
969 meta.importer = std::make_shared<mesh_importer_meta>();
970
971 meta.uid = am.add_asset_info_for_path(resolve_input_file(key), meta, true);
972
973 fs::error_code err;
974 asset_writer::atomic_write_file(absolute, [&](const fs::path& temp) -> void
975 {
976 save_to_file(temp.string(), meta);
977 }, err);
978
979 return nullptr;
980 }
981 }
982
983 return meta.importer;
984}
985
986template<>
987auto compile<mesh>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
988{
989 APP_SCOPE_PERF("Compile Mesh");
990 // Try to import first.
991 auto base_importer = read_importer<mesh>(am, key);
992
993 if(!base_importer)
994 {
995 return true;
996 }
997
998 auto importer = std::static_pointer_cast<mesh_importer_meta>(base_importer);
999
1000 auto absolute_path = resolve_input_file(key);
1001
1002 std::string str_input = absolute_path.string();
1003
1004 fs::error_code err;
1005
1006 fs::path file = absolute_path.stem();
1007 fs::path dir = absolute_path.parent_path();
1008
1009 mesh::load_data data;
1010 std::vector<animation_clip> animations;
1011 std::vector<importer::imported_material> materials;
1012 std::vector<importer::imported_texture> textures;
1013
1014 if(!importer::load_mesh_data_from_file(am, absolute_path, *importer, data, animations, materials, textures))
1015 {
1016 APPLOG_ERROR("Failed compilation of {0}", str_input);
1017 return false;
1018 }
1019 if(!data.vertex_data.empty())
1020 {
1021 // IMPORTANT:
1022 // For skinned meshes, the skin binding step can duplicate vertices and rewrite triangle indices
1023 // to ensure a consistent bone palette per submesh. LODs must be generated AFTER this rewrite,
1024 // otherwise the stored LOD index buffers will reference the wrong vertices at runtime.
1025 if(data.skin_data.has_bones())
1026 {
1027 APP_SCOPE_PERF("Apply Skin to Load Data");
1029 {
1030 APPLOG_ERROR("Failed to apply skinning data before generating LODs for {0}", str_input);
1031 return false;
1032 }
1033 }
1034
1035 // Generate LODs offline during compilation (no GPU buffers created)
1036 if(importer->model.generate_lods)
1037 {
1038 // Use custom LOD configs if provided, otherwise use defaults
1039 auto lod_configs = mesh::generate_default_lod_configs(data, importer->model.lod_target_error);
1040
1041
1042 if(!lod_configs.empty())
1043 {
1044 APP_SCOPE_PERF("Generate LODs for Load Data");
1045 mesh::generate_lods_for_load_data(data, lod_configs);
1046 }
1047 }
1048
1049 // Save materials and register their UIDs before writing the mesh binary
1050 data.default_material_uids.reserve(materials.size());
1051
1052 APPLOG_INFO("Adding default material UIDs for {0}", str_input);
1053
1054 for(const auto& material : materials)
1055 {
1056 fs::path mat_output;
1057
1058 if(material.name.empty())
1059 {
1060 mat_output = (dir / file).string() + ".mat";
1061 }
1062 else
1063 {
1064 mat_output = dir / (material.name + ".mat");
1065 }
1066
1067 auto uid = am.add_asset_for_path(mat_output, false);
1068 data.default_material_uids.push_back(uid);
1069
1070 asset_writer::atomic_write_file(mat_output, [&](const fs::path& temp) -> void
1071 {
1072 save_to_file(temp.string(), material.mat);
1073 }, err);
1074
1075 }
1076
1077 asset_writer::atomic_write_file(output, [&](const fs::path& temp) -> void
1078 {
1079 save_to_file_bin(temp.string(), data);
1080 }, err);
1081 }
1082
1083 {
1084 APP_SCOPE_PERF("Write Animations");
1085 for(const auto& animation : animations)
1086 {
1087 fs::path anim_output;
1088 if(animation.name.empty())
1089 {
1090 anim_output = (dir / file).string() + ".anim";
1091 }
1092 else
1093 {
1094 anim_output = dir / (animation.name + ".anim");
1095 }
1096
1097 asset_writer::atomic_write_file(anim_output, [&](const fs::path& temp) -> void
1098 {
1099 save_to_file(temp.string(), animation);
1100 }, err);
1101 }
1102 }
1103
1104 if(err)
1105 {
1106 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1107 str_input, output.filename().string(), err.message());
1108 return false;
1109 }
1110
1111 if(!write_manifest_file<mesh>(absolute_path, output))
1112 {
1113 APPLOG_ERROR("Failed to write manifest for compiled mesh: {0}", output.string());
1114 return false;
1115 }
1116
1117 return true;
1118}
1119
1120template<>
1121auto read_importer<animation_clip>(asset_manager& am, const fs::path& key) -> std::shared_ptr<asset_importer_meta>
1122{
1123 APP_SCOPE_PERF("Read Animation Clip Importer");
1124 auto absolute = fs::resolve_protocol(key).string();
1125 asset_meta meta;
1126 if(load_from_file(absolute, meta))
1127 {
1128 if(!meta.importer)
1129 {
1130 meta.importer = std::make_shared<animation_importer_meta>();
1131
1132 meta.uid = am.add_asset_info_for_path(resolve_input_file(key), meta, true);
1133
1134 fs::error_code err;
1135 asset_writer::atomic_write_file(absolute, [&](const fs::path& temp) -> void
1136 {
1137 save_to_file(temp.string(), meta);
1138 }, err);
1139
1140 return nullptr;
1141 }
1142 }
1143
1144 return meta.importer;
1145}
1146
1147template<>
1148auto compile<animation_clip>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1149{
1150 APP_SCOPE_PERF("Compile Animation Clip");
1151 // Try to import first.
1152 auto base_importer = read_importer<animation_clip>(am, key);
1153
1154 if(!base_importer)
1155 {
1156 return true;
1157 }
1158
1159 auto importer = std::static_pointer_cast<animation_importer_meta>(base_importer);
1160
1161 auto absolute_path = resolve_input_file(key);
1162
1163 std::string str_input = absolute_path.string();
1164
1165 fs::error_code err;
1166
1167 animation_clip anim;
1168 {
1169 load_from_file(str_input, anim);
1170
1171 anim.root_motion.keep_position_y = importer->root_motion.keep_position_y;
1172 anim.root_motion.keep_position_xz = importer->root_motion.keep_position_xz;
1173 anim.root_motion.keep_rotation = importer->root_motion.keep_rotation;
1174 anim.root_motion.keep_in_place = importer->root_motion.keep_in_place;
1175
1176 fs::error_code err;
1177 asset_writer::atomic_write_file(output, [&](const fs::path& temp) -> void
1178 {
1179 save_to_file_bin(temp.string(), anim);
1180 }, err);
1181 }
1182
1183 if(err)
1184 {
1185 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1186 str_input, output.filename().string(), err.message());
1187 return false;
1188 }
1189
1190 if(!write_manifest_file<animation_clip>(absolute_path, output))
1191 {
1192 APPLOG_ERROR("Failed to write manifest for compiled animation: {0}", output.string());
1193 return false;
1194 }
1195
1196 return true;
1197}
1198
1199template<>
1200auto compile<font>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1201{
1202 APP_SCOPE_PERF("Compile Font");
1203 auto absolute_path = resolve_input_file(key);
1204
1205 copy_compiled_file(absolute_path, output);
1206
1207 if(!write_manifest_file<font>(absolute_path, output))
1208 {
1209 APPLOG_ERROR("Failed to write manifest for compiled font: {0}", output.string());
1210 return false;
1211 }
1212
1213 return true;
1214}
1215
1216template<>
1217auto compile<prefab>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1218{
1219 APP_SCOPE_PERF("Compile Prefab");
1220 auto absolute_path = resolve_input_file(key);
1221
1222 if(!write_minified_file(absolute_path, output))
1223 {
1224 APPLOG_ERROR("Failed to write minified file for compiled prefab: {0}", output.string());
1225 return false;
1226 }
1227
1228 if(!write_manifest_file<prefab>(absolute_path, output))
1229 {
1230 APPLOG_ERROR("Failed to write manifest for compiled prefab: {0}", output.string());
1231 return false;
1232 }
1233
1234 return true;
1235}
1236
1237template<>
1238auto compile<scene_prefab>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1239{
1240 APP_SCOPE_PERF("Compile Scene Prefab");
1241 auto absolute_path = resolve_input_file(key);
1242
1243 if(!write_minified_file(absolute_path, output))
1244 {
1245 APPLOG_ERROR("Failed to write minified file for compiled scene: {0}", output.string());
1246 return false;
1247 }
1248
1249 if(!write_manifest_file<scene_prefab>(absolute_path, output))
1250 {
1251 APPLOG_ERROR("Failed to write manifest for compiled scene_prefab: {0}", output.string());
1252 return false;
1253 }
1254
1255 return true;
1256}
1257
1258template<>
1259auto compile<physics_material>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1260{
1261 APP_SCOPE_PERF("Compile Physics Material");
1262 auto absolute_path = resolve_input_file(key);
1263
1264 std::string str_input = absolute_path.string();
1265
1266 fs::error_code err;
1267
1268 auto material = std::make_shared<physics_material>();
1269 {
1270 load_from_file(str_input, material);
1271
1272 asset_writer::atomic_write_file(output, [&](const fs::path& temp) -> void
1273 {
1274 save_to_file_bin(temp.string(), material);
1275 }, err);
1276 }
1277
1278 if(err)
1279 {
1280 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1281 str_input, output.filename().string(), err.message());
1282 return false;
1283 }
1284
1285 if(!write_manifest_file<physics_material>(absolute_path, output))
1286 {
1287 APPLOG_ERROR("Failed to write manifest for compiled physics_material: {0}", output.string());
1288 return false;
1289 }
1290
1291 return true;
1292}
1293
1294template<>
1295auto compile<ui_tree>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1296{
1297 APP_SCOPE_PERF("Compile UI Tree");
1298 auto absolute_path = resolve_input_file(key);
1299 std::string str_input = absolute_path.string();
1300 fs::error_code err;
1301
1302 auto tree = std::make_shared<ui_tree>();
1303 {
1304 // For ui_tree, we can load the HTML/RML content directly from file
1305 std::ifstream file(absolute_path);
1306 if (file.is_open())
1307 {
1308 std::stringstream buffer;
1309 buffer << file.rdbuf();
1310 tree->content = buffer.str();
1311 file.close();
1312 }
1313
1314 asset_writer::atomic_write_file(output, [&](const fs::path& temp) -> void
1315 {
1316 save_to_file(temp.string(), tree);
1317 }, err);
1318 }
1319
1320 if(err)
1321 {
1322 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1323 str_input, output.filename().string(), err.message());
1324 return false;
1325 }
1326
1327 if(!write_manifest_file<ui_tree>(absolute_path, output))
1328 {
1329 APPLOG_ERROR("Failed to write manifest for compiled ui_tree: {0}", output.string());
1330 return false;
1331 }
1332
1333 return true;
1334}
1335
1336template<>
1337auto compile<style_sheet>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1338{
1339 APP_SCOPE_PERF("Compile Style Sheet");
1340 auto absolute_path = resolve_input_file(key);
1341 std::string str_input = absolute_path.string();
1342 fs::error_code err;
1343
1344 auto sheet = std::make_shared<style_sheet>();
1345 {
1346 // For style_sheet, we can load the CSS/RCSS content directly from file
1347 std::ifstream file(absolute_path);
1348 if (file.is_open())
1349 {
1350 std::stringstream buffer;
1351 buffer << file.rdbuf();
1352 sheet->content = buffer.str();
1353 file.close();
1354 }
1355
1356 asset_writer::atomic_write_file(output, [&](const fs::path& temp) -> void
1357 {
1358 save_to_file(temp.string(), sheet);
1359 }, err);
1360 }
1361
1362 if(err)
1363 {
1364 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1365 str_input, output.filename().string(), err.message());
1366 return false;
1367 }
1368
1369 if(!write_manifest_file<style_sheet>(absolute_path, output))
1370 {
1371 APPLOG_ERROR("Failed to write manifest for compiled style_sheet: {0}", output.string());
1372 return false;
1373 }
1374
1375 return true;
1376}
1377
1378template<>
1379auto read_importer<audio_clip>(asset_manager& am, const fs::path& key) -> std::shared_ptr<asset_importer_meta>
1380{
1381 APP_SCOPE_PERF("Read Audio Clip Importer");
1382 auto absolute = fs::resolve_protocol(key).string();
1383 asset_meta meta;
1384 if(load_from_file(absolute, meta))
1385 {
1386 if(!meta.importer)
1387 {
1388 meta.importer = std::make_shared<audio_importer_meta>();
1389
1390 meta.uid = am.add_asset_info_for_path(resolve_input_file(key), meta, true);
1391
1392 fs::error_code err;
1393 asset_writer::atomic_write_file(absolute, [&](const fs::path& temp) -> void
1394 {
1395 save_to_file(temp.string(), meta);
1396 }, err);
1397
1398 return nullptr;
1399 }
1400 }
1401
1402 return meta.importer;
1403}
1404
1405template<>
1406auto compile<audio_clip>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1407{
1408 APP_SCOPE_PERF("Compile Audio Clip");
1409 // Try to import first.
1410 auto base_importer = read_importer<audio_clip>(am, key);
1411
1412 if(!base_importer)
1413 {
1414 return true;
1415 }
1416
1417 auto importer = std::static_pointer_cast<audio_importer_meta>(base_importer);
1418
1419 auto absolute_path = resolve_input_file(key);
1420
1421 std::string str_input = absolute_path.string();
1422
1423 fs::error_code err;
1424
1425 audio::sound_data clip;
1426 {
1427 std::string error;
1428 if(!load_from_file(str_input, clip, error))
1429 {
1430 APPLOG_ERROR("Failed compilation of {0} with error: {1}", str_input, error);
1431 return false;
1432 }
1433
1434 if(importer->force_to_mono)
1435 {
1436 clip.convert_to_mono();
1437 }
1438 else
1439 {
1440 clip.convert_to_stereo();
1441 }
1442
1443 asset_writer::atomic_write_file(output, [&](const fs::path& temp) -> void
1444 {
1445 save_to_file_bin(temp.string(), clip);
1446 }, err);
1447 }
1448
1449 if(err)
1450 {
1451 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1452 str_input, output.filename().string(), err.message());
1453 return false;
1454 }
1455
1456 if(!write_manifest_file<audio_clip>(absolute_path, output))
1457 {
1458 APPLOG_ERROR("Failed to write manifest for compiled audio_clip: {0}", output.string());
1459 return false;
1460 }
1461
1462 return true;
1463}
1464// Struct to hold the parsed error details
1466{
1467 std::string file{}; // Path to the file
1468 int line{}; // Line number of the error
1469 std::string msg{}; // Full error line
1470};
1471
1472// Function to parse all compilation errors
1473auto parse_compilation_errors(const std::string& log) -> std::vector<script_compilation_entry>
1474{
1475 // Regular expression to extract the warning details
1476 std::regex warning_regex(R"((.*)\‍((\d+),\d+\): error .*)");
1477 std::vector<script_compilation_entry> entries;
1478
1479 // Use std::sregex_iterator to find all matches
1480 auto begin = std::sregex_iterator(log.begin(), log.end(), warning_regex);
1481 auto end = std::sregex_iterator();
1482
1483 for(auto it = begin; it != end; ++it)
1484 {
1485 const std::smatch& match = *it;
1486 if(match.size() >= 3)
1487 {
1489 entry.file = match[1].str(); // Extract file path
1490 entry.line = std::stoi(match[2].str()); // Extract line number
1491 entry.msg = match[0].str(); // Extract full warning line
1492 entries.emplace_back(std::move(entry));
1493 }
1494 }
1495
1496 return entries;
1497}
1498
1499// Function to parse all compilation warnings
1500auto parse_compilation_warnings(const std::string& log) -> std::vector<script_compilation_entry>
1501{
1502 // Regular expression to extract the warning details
1503 std::regex warning_regex(R"((.*)\‍((\d+),\d+\): error .*)");
1504 std::vector<script_compilation_entry> entries;
1505
1506 // Use std::sregex_iterator to find all matches
1507 auto begin = std::sregex_iterator(log.begin(), log.end(), warning_regex);
1508 auto end = std::sregex_iterator();
1509
1510 for(auto it = begin; it != end; ++it)
1511 {
1512 const std::smatch& match = *it;
1513 if(match.size() >= 3)
1514 {
1516 entry.file = match[1].str(); // Extract file path
1517 entry.line = std::stoi(match[2].str()); // Extract line number
1518 entry.msg = match[0].str(); // Extract full warning line
1519 entries.emplace_back(std::move(entry));
1520 }
1521
1522 }
1523
1524 return entries;
1525}
1526
1527template<>
1528auto compile<script_library>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1529{
1530 bool result = true;
1531 fs::error_code err;
1532 fs::path temp = fs::temp_directory_path(err);
1533
1534 dotnet::compiler_params params;
1535
1536 auto protocol = fs::extract_protocol(fs::convert_to_protocol(key)).generic_string();
1537
1538 if(protocol != "engine")
1539 {
1540 auto lib_compiled_key = fs::resolve_protocol(script_system::get_lib_compiled_key("engine"));
1541
1542 params.references.emplace_back(lib_compiled_key.filename().string());
1543
1544 params.references_locations.emplace_back(lib_compiled_key.parent_path().string());
1545 }
1546
1547 auto assets = am.get_assets<script>(protocol);
1548 for(const auto& asset : assets)
1549 {
1550 if(asset)
1551 {
1552 params.files.emplace_back(fs::resolve_protocol(asset.id()).string());
1553 }
1554 }
1555
1556 temp /= script_system::get_lib_name(protocol);
1557
1558 auto temp_xml = temp;
1559 temp_xml.replace_extension(".xml");
1560 auto output_xml = output;
1561 output_xml.replace_extension(".xml");
1562
1563 auto temp_mdb = temp;
1564 temp_mdb.concat(".mdb");
1565 auto output_mdb = output;
1566 output_mdb.concat(".mdb");
1567
1568 std::string str_output = temp.string();
1569
1570 params.output_name = str_output;
1571 params.output_doc_name = temp_xml.string();
1572 if(params.files.empty())
1573 {
1574 fs::remove(output, err);
1575 fs::remove(output_mdb, err);
1576
1577 if(protocol == "engine")
1578 {
1579 APPLOG_ERROR("No scripts to compile for engine");
1580 return false;
1581 }
1582
1583 return result;
1584 }
1585
1586 params.debug = flags & script_library::compilation_flags::debug;
1587
1588 std::string error;
1589 // auto cmd = dotnet::create_compile_command_detailed(params);
1590 auto cmd = dotnet::create_compile_command_detailed_rsp(params, temp.string() + ".rsp");
1591
1592 // APPLOG_TRACE("Script Compile : \n {0} {1}", cmd.cmd, cmd.args);
1593
1594 fs::remove(temp, err);
1595 fs::remove(temp_mdb, err);
1596 fs::remove(temp_xml, err);
1597
1598 if(!run_process(cmd.cmd, cmd.args, true, error))
1599 {
1600 auto parsed_errors = parse_compilation_errors(error);
1601
1602 if(!parsed_errors.empty())
1603 {
1604 for(const auto& error : parsed_errors)
1605 {
1606 APPLOG_ERROR_LOC(error.file.c_str(), error.line, "", error.msg);
1607 }
1608 }
1609 else
1610 {
1611 APPLOG_ERROR("Failed compilation of {0} with error: {1}", output.string(), error);
1612 }
1613 result = false;
1614 }
1615 else
1616 {
1617 if(!params.debug)
1618 {
1619 fs::remove(output_mdb, err);
1620 }
1621
1622 fs::create_directories(output.parent_path(), err);
1623
1624 if(protocol != "engine")
1625 {
1626 auto parsed_warnings = parse_compilation_warnings(error);
1627
1628 for(const auto& warning : parsed_warnings)
1629 {
1630 APPLOG_WARNING_LOC(warning.file.c_str(), warning.line, "", warning.msg);
1631 }
1632 }
1633
1634 // dotnet::compile_cmd aot_cmd;
1635 // aot_cmd.cmd = "mono";
1636 // aot_cmd.args.emplace_back("--aot=full");
1637 // aot_cmd.args.emplace_back(temp.string());
1638 // error = {};
1639 // bool ok = run_process(aot_cmd.cmd, aot_cmd.args, true, error);
1640
1641 //APPLOG_INFO("Successful compilation of {0}", fs::replace(output, "temp-", "").string());
1642
1643 // Part of script compilation: rewrite mono-style [InternalCall]
1644 // externs with real bodies (coreclr backend; no-op on mono).
1645 if(!dotnet::weave_assembly(str_output))
1646 {
1647 APPLOG_ERROR("Failed internal call weaving of {0}", output.string());
1648 return false;
1649 }
1650
1652 }
1653
1654 return result;
1655}
1656
1657template<>
1658auto compile<script>(asset_manager& am, const fs::path& key, const fs::path& output, uint32_t flags) -> bool
1659{
1660 APP_SCOPE_PERF("Compile Script");
1661 auto absolute_path = resolve_input_file(key);
1662
1663 fs::error_code er;
1664 asset_writer::atomic_copy_file(absolute_path, output, er);
1665
1666 if(er)
1667 {
1668 APPLOG_ERROR("Failed compilation of {0} -> {1} with error: {2}",
1669 absolute_path.string(), output.filename().string(), er.message());
1670 return false;
1671 }
1672
1673 if(!write_manifest_file<script>(absolute_path, output))
1674 {
1675 APPLOG_ERROR("Failed to write manifest for compiled script: {0}", output.string());
1676 return false;
1677 }
1678
1679
1680 return true;
1681}
1682
1683} // namespace unravel::asset_compiler
1684
uint32_t width
uint32_t height
gfx::texture_format format
bool fits_max_size
bool imageParseInfo(const void *_data, uint32_t _size, bimg::ImageContainer &_info, bx::Error *_err)
bimg::ImageContainer * imageLoad(const void *data, uint32_t size, bgfx::TextureFormat::Enum _dstFormat)
bool imageSave(const char *saveAs, bimg::ImageContainer *image)
bool imageFlipTangentSpaceNormalY(bimg::ImageContainer *&_image)
bool imagePrepareNormalMapBakePng(bimg::ImageContainer *&_image)
Prepare a flipped normal map for PNG bake export (RGBA8, opaque alpha when unused).
Manages assets, including loading, unloading, and storage.
Base class for materials used in rendering.
Definition material.h:44
static auto apply_skin_to_load_data(load_data &data) -> bool
Applies skinning vertex duplication to load_data (for offline LOD generation).
Definition mesh.cpp:2033
static auto generate_lods_for_load_data(load_data &data, const std::vector< std::pair< size_t, float > > &lod_configs) -> bool
Generates LOD levels directly in load_data without creating GPU buffers.
Definition mesh.cpp:2236
static auto generate_default_lod_configs(const load_data &data, float target_error=0.01f) -> std::vector< std::pair< size_t, float > >
Generates default LOD configurations based on mesh triangle count.
Definition mesh.cpp:2018
auto has_bones() const -> bool
Checks whether the skin data has any bones.
Definition mesh.cpp:3756
std::vector< render_pass_entry > entries
#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
std::string error
Definition mcp_async.cpp:33
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:404
auto is_compressed_format(texture_format fmt) -> bool
Definition format.cpp:394
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, const std::vector< std::string > &environment_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
void resolve_dependencies(const fs::path &, std::vector< fs::path > &)
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)
#define APP_SCOPE_PERF(name_literal)
Create a scoped performance timer that records to the timeline profiler. Only accepts string literals...
Definition profiler.h:675
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:111
Struct used for mesh construction.
Definition mesh.h:451
std::vector< hpp::uuid > default_material_uids
Definition mesh.h:481
std::vector< uint8_t > vertex_data
Total number of vertices.
Definition mesh.h:455
skin_bind_data skin_data
True if skinning was baked into topology (vertex duplication + palette index encoding) during compila...
Definition mesh.h:467
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)
std::vector< GitHubAsset > assets