texture_settings_dialog.vala 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*
  2. * Copyright (c) 2012-2026 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. namespace Crown
  6. {
  7. public class TextureSettingsDialog : Gtk.Window
  8. {
  9. public Project _project;
  10. public Database _database;
  11. public Database _texture_database;
  12. public Guid _texture_id;
  13. public PropertyGridSet _texture_set;
  14. public Gtk.ListStore _platforms_store;
  15. public Gtk.TreeView _platforms;
  16. public Gtk.Stack _stack;
  17. public bool _never_opened_before;
  18. public string _texture_path;
  19. // Input page.
  20. public InputResource _texture_name;
  21. public InputString _source;
  22. // Output page.
  23. public InputEnum _format;
  24. public InputBool _generate_mips;
  25. public InputDouble _mip_skip_smallest;
  26. public InputBool _normal_map;
  27. public InputBool _linear;
  28. public InputBool _premultiply_alpha;
  29. public Gtk.Box _box;
  30. public Gtk.EventControllerKey _controller_key;
  31. public Gtk.Button _cancel;
  32. public Gtk.Button _save;
  33. public Gtk.HeaderBar _header_bar;
  34. public signal void texture_saved();
  35. public void text_func(Gtk.CellLayout cell_layout, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
  36. {
  37. Value? platform;
  38. model.get_value(iter, 0, out platform);
  39. cell.set_property("text", ((TargetPlatform)platform).to_label());
  40. }
  41. public TextureSettingsDialog(Project project, Database database)
  42. {
  43. _project = project;
  44. _database = database;
  45. _texture_database = new Database(project);
  46. _texture_id = GUID_ZERO;
  47. create_object_types(_texture_database);
  48. _platforms_store = new Gtk.ListStore(1
  49. , typeof(TargetPlatform) // platform name
  50. );
  51. for (int p = 0; p < TargetPlatform.COUNT; ++p) {
  52. Gtk.TreeIter iter;
  53. _platforms_store.insert_with_values(out iter, -1, 0, (TargetPlatform)p, -1);
  54. }
  55. Gtk.CellRendererText text_renderer = new Gtk.CellRendererText();
  56. Gtk.TreeViewColumn column = new Gtk.TreeViewColumn.with_attributes("Target Platform", text_renderer, null);
  57. column.set_cell_data_func(text_renderer, text_func);
  58. _platforms = new Gtk.TreeView.with_model(_platforms_store);
  59. _platforms.append_column(column);
  60. _platforms.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
  61. _platforms.get_selection().changed.connect(on_platforms_selection_changed);
  62. this.set_icon_name(CROWN_EDITOR_ICON_NAME);
  63. _texture_set = new PropertyGridSet();
  64. _texture_path = "";
  65. // Input grid.
  66. _texture_name = new InputResource(OBJECT_TYPE_TEXTURE, database);
  67. _texture_name.value_changed.connect(on_texture_resource_value_changed);
  68. _source = new InputString();
  69. _source.sensitive = false;
  70. PropertyGrid cv;
  71. cv = new PropertyGrid();
  72. cv.column_homogeneous = true;
  73. cv.add_row("Name", _texture_name, "Resource name.");
  74. _texture_set.add_property_grid(cv, "Texture");
  75. cv = new PropertyGrid();
  76. cv.column_homogeneous = true;
  77. cv.add_row("Source", _source, "Source image.");
  78. _texture_set.add_property_grid(cv, "Input");
  79. // Output grid.
  80. string[] texture_formats = new string[TextureFormat.COUNT];
  81. for (int tf = 0; tf < TextureFormat.COUNT; ++tf)
  82. texture_formats[tf] = ((TextureFormat)tf).to_key();
  83. _format = new InputEnum(texture_formats[TextureFormat.BC1]
  84. , texture_formats
  85. , texture_formats
  86. );
  87. _format.value_changed.connect(on_format_value_changed);
  88. _generate_mips = new InputBool();
  89. _generate_mips.value = true;
  90. _generate_mips.value_changed.connect(on_generate_mips_value_changed);
  91. _mip_skip_smallest = new InputDouble(0, 0, 32);
  92. _mip_skip_smallest.value_changed.connect(on_mip_skip_smallest_value_changed);
  93. _normal_map = new InputBool();
  94. _normal_map.value = false;
  95. _normal_map.value_changed.connect(on_normal_map_value_changed);
  96. _linear = new InputBool();
  97. _linear.value = false;
  98. _linear.value_changed.connect(on_linear_value_changed);
  99. _premultiply_alpha = new InputBool();
  100. _premultiply_alpha.value = false;
  101. _premultiply_alpha.value_changed.connect(on_premultiply_alpha_value_changed);
  102. cv = new PropertyGrid();
  103. cv.column_homogeneous = true;
  104. cv.add_row("Format", _format, "Output format.");
  105. cv.add_row("Generate Mips", _generate_mips, "Generate mip-maps.");
  106. cv.add_row("Skip Smallest Mips", _mip_skip_smallest, "Skip generation of the N smallest mip-maps.");
  107. cv.add_row("Normal Map", _normal_map, "Skip gamma correction and mark as normal map.");
  108. cv.add_row("Linear", _linear, "Skip gamma correction.");
  109. cv.add_row("Premultiply Alpha", _premultiply_alpha, "Premultiply alpha into RGB channels.");
  110. _texture_set.add_property_grid(cv, "Output");
  111. _stack = new Gtk.Stack();
  112. _stack.add_named(new Gtk.Label("Select one or more platforms to change its settings"), "none-selected");
  113. _stack.add_named(_texture_set, "some-selected");
  114. _box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
  115. _box.pack_start(_platforms, false, true, 0);
  116. _box.pack_start(_stack, false, true, 0);
  117. _box.vexpand = true;
  118. this.add(_box);
  119. _controller_key = new Gtk.EventControllerKey(this);
  120. _controller_key.key_pressed.connect((keyval, keycode, state) => {
  121. if (keyval == Gdk.Key.Escape) {
  122. close();
  123. return Gdk.EVENT_STOP;
  124. }
  125. return Gdk.EVENT_PROPAGATE;
  126. });
  127. _cancel = new Gtk.Button.with_label("Cancel");
  128. _cancel.clicked.connect(() => {
  129. close();
  130. });
  131. _save = new Gtk.Button.with_label("Save & Reload");
  132. _save.get_style_context().add_class("suggested-action");
  133. _save.clicked.connect(() => {
  134. save();
  135. });
  136. _header_bar = new Gtk.HeaderBar();
  137. _header_bar.title = "Texture Settings";
  138. _header_bar.show_close_button = true;
  139. _header_bar.pack_start(_cancel);
  140. _header_bar.pack_end(_save);
  141. this.set_titlebar(_header_bar);
  142. _never_opened_before = true;
  143. _stack.map.connect(on_stack_map);
  144. this.delete_event.connect(on_delete_event);
  145. }
  146. public void on_stack_map()
  147. {
  148. if (_never_opened_before) {
  149. _never_opened_before = false;
  150. TargetPlatform host_platform = TargetPlatform.COUNT;
  151. #if CROWN_PLATFORM_WINDOWS
  152. host_platform = TargetPlatform.WINDOWS;
  153. #elif CROWN_PLATFORM_LINUX
  154. host_platform = TargetPlatform.LINUX;
  155. #endif
  156. if (host_platform == TargetPlatform.COUNT) {
  157. _platforms.get_selection().select_path(new Gtk.TreePath.first());
  158. } else {
  159. _platforms_store.foreach((model, path, iter) => {
  160. Value platform;
  161. model.get_value(iter, 0, out platform);
  162. if (((TargetPlatform)platform) == host_platform) {
  163. _platforms.get_selection().select_iter(iter);
  164. return true;
  165. }
  166. return false;
  167. });
  168. }
  169. }
  170. _platforms.grab_focus();
  171. }
  172. public int load_texture(string texture_name)
  173. {
  174. string new_texture_path = texture_name + ".texture";
  175. if (_texture_path != new_texture_path)
  176. save();
  177. _texture_database.reset();
  178. if (_texture_database.add_from_resource_path(out _texture_id, new_texture_path) != 0) {
  179. _texture_id = GUID_ZERO;
  180. return -1;
  181. }
  182. _texture_path = new_texture_path;
  183. on_platforms_selection_changed();
  184. return 0;
  185. }
  186. public void set_texture(string texture_name)
  187. {
  188. if (load_texture(texture_name) == 0)
  189. _texture_name.value = texture_name;
  190. }
  191. public void on_texture_resource_value_changed()
  192. {
  193. load_texture(_texture_name.value);
  194. }
  195. public bool are_values_equal(Value? a, Value? b)
  196. {
  197. if (a.type() != b.type())
  198. return false;
  199. if (a.holds(typeof(bool))) {
  200. return (bool)a == (bool)b;
  201. } else if (a.holds(typeof(double))) {
  202. return (double)a == (double)b;
  203. } else if (a.holds(typeof(string))) {
  204. return (string)a == (string)b;
  205. } else if (a == null && b == null) {
  206. return true;
  207. }
  208. return false;
  209. }
  210. public void on_platforms_selection_changed()
  211. {
  212. if (_texture_id == GUID_ZERO)
  213. return;
  214. if (_platforms.get_selection().count_selected_rows() > 0) {
  215. _stack.set_visible_child_full("some-selected", Gtk.StackTransitionType.NONE);
  216. } else {
  217. _stack.set_visible_child_full("none-selected", Gtk.StackTransitionType.NONE);
  218. return;
  219. }
  220. string property_names[] = { "source", "format", "generate_mips", "mip_skip_smallest", "normal_map", "linear", "premultiply_alpha" };
  221. InputField properties[] = { _source, _format, _generate_mips, _mip_skip_smallest, _normal_map, _linear, _premultiply_alpha };
  222. _format.value_changed.disconnect(on_format_value_changed);
  223. _generate_mips.value_changed.disconnect(on_generate_mips_value_changed);
  224. _mip_skip_smallest.value_changed.disconnect(on_mip_skip_smallest_value_changed);
  225. _normal_map.value_changed.disconnect(on_normal_map_value_changed);
  226. _linear.value_changed.disconnect(on_linear_value_changed);
  227. _premultiply_alpha.value_changed.disconnect(on_premultiply_alpha_value_changed);
  228. for (int i = 0; i < properties.length; ++i)
  229. properties[i].set_data("init", false);
  230. for (int i = 0; i < properties.length; ++i) {
  231. _platforms.get_selection().selected_foreach((model, path, iter) => {
  232. Value? platform;
  233. model.get_value(iter, 0, out platform);
  234. string key = platform_property(((TargetPlatform)platform).to_key(), property_names[i]);
  235. bool init = properties[i].get_data<bool>("init");
  236. // Try <platform>.<property> first. Fallback to <property>.
  237. if (!_texture_database.has_property(_texture_id, key))
  238. key = property_names[i];
  239. if (_texture_database.has_property(_texture_id, key)) {
  240. Value? val = _texture_database.get_property(_texture_id, key);
  241. if (!init) {
  242. properties[i].set_data("init", true);
  243. properties[i].set_union_value(val);
  244. properties[i].set_inconsistent(false);
  245. } else if (!are_values_equal(val, properties[i].union_value())) {
  246. properties[i].set_inconsistent(true);
  247. }
  248. } else {
  249. properties[i].set_inconsistent(true);
  250. if (!init) {
  251. properties[i].set_data("init", true);
  252. }
  253. }
  254. });
  255. }
  256. _format.value_changed.connect(on_format_value_changed);
  257. _generate_mips.value_changed.connect(on_generate_mips_value_changed);
  258. _mip_skip_smallest.value_changed.connect(on_mip_skip_smallest_value_changed);
  259. _normal_map.value_changed.connect(on_normal_map_value_changed);
  260. _linear.value_changed.connect(on_linear_value_changed);
  261. _premultiply_alpha.value_changed.connect(on_premultiply_alpha_value_changed);
  262. }
  263. public void on_format_value_changed()
  264. {
  265. on_property_value_changed("format", _format);
  266. }
  267. public void on_generate_mips_value_changed()
  268. {
  269. on_property_value_changed("generate_mips", _generate_mips);
  270. }
  271. public void on_mip_skip_smallest_value_changed()
  272. {
  273. on_property_value_changed("mip_skip_smallest", _mip_skip_smallest);
  274. }
  275. public void on_normal_map_value_changed()
  276. {
  277. on_property_value_changed("normal_map", _normal_map);
  278. }
  279. public void on_linear_value_changed()
  280. {
  281. on_property_value_changed("linear", _linear);
  282. }
  283. public void on_premultiply_alpha_value_changed()
  284. {
  285. on_property_value_changed("premultiply_alpha", _premultiply_alpha);
  286. }
  287. public void on_property_value_changed(string property_name, InputField property_value)
  288. {
  289. if (_texture_id == GUID_ZERO)
  290. return;
  291. Value val = property_value.union_value();
  292. // For backward compatibility.
  293. if (property_name == "generate_mips" || property_name == "normal_map") {
  294. if (_texture_database.has_property(_texture_id, property_name))
  295. _texture_database.set_property(_texture_id, property_name, val);
  296. }
  297. _platforms.get_selection().selected_foreach((model, path, iter) => {
  298. Value? platform;
  299. model.get_value(iter, 0, out platform);
  300. string key = platform_property(((TargetPlatform)platform).to_key(), property_name);
  301. _texture_database.set_property(_texture_id, key, val);
  302. });
  303. }
  304. public void save()
  305. {
  306. if (_texture_id == GUID_ZERO)
  307. return;
  308. if (_texture_database.dump(_project.absolute_path(_texture_path), _texture_id) == 0)
  309. texture_saved();
  310. }
  311. public string platform_property(string platform_name, string property)
  312. {
  313. return "output."
  314. + platform_name
  315. + "."
  316. + property
  317. ;
  318. }
  319. public bool on_delete_event(Gdk.EventAny event)
  320. {
  321. _texture_id = GUID_ZERO;
  322. return Gdk.EVENT_PROPAGATE;
  323. }
  324. }
  325. } /* namespace Crown */