box_export.ts 17 KB

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