box_export.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. let box_export_htab: zui_handle_t = zui_handle_create();
  2. let box_export_files: string[] = null;
  3. let box_export_mesh_handle: zui_handle_t = zui_handle_create();
  4. ///if (is_paint || is_lab)
  5. let box_export_hpreset: zui_handle_t = zui_handle_create();
  6. let box_export_preset: export_preset_t = null;
  7. let box_export_channels: string[] = ["base_r", "base_g", "base_b", "height", "metal", "nor_r", "nor_g", "nor_g_directx", "nor_b", "occ", "opac", "rough", "smooth", "emis", "subs", "0.0", "1.0"];
  8. let box_export_color_spaces: string[] = ["linear", "srgb"];
  9. ///end
  10. ///if (is_paint || is_lab)
  11. function box_export_show_textures() {
  12. ui_box_show_custom(function (ui: zui_t) {
  13. if (box_export_files == null) {
  14. box_export_fetch_presets();
  15. box_export_hpreset.position = array_index_of(box_export_files, "generic");
  16. }
  17. if (box_export_preset == null) {
  18. box_export_parse_preset();
  19. box_export_hpreset.children = null;
  20. }
  21. box_export_tab_export_textures(ui, tr("Export Textures"));
  22. box_export_tab_presets(ui);
  23. ///if is_paint
  24. box_export_tab_atlases(ui);
  25. ///if (krom_android || krom_ios)
  26. box_export_tab_export_mesh(ui, box_export_htab);
  27. ///end
  28. ///end
  29. }, 540, 310);
  30. }
  31. ///end
  32. ///if is_paint
  33. function box_export_show_bake_material() {
  34. ui_box_show_custom(function (ui: zui_t) {
  35. if (box_export_files == null) {
  36. box_export_fetch_presets();
  37. box_export_hpreset.position = array_index_of(box_export_files, "generic");
  38. }
  39. if (box_export_preset == null) {
  40. box_export_parse_preset();
  41. box_export_hpreset.children = null;
  42. }
  43. box_export_tab_export_textures(ui, tr("Bake to Textures"), true);
  44. box_export_tab_presets(ui);
  45. }, 540, 310);
  46. }
  47. ///end
  48. let _box_export_bake_material: bool;
  49. ///if (is_paint || is_lab)
  50. function box_export_tab_export_textures(ui: zui_t, title: string, bake_material: bool = false) {
  51. let tab_vertical: bool = config_raw.touch_ui;
  52. if (zui_tab(box_export_htab, title, tab_vertical)) {
  53. let row: f32[] = [0.5, 0.5];
  54. zui_row(row);
  55. ///if is_paint
  56. ///if (krom_android || krom_ios)
  57. let base_res_combo: string[] = ["128", "256", "512", "1K", "2K", "4K"];
  58. zui_combo(base_res_handle, base_res_combo, tr("Resolution"), true);
  59. ///else
  60. let base_res_combo: string[] = ["128", "256", "512", "1K", "2K", "4K", "8K", "16K"];
  61. zui_combo(base_res_handle, base_res_combo, tr("Resolution"), true);
  62. ///end
  63. ///end
  64. ///if is_lab
  65. ///if (krom_android || krom_ios)
  66. let base_res_combo: string[] = ["2K", "4K"];
  67. zui_combo(base_res_handle, base_res_combo, tr("Resolution"), true);
  68. ///else
  69. let base_res_combo: string[] = ["2K", "4K", "8K", "16K"];
  70. zui_combo(base_res_handle, base_res_combo, tr("Resolution"), true);
  71. ///end
  72. ///end
  73. if (base_res_handle.changed) {
  74. base_on_layers_resized();
  75. }
  76. ///if (is_lab || krom_android || krom_ios)
  77. let base_bits_combo: string[] = ["8bit"];
  78. zui_combo(base_bits_handle, base_bits_combo, tr("Color"), true);
  79. ///else
  80. let base_bits_combo: string[] = ["8bit", "16bit", "32bit"];
  81. zui_combo(base_bits_handle, base_bits_combo, tr("Color"), true);
  82. ///end
  83. ///if is_paint
  84. if (base_bits_handle.changed) {
  85. app_notify_on_init(base_set_layer_bits);
  86. }
  87. ///end
  88. zui_row(row);
  89. if (base_bits_handle.position == texture_bits_t.BITS8) {
  90. let h: zui_handle_t = zui_handle(__ID__);
  91. if (h.init) {
  92. h.position = context_raw.format_type;
  93. }
  94. let format_combo: string[] = ["png", "jpg"];
  95. context_raw.format_type = zui_combo(h, format_combo, tr("Format"), true);
  96. }
  97. else {
  98. let h: zui_handle_t = zui_handle(__ID__);
  99. if (h.init) {
  100. h.position = context_raw.format_type;
  101. }
  102. let format_combo: string[] = ["exr"];
  103. context_raw.format_type = zui_combo(h, format_combo, tr("Format"), true);
  104. }
  105. ui.enabled = context_raw.format_type == texture_ldr_format_t.JPG && base_bits_handle.position == texture_bits_t.BITS8;
  106. let h_quality: zui_handle_t = zui_handle(__ID__);
  107. if (h_quality.init) {
  108. h_quality.value = context_raw.format_quality;
  109. }
  110. context_raw.format_quality = zui_slider(h_quality, tr("Quality"), 0.0, 100.0, true, 1);
  111. ui.enabled = true;
  112. ///if is_paint
  113. zui_row(row);
  114. ui.enabled = !bake_material;
  115. let layers_export_handle: zui_handle_t = zui_handle(__ID__);
  116. layers_export_handle.position = context_raw.layers_export;
  117. let layers_export_combo: string[] = [tr("Visible"), tr("Selected"), tr("Per Object"), tr("Per Udim Tile")];
  118. context_raw.layers_export = zui_combo(layers_export_handle, layers_export_combo, tr("Layers"), true);
  119. ui.enabled = true;
  120. ///end
  121. zui_combo(box_export_hpreset, box_export_files, tr("Preset"), true);
  122. if (box_export_hpreset.changed) box_export_preset = null;
  123. let layers_destination_handle: zui_handle_t = zui_handle(__ID__);
  124. layers_destination_handle.position = context_raw.layers_destination;
  125. let layers_destination_combo: string[] = [tr("Disk"), tr("Packed")];
  126. context_raw.layers_destination = zui_combo(layers_destination_handle, layers_destination_combo, tr("Destination"), true);
  127. zui_end_element();
  128. zui_row(row);
  129. if (zui_button(tr("Cancel"))) {
  130. ui_box_hide();
  131. }
  132. if (zui_button(tr("Export"))) {
  133. ui_box_hide();
  134. if (context_raw.layers_destination == export_destination_t.PACKED) {
  135. _box_export_bake_material = bake_material;
  136. context_raw.texture_export_path = "/";
  137. app_notify_on_init(function () {
  138. ///if is_paint
  139. export_texture_run(context_raw.texture_export_path, _box_export_bake_material);
  140. ///end
  141. ///if is_lab
  142. export_texture_run(context_raw.texture_export_path);
  143. ///end
  144. });
  145. }
  146. else {
  147. let filters = base_bits_handle.position != texture_bits_t.BITS8 ? "exr" : context_raw.format_type == texture_ldr_format_t.PNG ? "png" : "jpg";
  148. ui_files_show(filters, true, false, function (path: string) {
  149. context_raw.texture_export_path = path;
  150. ///if (krom_android || krom_ios)
  151. console_toast(tr("Exporting textures"));
  152. krom_g4_swap_buffers();
  153. ///end
  154. _box_export_bake_material = bake_material;
  155. app_notify_on_init(function () {
  156. ///if is_paint
  157. export_texture_run(context_raw.texture_export_path, _box_export_bake_material);
  158. ///end
  159. ///if is_lab
  160. export_texture_run(context_raw.texture_export_path);
  161. ///end
  162. });
  163. });
  164. }
  165. }
  166. if (ui.is_hovered) {
  167. zui_tooltip(tr("Export texture files") + " (" + map_get(config_keymap, "file_export_textures") + ")");
  168. }
  169. }
  170. }
  171. let _box_export_t: export_preset_texture_t;
  172. function box_export_tab_presets(ui: zui_t) {
  173. let tab_vertical: bool = config_raw.touch_ui;
  174. if (zui_tab(box_export_htab, tr("Presets"), tab_vertical)) {
  175. let row: f32[] = [3 / 5, 1 / 5, 1 / 5];
  176. zui_row(row);
  177. zui_combo(box_export_hpreset, box_export_files, tr("Preset"));
  178. if (box_export_hpreset.changed) {
  179. box_export_preset = null;
  180. }
  181. if (zui_button(tr("New"))) {
  182. ui_box_show_custom(function (ui: zui_t) {
  183. let tab_vertical: bool = config_raw.touch_ui;
  184. if (zui_tab(zui_handle(__ID__), tr("New Preset"), tab_vertical)) {
  185. let row: f32[] = [0.5, 0.5];
  186. zui_row(row);
  187. let h_preset: zui_handle_t = zui_handle(__ID__);
  188. if (h_preset.init) {
  189. h_preset.text = "new_preset";
  190. }
  191. let preset_name: string = zui_text_input(h_preset, tr("Name"));
  192. if (zui_button(tr("OK")) || ui.is_return_down) {
  193. box_export_new_preset(preset_name);
  194. box_export_fetch_presets();
  195. box_export_preset = null;
  196. box_export_hpreset.position = array_index_of(box_export_files, preset_name);
  197. ui_box_hide();
  198. box_export_htab.position = 1; // Presets
  199. box_export_show_textures();
  200. }
  201. }
  202. });
  203. }
  204. if (zui_button(tr("Import"))) {
  205. ui_files_show("json", false, false, function (path: string) {
  206. path = to_lower_case(path);
  207. if (ends_with(path, ".json")) {
  208. let filename: string = substring(path, string_last_index_of(path, path_sep) + 1, path.length);
  209. let dst_path: string = path_data() + path_sep + "export_presets" + path_sep + filename;
  210. file_copy(path, dst_path); // Copy to presets folder
  211. box_export_fetch_presets();
  212. box_export_preset = null;
  213. box_export_hpreset.position = array_index_of(box_export_files, substring(filename, 0, filename.length - 5)); // Strip .json
  214. console_info(tr("Preset imported:") + " " + filename);
  215. }
  216. else {
  217. console_error(strings_error1());
  218. }
  219. });
  220. }
  221. if (box_export_preset == null) {
  222. box_export_parse_preset();
  223. box_export_hpreset.children = null;
  224. }
  225. // Texture list
  226. zui_separator(10, false);
  227. row = [1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6, 1 / 6];
  228. zui_row(row);
  229. zui_text(tr("Texture"));
  230. zui_text(tr("R"));
  231. zui_text(tr("G"));
  232. zui_text(tr("B"));
  233. zui_text(tr("A"));
  234. zui_text(tr("Color Space"));
  235. ui.changed = false;
  236. for (let i: i32 = 0; i < box_export_preset.textures.length; ++i) {
  237. let t: export_preset_texture_t = box_export_preset.textures[i];
  238. zui_row(row);
  239. let htex: zui_handle_t = zui_nest(box_export_hpreset, i);
  240. htex.text = t.name;
  241. t.name = zui_text_input(htex);
  242. if (ui.is_hovered && ui.input_released_r) {
  243. _box_export_t = t;
  244. ui_menu_draw(function (ui: zui_t) {
  245. if (ui_menu_button(ui, tr("Delete"))) {
  246. array_remove(box_export_preset.textures, _box_export_t);
  247. box_export_save_preset();
  248. }
  249. }, 1);
  250. }
  251. let hr: zui_handle_t = zui_nest(htex, 0);
  252. hr.position = array_index_of(box_export_channels, t.channels[0]);
  253. let hg: zui_handle_t = zui_nest(htex, 1);
  254. hg.position = array_index_of(box_export_channels, t.channels[1]);
  255. let hb: zui_handle_t = zui_nest(htex, 2);
  256. hb.position = array_index_of(box_export_channels, t.channels[2]);
  257. let ha: zui_handle_t = zui_nest(htex, 3);
  258. ha.position = array_index_of(box_export_channels, t.channels[3]);
  259. zui_combo(hr, box_export_channels, tr("R"));
  260. if (hr.changed) {
  261. t.channels[0] = box_export_channels[hr.position];
  262. }
  263. zui_combo(hg, box_export_channels, tr("G"));
  264. if (hg.changed) {
  265. t.channels[1] = box_export_channels[hg.position];
  266. }
  267. zui_combo(hb, box_export_channels, tr("B"));
  268. if (hb.changed) {
  269. t.channels[2] = box_export_channels[hb.position];
  270. }
  271. zui_combo(ha, box_export_channels, tr("A"));
  272. if (ha.changed) {
  273. t.channels[3] = box_export_channels[ha.position];
  274. }
  275. let hspace: zui_handle_t = zui_nest(htex, 4);
  276. hspace.position = array_index_of(box_export_color_spaces, t.color_space);
  277. zui_combo(hspace, box_export_color_spaces, tr("Color Space"));
  278. if (hspace.changed) {
  279. t.color_space = box_export_color_spaces[hspace.position];
  280. }
  281. }
  282. if (ui.changed) {
  283. box_export_save_preset();
  284. }
  285. row = [1 / 8];
  286. zui_row(row);
  287. if (zui_button(tr("Add"))) {
  288. array_push(box_export_preset.textures, { name: "base", channels: ["base_r", "base_g", "base_b", "1.0"], color_space: "linear" });
  289. box_export_hpreset.children = null;
  290. box_export_save_preset();
  291. }
  292. }
  293. }
  294. ///end
  295. ///if is_paint
  296. function box_export_tab_atlases(ui: zui_t) {
  297. let tab_vertical: bool = config_raw.touch_ui;
  298. if (zui_tab(box_export_htab, tr("Atlases"), tab_vertical)) {
  299. if (project_atlas_objects == null || project_atlas_objects.length != project_paint_objects.length) {
  300. project_atlas_objects = [];
  301. project_atlas_names = [];
  302. for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
  303. array_push(project_atlas_objects, 0);
  304. array_push(project_atlas_names, tr("Atlas") + " " + (i + 1));
  305. }
  306. }
  307. for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
  308. let row: f32[] = [1 / 2, 1 / 2];
  309. zui_row(row);
  310. zui_text(project_paint_objects[i].base.name);
  311. let hatlas: zui_handle_t = zui_nest(zui_handle(__ID__), i);
  312. hatlas.position = project_atlas_objects[i];
  313. project_atlas_objects[i] = zui_combo(hatlas, project_atlas_names, tr("Atlas"));
  314. }
  315. }
  316. }
  317. ///end
  318. function box_export_show_mesh() {
  319. box_export_mesh_handle.position = context_raw.export_mesh_index;
  320. ui_box_show_custom(function (ui: zui_t) {
  321. let htab: zui_handle_t = zui_handle(__ID__);
  322. box_export_tab_export_mesh(ui, htab);
  323. });
  324. }
  325. function box_export_tab_export_mesh(ui: zui_t, htab: zui_handle_t) {
  326. let tab_vertical: bool = config_raw.touch_ui;
  327. if (zui_tab(htab, tr("Export Mesh"), tab_vertical)) {
  328. let row: f32[] = [1 / 2, 1 / 2];
  329. zui_row(row);
  330. let h_export_mesh_format: zui_handle_t = zui_handle(__ID__);
  331. if (h_export_mesh_format.init) {
  332. h_export_mesh_format.position = context_raw.export_mesh_format;
  333. }
  334. let export_mesh_format_combo: string[] = ["obj", "arm"];
  335. context_raw.export_mesh_format = zui_combo(h_export_mesh_format, export_mesh_format_combo, tr("Format"), true);
  336. let ar: string[] = [tr("All")];
  337. for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
  338. let p: mesh_object_t = project_paint_objects[i];
  339. array_push(ar, p.base.name);
  340. }
  341. zui_combo(box_export_mesh_handle, ar, tr("Meshes"), true);
  342. let apply_displacement: bool = zui_check(zui_handle(__ID__), tr("Apply Displacement"));
  343. let tris: i32 = 0;
  344. let pos: i32 = box_export_mesh_handle.position;
  345. let paint_objects: mesh_object_t[] = pos == 0 ? project_paint_objects : [project_paint_objects[pos - 1]];
  346. for (let i: i32 = 0; i < paint_objects.length; ++i) {
  347. let po: mesh_object_t = paint_objects[i];
  348. for (let i: i32 = 0; i < po.data.index_arrays.length; ++i) {
  349. let inda: index_array_t = po.data.index_arrays[i];
  350. tris += math_floor(inda.values.length / 3);
  351. }
  352. }
  353. zui_text(tris + " " + tr("triangles"));
  354. zui_row(row);
  355. if (zui_button(tr("Cancel"))) {
  356. ui_box_hide();
  357. }
  358. if (zui_button(tr("Export"))) {
  359. ui_box_hide();
  360. ui_files_show(context_raw.export_mesh_format == mesh_format_t.OBJ ? "obj" : "arm", true, false, function (path: string) {
  361. ///if (krom_android || krom_ios)
  362. let f: string = sys_title();
  363. ///else
  364. let f: string = ui_files_filename;
  365. ///end
  366. if (f == "") {
  367. f = tr("untitled");
  368. }
  369. ///if (krom_android || krom_ios)
  370. console_toast(tr("Exporting mesh"));
  371. krom_g4_swap_buffers();
  372. ///end
  373. export_mesh_run(path + path_sep + f, box_export_mesh_handle.position == 0 ? null : [project_paint_objects[box_export_mesh_handle.position - 1]], apply_displacement);
  374. });
  375. }
  376. }
  377. }
  378. ///if (is_paint || is_sculpt)
  379. function box_export_show_material() {
  380. ui_box_show_custom(function (ui: zui_t) {
  381. let htab: zui_handle_t = zui_handle(__ID__);
  382. let tab_vertical: bool = config_raw.touch_ui;
  383. if (zui_tab(htab, tr("Export Material"), tab_vertical)) {
  384. let h1: zui_handle_t = zui_handle(__ID__);
  385. let h2: zui_handle_t = zui_handle(__ID__);
  386. h1.selected = context_raw.pack_assets_on_export;
  387. h2.selected = context_raw.write_icon_on_export;
  388. context_raw.pack_assets_on_export = zui_check(h1, tr("Pack Assets"));
  389. context_raw.write_icon_on_export = zui_check(h2, tr("Export Icon"));
  390. let row: f32[] = [0.5, 0.5];
  391. zui_row(row);
  392. if (zui_button(tr("Cancel"))) {
  393. ui_box_hide();
  394. }
  395. if (zui_button(tr("Export"))) {
  396. ui_box_hide();
  397. ui_files_show("arm", true, false, function (path: string) {
  398. let f: string = ui_files_filename;
  399. if (f == "") {
  400. f = tr("untitled");
  401. }
  402. app_notify_on_init(function (path: string) {
  403. export_arm_run_material(path);
  404. }, path + path_sep + f);
  405. });
  406. }
  407. }
  408. });
  409. }
  410. function box_export_show_brush() {
  411. ui_box_show_custom(function (ui: zui_t) {
  412. let htab: zui_handle_t = zui_handle(__ID__);
  413. let tab_vertical: bool = config_raw.touch_ui;
  414. if (zui_tab(htab, tr("Export Brush"), tab_vertical)) {
  415. let h1: zui_handle_t = zui_handle(__ID__);
  416. let h2: zui_handle_t = zui_handle(__ID__);
  417. h1.selected = context_raw.pack_assets_on_export;
  418. h2.selected = context_raw.write_icon_on_export;
  419. context_raw.pack_assets_on_export = zui_check(h1, tr("Pack Assets"));
  420. context_raw.write_icon_on_export = zui_check(h2, tr("Export Icon"));
  421. let row: f32[] = [0.5, 0.5];
  422. zui_row(row);
  423. if (zui_button(tr("Cancel"))) {
  424. ui_box_hide();
  425. }
  426. if (zui_button(tr("Export"))) {
  427. ui_box_hide();
  428. ui_files_show("arm", true, false, function (path: string) {
  429. let f: string = ui_files_filename;
  430. if (f == "") f = tr("untitled");
  431. app_notify_on_init(function (path: string) {
  432. export_arm_run_brush(path);
  433. }, path + path_sep + f);
  434. });
  435. }
  436. }
  437. });
  438. }
  439. ///end
  440. ///if (is_paint || is_lab)
  441. function box_export_fetch_presets() {
  442. box_export_files = file_read_directory(path_data() + path_sep + "export_presets");
  443. for (let i: i32 = 0; i < box_export_files.length; ++i) {
  444. box_export_files[i] = substring(box_export_files[i], 0, box_export_files[i].length - 5); // Strip .json
  445. }
  446. }
  447. function box_export_parse_preset() {
  448. let file: string = "export_presets/" + box_export_files[box_export_hpreset.position] + ".json";
  449. let blob: buffer_t = data_get_blob(file);
  450. box_export_preset = json_parse(sys_buffer_to_string(blob));
  451. data_delete_blob("export_presets/" + file);
  452. }
  453. function box_export_new_preset(name: string) {
  454. let template: string =
  455. "{\
  456. \"textures\": [\
  457. { \"name\": \"base\", \"channels\": [\"base_r\", \"base_g\", \"base_b\", \"1.0\"], \"color_space\": \"linear\" }\
  458. ]\
  459. }\
  460. ";
  461. if (!ends_with(name, ".json")) {
  462. name += ".json";
  463. }
  464. let path: string = path_data() + path_sep + "export_presets" + path_sep + name;
  465. krom_file_save_bytes(path, sys_string_to_buffer(template));
  466. }
  467. function box_export_save_preset() {
  468. let name: string = box_export_files[box_export_hpreset.position];
  469. if (name == "generic") {
  470. return; // generic is const
  471. }
  472. let path: string = path_data() + path_sep + "export_presets" + path_sep + name + ".json";
  473. krom_file_save_bytes(path, sys_string_to_buffer(json_stringify(box_export_preset)));
  474. }
  475. ///end