box_export.ts 17 KB

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