53auto resolve_path(
const std::string& key) -> fs::path
58auto resolve_input_file(
const fs::path& key) -> fs::path
62 if(absolute_path.extension() ==
".meta")
64 absolute_path.replace_extension();
69auto escape_str(
const std::string& str) -> std::string
71 return "\"" + str +
"\"";
74auto run_process(
const std::string& process,
75 const std::vector<std::string>& args_array,
77 std::string& err) ->
bool
80 err = result.out_output;
82 if(!result.err_output.empty())
89 err += result.err_output;
92 if(err.find(
"error") != std::string::npos)
97 return result.retcode == 0;
100struct input_texture_info
140 const uint32_t limit = texture_size_to_pixel_limit(max_size);
143 args.emplace_back(
"--max");
144 args.emplace_back(std::to_string(limit));
148auto fill_input_texture_info_from_container(
const bimg::ImageContainer& info, input_texture_info& out) ->
void
151 out.width = info.m_width;
152 out.height = info.m_height;
157 input_texture_info result{};
158 const bx::FilePath file_path(input_path.string().c_str());
160 bimg::ImageContainer header{};
163 fill_input_texture_info_from_container(header, result);
167 bimg::ImageContainer* image =
imageLoad(file_path, bgfx::TextureFormat::Count);
173 fill_input_texture_info_from_container(*image, result);
174 bimg::imageFree(image);
177 const uint32_t limit = texture_size_to_pixel_limit(max_size);
180 const uint32_t largest_dimension = std::max(result.width, result.height);
181 result.fits_max_size = largest_dimension <= limit;
252bool copy_compiled_file(
const fs::path& from,
const fs::path& to)
259 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}", from.string(), to.filename().string(), err.message());
266 const fs::path& extension,
274 if(input_format == gfx::texture_format::BC1)
276 return gfx::texture_format::BC3;
286 if(extension ==
".hdr" || extension ==
".exr")
296 return gfx::texture_format::BC6H;
301 if(info.num_channels == 1)
303 return gfx::texture_format::BC4;
308 if(info.num_channels == 2)
310 return gfx::texture_format::BC5;
316 if(!info.has_alpha_channel)
322 return gfx::texture_format::BC1;
325 return gfx::texture_format::BC1;
329 return gfx::texture_format::BC1;
334 return gfx::texture_format::BC1;
345 return gfx::texture_format::BC3;
347 return gfx::texture_format::BC3;
351 return gfx::texture_format::BC3;
356 return gfx::texture_format::BC3;
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
366 temp_baked_path.clear();
368 if(!importer.invert_normal_y)
373 bimg::ImageContainer* image =
imageLoad(bx::FilePath(input_path.string().c_str()));
376 APPLOG_ERROR(
"Failed to load texture for normal Y bake: {0}", input_path.string());
382 bimg::imageFree(image);
383 APPLOG_ERROR(
"Failed to flip normal map Y for: {0}", input_path.string());
389 bimg::imageFree(image);
390 APPLOG_ERROR(
"Failed to prepare baked normal map PNG for: {0}", input_path.string());
396 temp_baked_path = fs::temp_directory_path(err) / (input_path.stem().
string() +
"_normal_y.png");
397 if(err || temp_baked_path.empty())
399 bimg::imageFree(image);
400 APPLOG_ERROR(
"Failed to resolve temp path for normal Y bake: {0}", input_path.string());
404 if(!
imageSave(temp_baked_path.string().c_str(), image))
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());
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;
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
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();
427 std::string str_input = compile_input.string();
428 std::string str_output = output_path.string();
430 bool try_compress = protocol ==
"app";
432 auto quality = importer.quality;
436 if(ctx.has<settings>())
438 auto& ss = ctx.get<settings>();
439 quality.compression = ss.assets.texture.default_compression;
446 if(ctx.has<settings>())
448 auto& ss = ctx.get<settings>();
449 quality.max_size = ss.assets.texture.default_max_size;
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);
468 const bool needs_format_conversion = input_info.format !=
format;
469 const bool needs_downscale = !input_info.fits_max_size;
471 if(needs_format_conversion || needs_downscale)
473 if(needs_downscale && !needs_format_conversion)
475 APPLOG_INFO(
"Downscaling {0} ({1}x{2}) to fit max size {3}",
476 compile_input.filename().string(),
479 texture_size_to_pixel_limit(quality.max_size));
482 std::vector<std::string> args_array = {
493 args_array.emplace_back(
"-t");
496 if(
format == gfx::texture_format::BC7 ||
format == gfx::texture_format::BC6H)
500 args_array.emplace_back(
"-q");
501 args_array.emplace_back(
"fastest");
503 else if(needs_format_conversion
506 args_array.emplace_back(
"-q");
507 args_array.emplace_back(
"highest");
511 if(importer.generate_mipmaps)
513 args_array.emplace_back(
"-m");
516 append_texture_max_size_args(args_array, quality.max_size);
518 switch(importer.type)
522 args_array.emplace_back(
"--equirect");
528 args_array.emplace_back(
"--normalmap");
540 std::ofstream output_file(str_output);
547 bool compiled = run_process(texturec.string(), args_array,
false,
error);
551 fs::remove(str_output);
557 copy_compiled_file(compile_input, output_path);
562 fs::error_code remove_err;
563 fs::remove(temp_baked_path, remove_err);
570auto compile_shader_to_file(
const fs::path& input_path,
571 const fs::path& output_path,
575 std::string str_input = input_path.string();
576 std::string str_output = output_path.string();
578 std::string file = input_path.stem().string();
579 fs::path dir = input_path.parent_path();
582 std::string str_include = include.string();
584 fs::path varying = dir / (file +
".io");
587 if(!fs::exists(varying, err))
589 varying = dir /
"varying.def.io";
591 if(!fs::exists(varying, err))
593 varying = dir /
"varying.def.sc";
596 std::string str_varying = varying.string();
598 std::string str_platform;
599 std::string str_profile;
600 std::string str_type;
602 bool optimize =
true;
607 std::string str_opt = optimize ?
"3" :
"0";
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_");
617 bool needs_compute_buffers =
false;
620 std::ifstream shader_file(str_input);
621 if(shader_file.is_open())
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;
632 if(renderer == gfx::renderer_type::Vulkan)
634 str_platform =
"windows";
635 str_profile =
"spirv";
638 if(renderer == gfx::renderer_type::Direct3D11 || renderer == gfx::renderer_type::Direct3D12)
640 str_platform =
"windows";
644 str_profile =
"s_5_0";
645 if(renderer == gfx::renderer_type::Direct3D12)
647 str_profile =
"s_6_0";
652 str_profile =
"s_5_0";
653 if(renderer == gfx::renderer_type::Direct3D12)
655 str_profile =
"s_6_0";
657 str_opt = optimize ?
"1" :
"0";
660 else if(renderer == gfx::renderer_type::OpenGLES)
662 str_platform =
"android";
663 str_profile =
"100_es";
665 else if(renderer == gfx::renderer_type::OpenGL)
667 str_platform =
"linux";
673 str_profile = needs_compute_buffers ?
"430" :
"140";
680 else if(renderer == gfx::renderer_type::Metal)
682 str_platform =
"osx";
683 str_profile =
"metal";
689 str_type =
"fragment";
691 str_type =
"compute";
693 str_type =
"unknown";
695 std::vector<std::string> args_array = {
711 if(!str_platform.empty())
713 args_array.emplace_back(
"--platform");
714 args_array.emplace_back(str_platform);
717 if(!str_profile.empty())
719 args_array.emplace_back(
"-p");
720 args_array.emplace_back(str_profile);
725 args_array.emplace_back(
"-O");
726 args_array.emplace_back(str_opt);
733 std::ofstream output_file(str_output);
739 if(!run_process(shaderc.string(), args_array,
true,
error))
741 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}", str_input, output_path.filename().string(),
error);
742 fs::remove(str_output);
749auto write_manifest_file(
const fs::path& input_path,
const fs::path& output_path) ->
bool
752 std::vector<fs::path> deps;
755 asset_manifest manifest(input_path);
756 manifest.compute_source_fingerprint(input_path, deps);
762 while(!
ok && attempts < 3)
770 ok = !err && success;
777auto write_minified_file(
const fs::path& input_path,
const fs::path& output_path) ->
bool
780#if SER20_ASSOCIATIVE_ARCHIVE == SER20_ASSOCIATIVE_ARCHIVE_SIMDJSON
782 std::string str_input = input_path.string();
783 simdjson::dom::parser parser;
784 auto doc = parser.load(str_input);
787 APPLOG_ERROR(
"Failed to parse {0}: {1}", input_path.string(), simdjson::error_message(doc.error()));
791 auto minified = simdjson::minify(doc);
798 std::ofstream file(temp_manifest_path);
808 copy_compiled_file(input_path, output_path);
820 auto absolute_path = resolve_input_file(key);
821 std::string str_input = absolute_path.string();
823 auto extension = output.extension();
830 compile_shader_to_file(
839 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
840 str_input, output.filename().string(), err.message());
844 if(!write_manifest_file<gfx::shader>(absolute_path, output))
846 APPLOG_ERROR(
"Failed to write manifest for compiled shader: {0}", output.string());
862 meta.
importer = std::make_shared<texture_importer_meta>();
864 meta.
uid = am.add_asset_info_for_path(resolve_input_file(key), meta,
true);
889 auto importer = std::static_pointer_cast<texture_importer_meta>(base_importer);
892 auto absolute_path = resolve_input_file(key);
893 std::string str_input = absolute_path.string();
899 compile_texture_to_file(
909 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
910 str_input, output.filename().string(), err.message());
914 if(!write_manifest_file<gfx::texture>(absolute_path, output))
916 APPLOG_ERROR(
"Failed to write manifest for compiled texture: {0}", output.string());
927 auto absolute_path = resolve_input_file(key);
929 std::string str_input = absolute_path.string();
945 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
946 str_input, output.filename().string(), err.message());
950 if(!write_manifest_file<unravel::material>(absolute_path, output))
952 APPLOG_ERROR(
"Failed to write manifest for compiled material: {0}", output.string());
969 meta.
importer = std::make_shared<mesh_importer_meta>();
971 meta.
uid = am.add_asset_info_for_path(resolve_input_file(key), meta,
true);
998 auto importer = std::static_pointer_cast<mesh_importer_meta>(base_importer);
1000 auto absolute_path = resolve_input_file(key);
1002 std::string str_input = absolute_path.string();
1006 fs::path file = absolute_path.stem();
1007 fs::path dir = absolute_path.parent_path();
1010 std::vector<animation_clip> animations;
1011 std::vector<importer::imported_material> materials;
1012 std::vector<importer::imported_texture> textures;
1030 APPLOG_ERROR(
"Failed to apply skinning data before generating LODs for {0}", str_input);
1036 if(importer->model.generate_lods)
1042 if(!lod_configs.empty())
1052 APPLOG_INFO(
"Adding default material UIDs for {0}", str_input);
1054 for(
const auto&
material : materials)
1056 fs::path mat_output;
1060 mat_output = (dir / file).
string() +
".mat";
1064 mat_output = dir / (
material.name +
".mat");
1067 auto uid = am.add_asset_for_path(mat_output,
false);
1085 for(
const auto& animation : animations)
1087 fs::path anim_output;
1088 if(animation.name.empty())
1090 anim_output = (dir / file).
string() +
".anim";
1094 anim_output = dir / (animation.name +
".anim");
1106 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
1107 str_input, output.filename().string(), err.message());
1111 if(!write_manifest_file<mesh>(absolute_path, output))
1113 APPLOG_ERROR(
"Failed to write manifest for compiled mesh: {0}", output.string());
1130 meta.
importer = std::make_shared<animation_importer_meta>();
1132 meta.
uid = am.add_asset_info_for_path(resolve_input_file(key), meta,
true);
1159 auto importer = std::static_pointer_cast<animation_importer_meta>(base_importer);
1161 auto absolute_path = resolve_input_file(key);
1163 std::string str_input = absolute_path.string();
1185 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
1186 str_input, output.filename().string(), err.message());
1190 if(!write_manifest_file<animation_clip>(absolute_path, output))
1192 APPLOG_ERROR(
"Failed to write manifest for compiled animation: {0}", output.string());
1203 auto absolute_path = resolve_input_file(key);
1205 copy_compiled_file(absolute_path, output);
1207 if(!write_manifest_file<font>(absolute_path, output))
1209 APPLOG_ERROR(
"Failed to write manifest for compiled font: {0}", output.string());
1220 auto absolute_path = resolve_input_file(key);
1222 if(!write_minified_file(absolute_path, output))
1224 APPLOG_ERROR(
"Failed to write minified file for compiled prefab: {0}", output.string());
1228 if(!write_manifest_file<prefab>(absolute_path, output))
1230 APPLOG_ERROR(
"Failed to write manifest for compiled prefab: {0}", output.string());
1241 auto absolute_path = resolve_input_file(key);
1243 if(!write_minified_file(absolute_path, output))
1245 APPLOG_ERROR(
"Failed to write minified file for compiled scene: {0}", output.string());
1249 if(!write_manifest_file<scene_prefab>(absolute_path, output))
1251 APPLOG_ERROR(
"Failed to write manifest for compiled scene_prefab: {0}", output.string());
1262 auto absolute_path = resolve_input_file(key);
1264 std::string str_input = absolute_path.string();
1268 auto material = std::make_shared<physics_material>();
1280 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
1281 str_input, output.filename().string(), err.message());
1285 if(!write_manifest_file<physics_material>(absolute_path, output))
1287 APPLOG_ERROR(
"Failed to write manifest for compiled physics_material: {0}", output.string());
1298 auto absolute_path = resolve_input_file(key);
1299 std::string str_input = absolute_path.string();
1302 auto tree = std::make_shared<ui_tree>();
1305 std::ifstream file(absolute_path);
1308 std::stringstream buffer;
1309 buffer << file.rdbuf();
1310 tree->content = buffer.str();
1322 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
1323 str_input, output.filename().string(), err.message());
1327 if(!write_manifest_file<ui_tree>(absolute_path, output))
1329 APPLOG_ERROR(
"Failed to write manifest for compiled ui_tree: {0}", output.string());
1340 auto absolute_path = resolve_input_file(key);
1341 std::string str_input = absolute_path.string();
1344 auto sheet = std::make_shared<style_sheet>();
1347 std::ifstream file(absolute_path);
1350 std::stringstream buffer;
1351 buffer << file.rdbuf();
1352 sheet->content = buffer.str();
1364 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
1365 str_input, output.filename().string(), err.message());
1369 if(!write_manifest_file<style_sheet>(absolute_path, output))
1371 APPLOG_ERROR(
"Failed to write manifest for compiled style_sheet: {0}", output.string());
1388 meta.
importer = std::make_shared<audio_importer_meta>();
1390 meta.
uid = am.add_asset_info_for_path(resolve_input_file(key), meta,
true);
1417 auto importer = std::static_pointer_cast<audio_importer_meta>(base_importer);
1419 auto absolute_path = resolve_input_file(key);
1421 std::string str_input = absolute_path.string();
1425 audio::sound_data clip;
1434 if(importer->force_to_mono)
1436 clip.convert_to_mono();
1440 clip.convert_to_stereo();
1451 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
1452 str_input, output.filename().string(), err.message());
1456 if(!write_manifest_file<audio_clip>(absolute_path, output))
1458 APPLOG_ERROR(
"Failed to write manifest for compiled audio_clip: {0}", output.string());
1476 std::regex warning_regex(R
"((.*)\((\d+),\d+\): error .*)");
1477 std::vector<script_compilation_entry> entries;
1480 auto begin = std::sregex_iterator(log.begin(), log.end(), warning_regex);
1481 auto end = std::sregex_iterator();
1483 for(
auto it = begin; it != end; ++it)
1485 const std::smatch& match = *it;
1486 if(match.size() >= 3)
1489 entry.file = match[1].str();
1490 entry.line = std::stoi(match[2].str());
1491 entry.msg = match[0].str();
1503 std::regex warning_regex(R
"((.*)\((\d+),\d+\): error .*)");
1504 std::vector<script_compilation_entry> entries;
1507 auto begin = std::sregex_iterator(log.begin(), log.end(), warning_regex);
1508 auto end = std::sregex_iterator();
1510 for(
auto it = begin; it != end; ++it)
1512 const std::smatch& match = *it;
1513 if(match.size() >= 3)
1516 entry.file = match[1].str();
1517 entry.line = std::stoi(match[2].str());
1518 entry.msg = match[0].str();
1532 fs::path temp = fs::temp_directory_path(err);
1534 dotnet::compiler_params params;
1538 if(protocol !=
"engine")
1542 params.references.emplace_back(lib_compiled_key.filename().string());
1544 params.references_locations.emplace_back(lib_compiled_key.parent_path().string());
1548 for(
const auto& asset :
assets)
1558 auto temp_xml = temp;
1559 temp_xml.replace_extension(
".xml");
1560 auto output_xml = output;
1561 output_xml.replace_extension(
".xml");
1563 auto temp_mdb = temp;
1564 temp_mdb.concat(
".mdb");
1565 auto output_mdb = output;
1566 output_mdb.concat(
".mdb");
1568 std::string str_output = temp.string();
1570 params.output_name = str_output;
1571 params.output_doc_name = temp_xml.string();
1572 if(params.files.empty())
1574 fs::remove(output, err);
1575 fs::remove(output_mdb, err);
1577 if(protocol ==
"engine")
1590 auto cmd = dotnet::create_compile_command_detailed_rsp(params, temp.string() +
".rsp");
1594 fs::remove(temp, err);
1595 fs::remove(temp_mdb, err);
1596 fs::remove(temp_xml, err);
1598 if(!run_process(cmd.cmd, cmd.args,
true,
error))
1602 if(!parsed_errors.empty())
1604 for(
const auto&
error : parsed_errors)
1611 APPLOG_ERROR(
"Failed compilation of {0} with error: {1}", output.string(),
error);
1619 fs::remove(output_mdb, err);
1622 fs::create_directories(output.parent_path(), err);
1624 if(protocol !=
"engine")
1628 for(
const auto& warning : parsed_warnings)
1645 if(!dotnet::weave_assembly(str_output))
1647 APPLOG_ERROR(
"Failed internal call weaving of {0}", output.string());
1661 auto absolute_path = resolve_input_file(key);
1668 APPLOG_ERROR(
"Failed compilation of {0} -> {1} with error: {2}",
1669 absolute_path.string(), output.filename().string(), er.message());
1673 if(!write_manifest_file<script>(absolute_path, output))
1675 APPLOG_ERROR(
"Failed to write manifest for compiled script: {0}", output.string());