project.ts 25 KB

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