project.ts 25 KB

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