project.ts 26 KB

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