project.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. let project_raw: project_format_t = {};
  2. let project_filepath: string = "";
  3. let project_assets: asset_t[] = [];
  4. let project_asset_names: string[] = [];
  5. let project_asset_id: i32 = 0;
  6. let project_mesh_assets: string[] = [];
  7. let project_material_groups: node_group_t[] = [];
  8. let project_paint_objects: mesh_object_t[] = null;
  9. let project_asset_map: map_t<i32, any> = map_create(); // image_t | font_t
  10. let project_mesh_list: string[] = null;
  11. ///if (is_paint || is_sculpt)
  12. let project_materials: slot_material_t[] = null;
  13. let project_brushes: slot_brush_t[] = null;
  14. let project_layers: slot_layer_t[] = null;
  15. let project_fonts: slot_font_t[] = null;
  16. let project_atlas_objects: i32[] = null;
  17. let project_atlas_names: string[] = null;
  18. ///end
  19. ///if is_lab
  20. let project_material_data: material_data_t = null; ////
  21. let project_materials: any[] = null; ////
  22. let project_nodes: zui_nodes_t;
  23. let project_canvas: zui_node_canvas_t;
  24. let project_default_canvas: buffer_t = null;
  25. ///end
  26. function project_open() {
  27. ui_files_show("arm", false, false, function (path: string) {
  28. if (!ends_with(path, ".arm")) {
  29. console_error(strings_error0());
  30. return;
  31. }
  32. let current: image_t = _g2_current;
  33. let g2_in_use: bool = _g2_in_use;
  34. if (g2_in_use) g2_end();
  35. import_arm_run_project(path);
  36. if (g2_in_use) g2_begin(current);
  37. });
  38. }
  39. let _project_save_and_quit: bool;
  40. function project_save(save_and_quit: bool = false) {
  41. if (project_filepath == "") {
  42. ///if krom_ios
  43. let document_directory: string = krom_save_dialog("", "");
  44. document_directory = substring(document_directory, 0, document_directory.length - 8); // Strip /"untitled"
  45. project_filepath = document_directory + "/" + sys_title() + ".arm";
  46. ///elseif krom_android
  47. project_filepath = krom_save_path() + "/" + sys_title() + ".arm";
  48. ///else
  49. project_save_as(save_and_quit);
  50. return;
  51. ///end
  52. }
  53. ///if (krom_windows || krom_linux || krom_darwin)
  54. let filename: string = substring(project_filepath, string_last_index_of(project_filepath, path_sep) + 1, project_filepath.length - 4);
  55. sys_title_set(filename + " - " + manifest_title);
  56. ///end
  57. _project_save_and_quit = save_and_quit;
  58. app_notify_on_init(function () {
  59. export_arm_run_project();
  60. if (_project_save_and_quit) {
  61. sys_stop();
  62. }
  63. });
  64. }
  65. function project_save_as(save_and_quit: bool = false) {
  66. _project_save_and_quit = save_and_quit;
  67. ui_files_show("arm", true, false, function (path: string) {
  68. let f: string = ui_files_filename;
  69. if (f == "") {
  70. f = tr("untitled");
  71. }
  72. project_filepath = path + path_sep + f;
  73. if (!ends_with(project_filepath, ".arm")) {
  74. project_filepath += ".arm";
  75. }
  76. project_save(_project_save_and_quit);
  77. });
  78. }
  79. function project_new_box() {
  80. ///if (is_paint || is_sculpt)
  81. ui_box_show_custom(function (ui: zui_t) {
  82. if (zui_tab(zui_handle(__ID__), tr("New Project"))) {
  83. if (project_mesh_list == null) {
  84. project_mesh_list = file_read_directory(path_data() + path_sep + "meshes");
  85. for (let i: i32 = 0; i < project_mesh_list.length; ++i) {
  86. project_mesh_list[i] = substring(project_mesh_list[i], 0, project_mesh_list[i].length - 4); // Trim .arm
  87. }
  88. project_mesh_list.unshift("plane");
  89. project_mesh_list.unshift("sphere");
  90. project_mesh_list.unshift("rounded_cube");
  91. }
  92. let row: f32[] = [0.5, 0.5];
  93. zui_row(row);
  94. let h_project_type: zui_handle_t = zui_handle(__ID__);
  95. if (h_project_type.init) {
  96. h_project_type.position = context_raw.project_type;
  97. }
  98. context_raw.project_type = zui_combo(h_project_type, project_mesh_list, tr("Template"), true);
  99. let h_project_aspect_ratio: zui_handle_t = zui_handle(__ID__);
  100. if (h_project_aspect_ratio.init) {
  101. h_project_aspect_ratio.position = context_raw.project_aspect_ratio;
  102. }
  103. let project_aspect_ratio_combo: string[] = ["1:1", "2:1", "1:2"];
  104. context_raw.project_aspect_ratio = zui_combo(h_project_aspect_ratio, project_aspect_ratio_combo, tr("Aspect Ratio"), true);
  105. zui_end_element();
  106. zui_row(row);
  107. if (zui_button(tr("Cancel"))) {
  108. ui_box_hide();
  109. }
  110. if (zui_button(tr("OK")) || ui.is_return_down) {
  111. project_new();
  112. viewport_scale_to_bounds();
  113. ui_box_hide();
  114. }
  115. }
  116. });
  117. ///end
  118. ///if is_lab
  119. project_new();
  120. viewport_scale_to_bounds();
  121. ///end
  122. }
  123. function project_new(reset_layers: bool = true) {
  124. ///if (krom_windows || krom_linux || krom_darwin)
  125. sys_title_set(manifest_title);
  126. ///end
  127. project_filepath = "";
  128. ///if (is_paint || is_sculpt)
  129. if (context_raw.merged_object != null) {
  130. mesh_object_remove(context_raw.merged_object);
  131. data_delete_mesh(context_raw.merged_object.data._.handle);
  132. context_raw.merged_object = null;
  133. }
  134. context_raw.layer_preview_dirty = true;
  135. context_raw.layer_filter = 0;
  136. project_mesh_assets = [];
  137. ///end
  138. viewport_reset();
  139. context_raw.paint_object = context_main_object();
  140. context_select_paint_object(context_main_object());
  141. for (let i: i32 = 1; i < project_paint_objects.length; ++i) {
  142. let p: mesh_object_t = project_paint_objects[i];
  143. if (p == context_raw.paint_object) {
  144. continue;
  145. }
  146. data_delete_mesh(p.data._.handle);
  147. mesh_object_remove(p);
  148. }
  149. let meshes: mesh_object_t[] = scene_meshes;
  150. let len: i32 = meshes.length;
  151. for (let i: i32 = 0; i < len; ++i) {
  152. let m: mesh_object_t = meshes[len - i - 1];
  153. if (array_index_of(context_raw.project_objects, m) == -1 &&
  154. m.base.name != ".ParticleEmitter" &&
  155. m.base.name != ".Particle") {
  156. data_delete_mesh(m.data._.handle);
  157. mesh_object_remove(m);
  158. }
  159. }
  160. let handle: string = context_raw.paint_object.data._.handle;
  161. if (handle != "SceneSphere" && handle != "ScenePlane") {
  162. data_delete_mesh(handle);
  163. }
  164. if (context_raw.project_type != project_model_t.ROUNDED_CUBE) {
  165. let raw: mesh_data_t = null;
  166. if (context_raw.project_type == project_model_t.SPHERE || context_raw.project_type == project_model_t.TESSELLATED_PLANE) {
  167. let mesh: any = context_raw.project_type == project_model_t.SPHERE ?
  168. geom_make_uv_sphere(1, 512, 256) :
  169. geom_make_plane(1, 1, 512, 512);
  170. mesh.name = "Tessellated";
  171. raw = import_mesh_raw_mesh(mesh);
  172. ///if is_sculpt
  173. app_notify_on_next_frame(function (mesh: any) {
  174. let f32a: f32_array_t = f32_array_create(config_get_texture_res_x() * config_get_texture_res_y() * 4);
  175. for (let i: i32 = 0; i < math_floor(mesh.inda.length); ++i) {
  176. let index: i32 = mesh.inda[i];
  177. f32a[i * 4] = mesh.posa[index * 4] / 32767;
  178. f32a[i * 4 + 1] = mesh.posa[index * 4 + 1] / 32767;
  179. f32a[i * 4 + 2] = mesh.posa[index * 4 + 2] / 32767;
  180. f32a[i * 4 + 3] = 1.0;
  181. }
  182. let imgmesh: image_t = image_from_bytes(f32a.buffer, config_get_texture_res_x(), config_get_texture_res_y(), tex_format_t.RGBA128);
  183. let texpaint: image_t = project_layers[0].texpaint;
  184. g2_begin(texpaint);
  185. g2_set_pipeline(base_pipe_copy128);
  186. g2_draw_scaled_image(imgmesh, 0, 0, config_get_texture_res_x(), config_get_texture_res_y());
  187. g2_set_pipeline(null);
  188. g2_end();
  189. }, mesh);
  190. ///end
  191. }
  192. else {
  193. let b: buffer_t = data_get_blob("meshes/" + project_mesh_list[context_raw.project_type] + ".arm");
  194. raw = armpack_decode(b).mesh_datas[0];
  195. }
  196. let md: mesh_data_t = mesh_data_create(raw);
  197. map_set(data_cached_meshes, "SceneTessellated", md);
  198. if (context_raw.project_type == project_model_t.TESSELLATED_PLANE) {
  199. viewport_set_view(0, 0, 0.75, 0, 0, 0); // Top
  200. }
  201. }
  202. let n: string = context_raw.project_type == project_model_t.ROUNDED_CUBE ? ".Cube" : "Tessellated";
  203. let md: mesh_data_t = data_get_mesh("Scene", n);
  204. let current: image_t = _g2_current;
  205. let g2_in_use: bool = _g2_in_use;
  206. if (g2_in_use) g2_end();
  207. ///if is_paint
  208. context_raw.picker_mask_handle.position = picker_mask_t.NONE;
  209. ///end
  210. mesh_object_set_data(context_raw.paint_object, md);
  211. vec4_set(context_raw.paint_object.base.transform.scale, 1, 1, 1);
  212. transform_build_matrix(context_raw.paint_object.base.transform);
  213. context_raw.paint_object.base.name = n;
  214. project_paint_objects = [context_raw.paint_object];
  215. ///if (is_paint || is_sculpt)
  216. while (project_materials.length > 0) {
  217. slot_material_unload(project_materials.pop());
  218. }
  219. ///end
  220. let m: material_data_t = data_get_material("Scene", "Material");
  221. ///if (is_paint || is_sculpt)
  222. array_push(project_materials, slot_material_create(m));
  223. ///end
  224. ///if is_lab
  225. project_material_data = m;
  226. ///end
  227. ///if (is_paint || is_sculpt)
  228. context_raw.material = project_materials[0];
  229. ///end
  230. ui_nodes_hwnd.redraws = 2;
  231. ui_nodes_group_stack = [];
  232. project_material_groups = [];
  233. ///if (is_paint || is_sculpt)
  234. project_brushes = [slot_brush_create()];
  235. context_raw.brush = project_brushes[0];
  236. project_fonts = [slot_font_create("default.ttf", base_font)];
  237. context_raw.font = project_fonts[0];
  238. ///end
  239. project_set_default_swatches();
  240. context_raw.swatch = project_raw.swatches[0];
  241. context_raw.picked_color = make_swatch();
  242. context_raw.color_picker_callback = null;
  243. history_reset();
  244. make_material_parse_paint_material();
  245. ///if (is_paint || is_sculpt)
  246. util_render_make_material_preview();
  247. ///end
  248. for (let i: i32 = 0; i < project_assets.length; ++i) {
  249. let a: asset_t = project_assets[i];
  250. data_delete_image(a.file);
  251. }
  252. project_assets = [];
  253. project_asset_names = [];
  254. project_asset_map = map_create();
  255. project_asset_id = 0;
  256. project_raw.packed_assets = [];
  257. context_raw.ddirty = 4;
  258. ///if (is_paint || is_sculpt)
  259. ui_base_hwnds[tab_area_t.SIDEBAR0].redraws = 2;
  260. ui_base_hwnds[tab_area_t.SIDEBAR1].redraws = 2;
  261. ///end
  262. if (reset_layers) {
  263. ///if (is_paint || is_sculpt)
  264. let aspect_ratio_changed: bool = project_layers[0].texpaint.width != config_get_texture_res_x() || project_layers[0].texpaint.height != config_get_texture_res_y();
  265. while (project_layers.length > 0) {
  266. slot_layer_unload(project_layers.pop());
  267. }
  268. let layer: slot_layer_t = slot_layer_create();
  269. array_push(project_layers, layer);
  270. context_set_layer(layer);
  271. if (aspect_ratio_changed) {
  272. app_notify_on_init(base_resize_layers);
  273. }
  274. ///end
  275. app_notify_on_init(base_init_layers);
  276. }
  277. if (g2_in_use) g2_begin(current);
  278. context_raw.saved_envmap = null;
  279. context_raw.envmap_loaded = false;
  280. scene_world._.envmap = context_raw.empty_envmap;
  281. scene_world.envmap = "World_radiance.k";
  282. context_raw.show_envmap_handle.selected = context_raw.show_envmap = false;
  283. scene_world._.radiance = context_raw.default_radiance;
  284. scene_world._.radiance_mipmaps = context_raw.default_radiance_mipmaps;
  285. scene_world._.irradiance = context_raw.default_irradiance;
  286. scene_world.strength = 4.0;
  287. ///if (is_paint || is_sculpt)
  288. context_init_tool();
  289. ///end
  290. ///if (krom_direct3d12 || krom_vulkan || krom_metal)
  291. render_path_raytrace_ready = false;
  292. ///end
  293. }
  294. ///if (is_paint || is_sculpt)
  295. function project_import_material() {
  296. ui_files_show("arm,blend", false, true, function (path: string) {
  297. ends_with(path, ".blend") ?
  298. import_blend_material_run(path) :
  299. import_arm_run_material(path);
  300. });
  301. }
  302. function project_import_brush() {
  303. ui_files_show("arm," + path_texture_formats.join(","), false, true, function (path: string) {
  304. // Create brush from texture
  305. if (path_is_texture(path)) {
  306. // Import texture
  307. import_asset_run(path);
  308. let asset_index: i32 = 0;
  309. for (let i: i32 = 0; i < project_assets.length; ++i) {
  310. if (project_assets[i].file == path) {
  311. asset_index = i;
  312. break;
  313. }
  314. }
  315. // Create a new brush
  316. context_raw.brush = slot_brush_create();
  317. array_push(project_brushes, context_raw.brush);
  318. // Create and link image node
  319. let n: zui_node_t = nodes_brush_create_node("TEX_IMAGE");
  320. n.x = 83;
  321. n.y = 340;
  322. n.buttons[0].default_value = f32_array_create_x(asset_index);
  323. let links: zui_node_link_t[] = context_raw.brush.canvas.links;
  324. array_push(links, {
  325. id: zui_get_link_id(links),
  326. from_id: n.id,
  327. from_socket: 0,
  328. to_id: 0,
  329. to_socket: 4
  330. });
  331. // Parse brush
  332. make_material_parse_brush();
  333. ui_nodes_hwnd.redraws = 2;
  334. app_notify_on_init(util_render_make_brush_preview);
  335. }
  336. // Import from project file
  337. else {
  338. import_arm_run_brush(path);
  339. }
  340. });
  341. }
  342. ///end
  343. let _project_import_mesh_replace_existing: bool;
  344. let _project_import_mesh_done: ()=>void;
  345. function project_import_mesh(replace_existing: bool = true, done: ()=>void = null) {
  346. _project_import_mesh_replace_existing = replace_existing;
  347. _project_import_mesh_done = done;
  348. ui_files_show(path_mesh_formats.join(","), false, false, function (path: string) {
  349. project_import_mesh_box(path, _project_import_mesh_replace_existing, true, _project_import_mesh_done);
  350. });
  351. }
  352. let _project_import_mesh_box_path: string;
  353. let _project_import_mesh_box_replace_existing: bool;
  354. let _project_import_mesh_box_clear_layers: bool;
  355. let _project_import_mesh_box_done: ()=>void;
  356. function project_import_mesh_box(path: string, replace_existing: bool = true, clear_layers: bool = true, done: ()=>void = null) {
  357. _project_import_mesh_box_path = path;
  358. _project_import_mesh_box_replace_existing = replace_existing;
  359. _project_import_mesh_box_clear_layers = clear_layers;
  360. _project_import_mesh_box_done = done;
  361. ///if krom_ios
  362. // Import immediately while access to resource is unlocked
  363. // data_get_blob(path);
  364. ///end
  365. ui_box_show_custom(function (ui: zui_t) {
  366. let path: string = _project_import_mesh_box_path;
  367. let replace_existing: bool = _project_import_mesh_box_replace_existing;
  368. let clear_layers: bool = _project_import_mesh_box_clear_layers;
  369. let done: ()=>void = _project_import_mesh_box_done;
  370. let tab_vertical: bool = config_raw.touch_ui;
  371. if (zui_tab(zui_handle(__ID__), tr("Import Mesh"), tab_vertical)) {
  372. if (ends_with(to_lower_case(path), ".obj")) {
  373. let split_by_combo: string[] = [tr("Object"), tr("Group"), tr("Material"), tr("UDIM Tile")];
  374. context_raw.split_by = zui_combo(zui_handle(__ID__), split_by_combo, tr("Split By"), true);
  375. if (ui.is_hovered) {
  376. zui_tooltip(tr("Split .obj mesh into objects"));
  377. }
  378. }
  379. // if (ends_with(to_lower_case(path), ".fbx")) {
  380. // raw.parseTransform = Zui.check(Zui.handle("project_5", { selected: raw.parseTransform }), tr("Parse Transforms"));
  381. // if (ui.isHovered) Zui.tooltip(tr("Load per-object transforms from .fbx"));
  382. // }
  383. ///if (is_paint || is_sculpt)
  384. // if (ends_with(to_lower_case(path), ".fbx") || ends_with(to_lower_case(path), ".blend")) {
  385. if (ends_with(to_lower_case(path), ".blend")) {
  386. let h: zui_handle_t = zui_handle(__ID__);
  387. if (h.init) {
  388. h.selected = context_raw.parse_vcols;
  389. }
  390. context_raw.parse_vcols = zui_check(h, tr("Parse Vertex Colors"));
  391. if (ui.is_hovered) {
  392. zui_tooltip(tr("Import vertex color data"));
  393. }
  394. }
  395. ///end
  396. let row: f32 [] = [0.45, 0.45, 0.1];
  397. zui_row(row);
  398. if (zui_button(tr("Cancel"))) {
  399. ui_box_hide();
  400. }
  401. if (zui_button(tr("Import")) || ui.is_return_down) {
  402. ui_box_hide();
  403. ///if (krom_android || krom_ios)
  404. console_toast(tr("Importing mesh"));
  405. krom_g4_swap_buffers();
  406. ///end
  407. ///if (is_paint || is_sculpt)
  408. import_mesh_run(path, clear_layers, replace_existing);
  409. ///end
  410. ///if is_lab
  411. import_mesh_run(path, replace_existing);
  412. ///end
  413. if (done != null) {
  414. done();
  415. }
  416. }
  417. if (zui_button(tr("?"))) {
  418. file_load_url("https://github.com/armory3d/armorpaint_docs/blob/master/faq.md");
  419. }
  420. }
  421. });
  422. ui_box_click_to_hide = false; // Prevent closing when going back to window from file browser
  423. }
  424. function project_reimport_mesh() {
  425. if (project_mesh_assets != null && project_mesh_assets.length > 0 && file_exists(project_mesh_assets[0])) {
  426. project_import_mesh_box(project_mesh_assets[0], true, false);
  427. }
  428. else {
  429. project_import_asset();
  430. }
  431. }
  432. let _project_unwrap_mesh_box_mesh: any;
  433. let _project_unwrap_mesh_box_done: (a: any)=>void;
  434. let _project_unwrap_mesh_box_skip_ui: bool;
  435. function project_unwrap_mesh_box(mesh: any, done: (a: any)=>void, skip_ui: bool = false) {
  436. _project_unwrap_mesh_box_mesh = mesh;
  437. _project_unwrap_mesh_box_done = done;
  438. _project_unwrap_mesh_box_skip_ui = skip_ui;
  439. ui_box_show_custom(function (ui: zui_t) {
  440. let mesh: any = _project_unwrap_mesh_box_mesh;
  441. let done: (a: any)=>void = _project_unwrap_mesh_box_done;
  442. let skip_ui: bool = _project_unwrap_mesh_box_skip_ui;
  443. let tab_vertical: bool = config_raw.touch_ui;
  444. if (zui_tab(zui_handle(__ID__), tr("Unwrap Mesh"), tab_vertical)) {
  445. let unwrap_plugins: string[] = [];
  446. if (box_preferences_files_plugin == null) {
  447. box_preferences_fetch_plugins();
  448. }
  449. for (let i: i32 = 0; i < box_preferences_files_plugin.length; ++i) {
  450. let f: string = box_preferences_files_plugin[i];
  451. if (string_index_of(f, "uv_unwrap") >= 0 && ends_with(f, ".js")) {
  452. array_push(unwrap_plugins, f);
  453. }
  454. }
  455. array_push(unwrap_plugins, "equirect");
  456. let unwrap_by: i32 = zui_combo(zui_handle(__ID__), unwrap_plugins, tr("Plugin"), true);
  457. let row: f32[] = [0.5, 0.5];
  458. zui_row(row);
  459. if (zui_button(tr("Cancel"))) {
  460. ui_box_hide();
  461. }
  462. if (zui_button(tr("Unwrap")) || ui.is_return_down || skip_ui) {
  463. ui_box_hide();
  464. ///if (krom_android || krom_ios)
  465. console_toast(tr("Unwrapping mesh"));
  466. krom_g4_swap_buffers();
  467. ///end
  468. if (unwrap_by == unwrap_plugins.length - 1) {
  469. util_mesh_equirect_unwrap(mesh);
  470. }
  471. else {
  472. let f: string = unwrap_plugins[unwrap_by];
  473. if (array_index_of(config_raw.plugins, f) == -1) {
  474. config_enable_plugin(f);
  475. console_info(f + " " + tr("plugin enabled"));
  476. }
  477. map_get(util_mesh_unwrappers, f)(mesh);
  478. }
  479. done(mesh);
  480. }
  481. }
  482. });
  483. }
  484. let _project_import_asset_hdr_as_envmap: bool;
  485. function project_import_asset(filters: string = null, hdr_as_envmap: bool = true) {
  486. if (filters == null) {
  487. filters = path_texture_formats.join(",") + "," + path_mesh_formats.join(",");
  488. }
  489. _project_import_asset_hdr_as_envmap = hdr_as_envmap;
  490. ui_files_show(filters, false, true, function (path: string) {
  491. import_asset_run(path, -1.0, -1.0, true, _project_import_asset_hdr_as_envmap);
  492. });
  493. }
  494. let _project_import_swatches_replace_existing: bool;
  495. function project_import_swatches(replace_existing: bool = false) {
  496. _project_import_swatches_replace_existing = replace_existing;
  497. ui_files_show("arm,gpl", false, false, function (path: string) {
  498. if (path_is_gimp_color_palette(path)) {
  499. import_gpl_run(path, _project_import_swatches_replace_existing);
  500. }
  501. else {
  502. import_arm_run_swatches(path, _project_import_swatches_replace_existing);
  503. }
  504. });
  505. }
  506. function project_reimport_textures() {
  507. for (let i: i32 = 0; i < project_assets.length; ++i) {
  508. let asset: asset_t = project_assets[i];
  509. project_reimport_texture(asset);
  510. }
  511. }
  512. function project_reimport_texture_load(path: string, asset: asset_t) {
  513. asset.file = path;
  514. let i: i32 = array_index_of(project_assets, asset);
  515. data_delete_image(asset.file);
  516. map_delete(project_asset_map, asset.id);
  517. let old_asset: asset_t = project_assets[i];
  518. array_splice(project_assets, i, 1);
  519. array_splice(project_asset_names, i, 1);
  520. import_texture_run(asset.file);
  521. array_insert(project_assets, i, project_assets.pop());
  522. array_insert(project_asset_names, i, project_asset_names.pop());
  523. ///if (is_paint || is_sculpt)
  524. if (context_raw.texture == old_asset) {
  525. context_raw.texture = project_assets[i];
  526. }
  527. ///end
  528. app_notify_on_next_frame(function () {
  529. make_material_parse_paint_material();
  530. ///if (is_paint || is_sculpt)
  531. util_render_make_material_preview();
  532. ui_base_hwnds[tab_area_t.SIDEBAR1].redraws = 2;
  533. ///end
  534. });
  535. }
  536. let _project_reimport_texture_asset: asset_t;
  537. function project_reimport_texture(asset: asset_t) {
  538. if (!file_exists(asset.file)) {
  539. let filters: string = path_texture_formats.join(",");
  540. _project_reimport_texture_asset = asset;
  541. ui_files_show(filters, false, false, function (path: string) {
  542. project_reimport_texture_load(path, _project_reimport_texture_asset);
  543. });
  544. }
  545. else {
  546. project_reimport_texture_load(asset.file, asset);
  547. }
  548. }
  549. function project_get_image(asset: asset_t): image_t {
  550. return asset != null ? map_get(project_asset_map, asset.id) : null;
  551. }
  552. ///if (is_paint || is_sculpt)
  553. function project_get_used_atlases(): string[] {
  554. if (project_atlas_objects == null) return null;
  555. let used: i32[] = [];
  556. for (let i: i32 = 0; i < project_atlas_objects.length; ++i) {
  557. let ao: i32 = project_atlas_objects[i];
  558. if (array_index_of(used, ao) == -1) {
  559. array_push(used, ao);
  560. }
  561. }
  562. if (used.length > 1) {
  563. let res: string[] = [];
  564. for (let i: i32 = 0; i < used.length; ++i) {
  565. let u: i32 = used[i];
  566. array_push(res, project_atlas_names[u]);
  567. }
  568. return res;
  569. }
  570. else return null;
  571. }
  572. function project_is_atlas_object(p: mesh_object_t): bool {
  573. if (context_raw.layer_filter <= project_paint_objects.length) {
  574. return false;
  575. }
  576. let atlas_name: string = project_get_used_atlases()[context_raw.layer_filter - project_paint_objects.length - 1];
  577. let atlas_i: i32 = array_index_of(project_atlas_names, atlas_name);
  578. return atlas_i == project_atlas_objects[array_index_of(project_paint_objects, p)];
  579. }
  580. function project_get_atlas_objects(object_mask: i32): mesh_object_t[] {
  581. let atlas_name: string = project_get_used_atlases()[object_mask - project_paint_objects.length - 1];
  582. let atlas_i: i32 = array_index_of(project_atlas_names, atlas_name);
  583. let visibles: mesh_object_t[] = [];
  584. for (let i: i32 = 0; i < project_paint_objects.length; ++i) {
  585. if (project_atlas_objects[i] == atlas_i) {
  586. array_push(visibles, project_paint_objects[i]);
  587. }
  588. }
  589. return visibles;
  590. }
  591. ///end
  592. function project_packed_asset_exists(packed_assets: packed_asset_t[], name: string): bool {
  593. for (let i: i32 = 0; i < packed_assets.length; ++i) {
  594. let pa: packed_asset_t = packed_assets[i];
  595. if (pa.name == name) {
  596. return true;
  597. }
  598. }
  599. return false;
  600. }
  601. function project_export_swatches() {
  602. ui_files_show("arm,gpl", true, false, function (path: string) {
  603. let f: string = ui_files_filename;
  604. if (f == "") {
  605. f = tr("untitled");
  606. }
  607. if (path_is_gimp_color_palette(f)) {
  608. export_gpl_run(path + path_sep + f, substring(f, 0, string_last_index_of(f, ".")), project_raw.swatches);
  609. }
  610. else {
  611. export_arm_run_swatches(path + path_sep + f);
  612. }
  613. });
  614. }
  615. function make_swatch(base: i32 = 0xffffffff): swatch_color_t {
  616. return { base: base, opacity: 1.0, occlusion: 1.0, roughness: 0.0, metallic: 0.0, normal: 0xff8080ff, emission: 0.0, height: 0.0, subsurface: 0.0 };
  617. }
  618. function project_clone_swatch(swatch: swatch_color_t): swatch_color_t {
  619. return { base: swatch.base, opacity: swatch.opacity, occlusion: swatch.occlusion, roughness: swatch.roughness, metallic: swatch.metallic, normal: swatch.normal, emission: swatch.emission, height: swatch.height, subsurface: swatch.subsurface };
  620. }
  621. function project_set_default_swatches() {
  622. // 32-Color Palette by Andrew Kensler
  623. // http://eastfarthing.com/blog/2016-05-06-palette/
  624. project_raw.swatches = [];
  625. let colors: i32[] = [0xffffffff, 0xff000000, 0xffd6a090, 0xffa12c32, 0xfffa2f7a, 0xfffb9fda, 0xffe61cf7, 0xff992f7c, 0xff47011f, 0xff051155, 0xff4f02ec, 0xff2d69cb, 0xff00a6ee, 0xff6febff, 0xff08a29a, 0xff2a666a, 0xff063619, 0xff4a4957, 0xff8e7ba4, 0xffb7c0ff, 0xffacbe9c, 0xff827c70, 0xff5a3b1c, 0xffae6507, 0xfff7aa30, 0xfff4ea5c, 0xff9b9500, 0xff566204, 0xff11963b, 0xff51e113, 0xff08fdcc];
  626. for (let i: i32 = 0; i < colors.length; ++i) {
  627. let c: i32 = colors[i];
  628. array_push(project_raw.swatches, make_swatch(c));
  629. }
  630. }
  631. function project_get_material_group_by_name(group_name: string): node_group_t {
  632. for (let i: i32 = 0; i < project_material_groups.length; ++i) {
  633. let g: node_group_t = project_material_groups[i];
  634. if (g.canvas.name == group_name) {
  635. return g;
  636. }
  637. }
  638. return null;
  639. }
  640. ///if (is_paint || is_sculpt)
  641. function project_is_material_group_in_use(group: node_group_t): bool {
  642. let canvases: zui_node_canvas_t[] = [];
  643. for (let i: i32 = 0; i < project_materials.length; ++i) {
  644. let m: slot_material_t = project_materials[i];
  645. array_push(canvases, m.canvas);
  646. }
  647. for (let i: i32 = 0; i < project_material_groups.length; ++i) {
  648. let m: node_group_t = project_material_groups[i];
  649. array_push(canvases, m.canvas);
  650. }
  651. for (let i: i32 = 0; i < canvases.length; ++i) {
  652. let canvas: zui_node_canvas_t = canvases[i];
  653. for (let i: i32 = 0; i < canvas.nodes.length; ++i) {
  654. let n: zui_node_t = canvas.nodes[i];
  655. if (n.type == "GROUP" && n.name == group.canvas.name) {
  656. return true;
  657. }
  658. }
  659. }
  660. return false;
  661. }
  662. ///end
  663. type node_group_t = {
  664. nodes?: zui_nodes_t;
  665. canvas?: zui_node_canvas_t;
  666. };
  667. type project_format_t = {
  668. version?: string;
  669. assets?: string[]; // texture_assets
  670. is_bgra?: bool; // Swapped red and blue channels for layer textures
  671. packed_assets?: packed_asset_t[];
  672. envmap?: string; // Asset name
  673. envmap_strength?: f32;
  674. camera_world?: f32_array_t;
  675. camera_origin?: f32_array_t;
  676. camera_fov?: f32;
  677. swatches?: swatch_color_t[];
  678. ///if (is_paint || is_sculpt)
  679. brush_nodes?: zui_node_canvas_t[];
  680. brush_icons?: buffer_t[];
  681. material_nodes?: zui_node_canvas_t[];
  682. material_groups?: zui_node_canvas_t[];
  683. material_icons?: buffer_t[];
  684. font_assets?: string[];
  685. layer_datas?: layer_data_t[];
  686. mesh_datas?: mesh_data_t[];
  687. mesh_assets?: string[];
  688. mesh_icons?: buffer_t[];
  689. ///end
  690. ///if is_paint
  691. atlas_objects?: i32[];
  692. atlas_names?: string[];
  693. ///end
  694. ///if is_lab
  695. material?: zui_node_canvas_t;
  696. material_groups?: zui_node_canvas_t[];
  697. mesh_data?: mesh_data_t;
  698. mesh_icon?: buffer_t;
  699. ///end
  700. };
  701. type asset_t = {
  702. id?: i32;
  703. name?: string;
  704. file?: string;
  705. };
  706. type packed_asset_t = {
  707. name?: string;
  708. bytes?: buffer_t;
  709. };
  710. type swatch_color_t = {
  711. base?: color_t;
  712. opacity?: f32;
  713. occlusion?: f32;
  714. roughness?: f32;
  715. metallic?: f32;
  716. normal?: color_t;
  717. emission?: f32;
  718. height?: f32;
  719. subsurface?: f32;
  720. };
  721. ///if (is_paint || is_sculpt)
  722. type layer_data_t = {
  723. name?: string;
  724. res?: i32; // Width pixels
  725. bpp?: i32; // Bits per pixel
  726. texpaint?: buffer_t;
  727. uv_scale?: f32;
  728. uv_rot?: f32;
  729. uv_type?: i32;
  730. decal_mat?: f32_array_t;
  731. opacity_mask?: f32;
  732. fill_layer?: i32;
  733. object_mask?: i32;
  734. blending?: i32;
  735. parent?: i32;
  736. visible?: bool;
  737. ///if is_paint
  738. texpaint_nor?: buffer_t;
  739. texpaint_pack?: buffer_t;
  740. paint_base?: bool;
  741. paint_opac?: bool;
  742. paint_occ?: bool;
  743. paint_rough?: bool;
  744. paint_met?: bool;
  745. paint_nor?: bool;
  746. paint_nor_blend?: bool;
  747. paint_height?: bool;
  748. paint_height_blend?: bool;
  749. paint_emis?: bool;
  750. paint_subs?: bool;
  751. ///end
  752. };
  753. ///end