Project.hx 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. package arm;
  2. import kha.System;
  3. import kha.Window;
  4. import zui.Zui;
  5. import zui.Id;
  6. import zui.Nodes;
  7. import iron.data.SceneFormat;
  8. import iron.data.MeshData;
  9. import iron.data.Data;
  10. import iron.object.MeshObject;
  11. import iron.Scene;
  12. import arm.util.RenderUtil;
  13. import arm.util.ViewportUtil;
  14. import arm.sys.Path;
  15. import arm.ui.UITrait;
  16. import arm.ui.UIFiles;
  17. import arm.ui.UIBox;
  18. import arm.data.LayerSlot;
  19. import arm.data.BrushSlot;
  20. import arm.data.MaterialSlot;
  21. import arm.node.MaterialParser;
  22. import arm.io.ImportAsset;
  23. import arm.io.ImportArm;
  24. import arm.io.ImportBlend;
  25. import arm.io.ImportMesh;
  26. import arm.io.ExportArm;
  27. import arm.Tool;
  28. using StringTools;
  29. class Project {
  30. public static var raw: TProjectFormat;
  31. public static var filepath = "";
  32. public static var assets: Array<TAsset> = [];
  33. public static var assetNames: Array<String> = [];
  34. public static var assetId = 0;
  35. public static var meshAssets: Array<String> = [];
  36. public static var materials: Array<MaterialSlot> = null;
  37. public static var materialsScene: Array<MaterialSlot> = null;
  38. public static var brushes: Array<BrushSlot> = null;
  39. public static var layers: Array<LayerSlot> = null;
  40. public static var paintObjects: Array<MeshObject> = null;
  41. public static var assetMap = new Map<Int, Dynamic>(); // kha.Image | kha.Font
  42. #if arm_world
  43. public static var waterPass = true;
  44. #end
  45. public static function projectOpen() {
  46. UIFiles.show("arm", false, function(path: String) {
  47. if (!path.endsWith(".arm")) {
  48. Log.error(Strings.error5);
  49. return;
  50. }
  51. var current = @:privateAccess kha.graphics4.Graphics2.current;
  52. if (current != null) current.end();
  53. ImportArm.runProject(path);
  54. if (current != null) current.begin(false);
  55. });
  56. }
  57. public static function projectSave(saveAndQuit = false) {
  58. if (filepath == "") {
  59. projectSaveAs();
  60. return;
  61. }
  62. Window.get(0).title = UIFiles.filename + " - ArmorPaint";
  63. function export(_) {
  64. ExportArm.runProject();
  65. iron.App.removeRender(export);
  66. if (saveAndQuit) System.stop();
  67. }
  68. iron.App.notifyOnRender(export);
  69. }
  70. public static function projectSaveAs() {
  71. UIFiles.show("arm", true, function(path: String) {
  72. var f = UIFiles.filename;
  73. if (f == "") f = "untitled";
  74. filepath = path + "/" + f;
  75. if (!filepath.endsWith(".arm")) filepath += ".arm";
  76. projectSave();
  77. });
  78. }
  79. public static function projectNewBox() {
  80. UIBox.showCustom(function(ui: Zui) {
  81. if (ui.tab(Id.handle(), "New Project")) {
  82. ui.row([0.5, 0.5]);
  83. UITrait.inst.projectType = ui.combo(Id.handle({position: UITrait.inst.projectType}), ["Cube", "Sphere", "Tessellated Plane"], "Template");
  84. if (ui.button("OK") || ui.isReturnDown) {
  85. Project.projectNew();
  86. ViewportUtil.scaleToBounds();
  87. UIBox.show = false;
  88. App.redrawUI();
  89. }
  90. }
  91. });
  92. }
  93. public static function projectNew(resetLayers = true) {
  94. Window.get(0).title = "ArmorPaint";
  95. filepath = "";
  96. if (Context.mergedObject != null) {
  97. Context.mergedObject.remove();
  98. Data.deleteMesh(Context.mergedObject.data.handle);
  99. Context.mergedObject = null;
  100. }
  101. ViewportUtil.resetViewport();
  102. Context.layerPreviewDirty = true;
  103. Context.paintObject = Context.mainObject();
  104. Context.selectPaintObject(Context.mainObject());
  105. for (i in 1...paintObjects.length) {
  106. var p = paintObjects[i];
  107. if (p == Context.paintObject) continue;
  108. Data.deleteMesh(p.data.handle);
  109. p.remove();
  110. }
  111. var meshes = Scene.active.meshes;
  112. var len = meshes.length;
  113. for (i in 0...len) {
  114. var m = meshes[len - i - 1];
  115. if (UITrait.inst.projectObjects.indexOf(m) == -1) {
  116. Data.deleteMesh(m.data.handle);
  117. m.remove();
  118. }
  119. }
  120. var handle = Context.paintObject.data.handle;
  121. if (handle != "SceneSphere" && handle != "ScenePlane") {
  122. Data.deleteMesh(handle);
  123. }
  124. if (UITrait.inst.projectType != ModelCube) {
  125. var mesh: Dynamic = UITrait.inst.projectType == ModelSphere ?
  126. new arm.format.proc.Sphere(1, 512, 256) :
  127. new arm.format.proc.Plane(1, 1, 512, 512);
  128. var raw = {
  129. name: "Tessellated",
  130. vertex_arrays: [
  131. { values: mesh.posa, attrib: "pos" },
  132. { values: mesh.nora, attrib: "nor" },
  133. { values: mesh.texa, attrib: "tex" }
  134. ],
  135. index_arrays: [
  136. { values: mesh.inda, material: 0 }
  137. ],
  138. scale_pos: mesh.scalePos,
  139. scale_tex: mesh.scaleTex
  140. };
  141. var md = new MeshData(raw, function(md: MeshData) {});
  142. Data.cachedMeshes.set("SceneTessellated", md);
  143. if (UITrait.inst.projectType == ModelSphere) {
  144. ViewportUtil.setView(0, 0, 1, 0, 0, 0); // Top
  145. ViewportUtil.orbit(0, Math.PI / 6); // Orbit down
  146. }
  147. else if (UITrait.inst.projectType == ModelTessellatedPlane) {
  148. ViewportUtil.setView(0, 0, 5, 0, 0, 0); // Top
  149. ViewportUtil.orbit(0, Math.PI / 6); // Orbit down
  150. }
  151. }
  152. var n = UITrait.inst.projectType == ModelCube ? "Cube" : "Tessellated";
  153. Data.getMesh("Scene", n, function(md: MeshData) {
  154. var current = @:privateAccess kha.graphics4.Graphics2.current;
  155. if (current != null) current.end();
  156. UITrait.inst.pickerMaskHandle.position = MaskNone;
  157. Context.paintObject.setData(md);
  158. Context.paintObject.transform.scale.set(1, 1, 1);
  159. #if arm_creator
  160. if (UITrait.inst.projectType == ModelTessellatedPlane) {
  161. Context.paintObject.transform.loc.set(0, 0, -0.15);
  162. Context.paintObject.transform.scale.set(10, 10, 1);
  163. }
  164. #end
  165. Context.paintObject.transform.buildMatrix();
  166. Context.paintObject.name = n;
  167. paintObjects = [Context.paintObject];
  168. while (materials.length > 0) materials.pop().unload();
  169. Data.getMaterial("Scene", "Material", function(m: iron.data.MaterialData) {
  170. materials.push(new MaterialSlot(m));
  171. });
  172. Context.material = materials[0];
  173. brushes = [new BrushSlot()];
  174. Context.brush = brushes[0];
  175. History.reset();
  176. MaterialParser.parsePaintMaterial();
  177. RenderUtil.makeMaterialPreview();
  178. for (a in assets) Data.deleteImage(a.file);
  179. assets = [];
  180. assetNames = [];
  181. assetId = 0;
  182. Context.ddirty = 4;
  183. UITrait.inst.hwnd.redraws = 2;
  184. UITrait.inst.hwnd1.redraws = 2;
  185. UITrait.inst.hwnd2.redraws = 2;
  186. if (resetLayers) {
  187. while (layers.length > 0) layers.pop().unload();
  188. var layer = new LayerSlot();
  189. layers.push(layer);
  190. Context.setLayer(layer);
  191. iron.App.notifyOnRender(Layers.initLayers);
  192. }
  193. if (current != null) current.begin(false);
  194. UITrait.inst.savedEnvmap = UITrait.inst.defaultEnvmap;
  195. Scene.active.world.envmap = UITrait.inst.emptyEnvmap;
  196. Scene.active.world.raw.envmap = "World_radiance.k";
  197. UITrait.inst.showEnvmapHandle.selected = UITrait.inst.showEnvmap = false;
  198. Scene.active.world.probe.radiance = UITrait.inst.defaultRadiance;
  199. Scene.active.world.probe.radianceMipmaps = UITrait.inst.defaultRadianceMipmaps;
  200. Scene.active.world.probe.irradiance = UITrait.inst.defaultIrradiance;
  201. Scene.active.world.probe.raw.strength = 4.0;
  202. });
  203. }
  204. public static function importMaterial() {
  205. UIFiles.show("arm,blend", false, function(path: String) {
  206. path.endsWith(".blend") ?
  207. ImportBlend.runMaterial(path) :
  208. ImportArm.runMaterial(path);
  209. });
  210. }
  211. public static function importMesh() {
  212. UIFiles.show(Path.meshFormats.join(","), false, function(path: String) {
  213. importMeshBox(path);
  214. });
  215. }
  216. public static function importMeshBox(path: String) {
  217. UIBox.showCustom(function(ui: Zui) {
  218. if (ui.tab(Id.handle(), "Import Mesh")) {
  219. if (path.toLowerCase().endsWith(".obj")) {
  220. UITrait.inst.splitBy = ui.combo(Id.handle(), ["Object", "Group", "Material", "UDIM Tile"], "Split By", true);
  221. if (ui.isHovered) ui.tooltip("Split .obj mesh into objects");
  222. }
  223. if (path.toLowerCase().endsWith(".fbx")) {
  224. UITrait.inst.parseTransform = ui.check(Id.handle({selected: UITrait.inst.parseTransform}), "Parse Transforms");
  225. if (ui.isHovered) ui.tooltip("Load per-object transforms from .fbx");
  226. }
  227. ui.row([0.5, 0.5]);
  228. if (ui.button("Cancel")) {
  229. UIBox.show = false;
  230. }
  231. if (ui.button("Import") || ui.isReturnDown) {
  232. UIBox.show = false;
  233. App.redrawUI();
  234. ImportMesh.run(path);
  235. }
  236. }
  237. });
  238. }
  239. public static function reimportMesh() {
  240. if (Project.meshAssets != null && Project.meshAssets.length > 0) {
  241. ImportMesh.run(Project.meshAssets[0], false);
  242. Log.info("Mesh reimported.");
  243. }
  244. else importAsset();
  245. }
  246. public static function importAsset(filters: String = null) {
  247. if (filters == null) filters = Path.textureFormats.join(",") + "," + Path.meshFormats.join(",");
  248. UIFiles.show(filters, false, function(path: String) {
  249. ImportAsset.run(path);
  250. });
  251. }
  252. }
  253. typedef TProjectFormat = {
  254. public var version: String;
  255. @:optional public var brush_nodes: Array<TNodeCanvas>;
  256. @:optional public var material_nodes: Array<TNodeCanvas>;
  257. @:optional public var assets: Array<String>; // texture_assets
  258. @:optional public var layer_datas: Array<TLayerData>;
  259. @:optional public var mesh_datas: Array<TMeshData>;
  260. @:optional public var mesh_assets: Array<String>;
  261. }
  262. typedef TLayerData = {
  263. public var res: Int; // Width pixels
  264. public var bpp: Int; // Bits per pixel
  265. public var texpaint: haxe.io.Bytes;
  266. public var texpaint_nor: haxe.io.Bytes;
  267. public var texpaint_pack: haxe.io.Bytes;
  268. public var texpaint_mask: haxe.io.Bytes;
  269. public var uv_scale: Float;
  270. public var uv_rot: Float;
  271. public var uv_type: Int;
  272. public var opacity_mask: Float;
  273. public var material_mask: Int;
  274. public var object_mask: Int;
  275. public var blending: Int;
  276. }
  277. typedef TAsset = {
  278. public var id: Int;
  279. public var name: String;
  280. public var file: String;
  281. }