package arm; import kha.System; import kha.Window; import zui.Zui; import zui.Id; import zui.Nodes; import iron.data.SceneFormat; import iron.data.MeshData; import iron.data.Data; import iron.object.MeshObject; import iron.Scene; import arm.util.RenderUtil; import arm.util.ViewportUtil; import arm.sys.Path; import arm.ui.UISidebar; import arm.ui.UIFiles; import arm.ui.UIBox; import arm.data.LayerSlot; import arm.data.BrushSlot; import arm.data.MaterialSlot; import arm.node.MaterialParser; import arm.io.ImportAsset; import arm.io.ImportArm; import arm.io.ImportBlend; import arm.io.ImportMesh; import arm.io.ExportArm; import arm.Enums; class Project { public static var raw: TProjectFormat; public static var filepath = ""; public static var assets: Array = []; public static var assetNames: Array = []; public static var assetId = 0; public static var meshAssets: Array = []; public static var materials: Array = null; public static var materialsScene: Array = null; public static var brushes: Array = null; public static var layers: Array = null; public static var paintObjects: Array = null; public static var assetMap = new Map(); // kha.Image | kha.Font #if arm_world public static var waterPass = true; #end public static function projectOpen() { UIFiles.show("arm", false, function(path: String) { if (!path.endsWith(".arm")) { Log.error(Strings.error5); return; } var current = @:privateAccess kha.graphics4.Graphics2.current; if (current != null) current.end(); ImportArm.runProject(path); if (current != null) current.begin(false); }); } public static function projectSave(saveAndQuit = false) { if (filepath == "") { projectSaveAs(); return; } Window.get(0).title = UIFiles.filename + " - ArmorPaint"; function export(_) { ExportArm.runProject(); iron.App.removeRender(export); if (saveAndQuit) System.stop(); } iron.App.notifyOnRender(export); } public static function projectSaveAs() { UIFiles.show("arm", true, function(path: String) { var f = UIFiles.filename; if (f == "") f = tr("untitled"); filepath = path + Path.sep + f; if (!filepath.endsWith(".arm")) filepath += ".arm"; projectSave(); }); } public static function projectNewBox() { UIBox.showCustom(function(ui: Zui) { if (ui.tab(Id.handle(), tr("New Project"))) { ui.row([0.5, 0.5]); UISidebar.inst.projectType = ui.combo(Id.handle({position: UISidebar.inst.projectType}), ["Cube", "Sphere", "Tessellated Plane"], tr("Template")); if (ui.button(tr("OK")) || ui.isReturnDown) { Project.projectNew(); ViewportUtil.scaleToBounds(); UIBox.show = false; App.redrawUI(); } } }); } public static function projectNew(resetLayers = true) { Window.get(0).title = "ArmorPaint"; filepath = ""; if (Context.mergedObject != null) { Context.mergedObject.remove(); Data.deleteMesh(Context.mergedObject.data.handle); Context.mergedObject = null; } ViewportUtil.resetViewport(); Context.layerPreviewDirty = true; Context.paintObject = Context.mainObject(); Context.selectPaintObject(Context.mainObject()); for (i in 1...paintObjects.length) { var p = paintObjects[i]; if (p == Context.paintObject) continue; Data.deleteMesh(p.data.handle); p.remove(); } var meshes = Scene.active.meshes; var len = meshes.length; for (i in 0...len) { var m = meshes[len - i - 1]; if (UISidebar.inst.projectObjects.indexOf(m) == -1) { Data.deleteMesh(m.data.handle); m.remove(); } } var handle = Context.paintObject.data.handle; if (handle != "SceneSphere" && handle != "ScenePlane") { Data.deleteMesh(handle); } if (UISidebar.inst.projectType != ModelCube) { var mesh: Dynamic = UISidebar.inst.projectType == ModelSphere ? new arm.format.proc.Sphere(1, 512, 256) : new arm.format.proc.Plane(1, 1, 512, 512); var raw = { name: "Tessellated", vertex_arrays: [ { values: mesh.posa, attrib: "pos", data: "short4norm" }, { values: mesh.nora, attrib: "nor", data: "short2norm" }, { values: mesh.texa, attrib: "tex", data: "short2norm" } ], index_arrays: [ { values: mesh.inda, material: 0 } ], scale_pos: mesh.scalePos, scale_tex: mesh.scaleTex }; var md = new MeshData(raw, function(md: MeshData) {}); Data.cachedMeshes.set("SceneTessellated", md); if (UISidebar.inst.projectType == ModelSphere) { ViewportUtil.setView(0, 0, 1, 0, 0, 0); // Top ViewportUtil.orbit(0, Math.PI / 6); // Orbit down } else if (UISidebar.inst.projectType == ModelTessellatedPlane) { ViewportUtil.setView(0, 0, 5, 0, 0, 0); // Top ViewportUtil.orbit(0, Math.PI / 6); // Orbit down } } var n = UISidebar.inst.projectType == ModelCube ? "Cube" : "Tessellated"; Data.getMesh("Scene", n, function(md: MeshData) { var current = @:privateAccess kha.graphics4.Graphics2.current; if (current != null) current.end(); UISidebar.inst.pickerMaskHandle.position = MaskNone; Context.paintObject.setData(md); Context.paintObject.transform.scale.set(1, 1, 1); #if arm_creator if (UISidebar.inst.projectType == ModelTessellatedPlane) { Context.paintObject.transform.loc.set(0, 0, -0.15); Context.paintObject.transform.scale.set(10, 10, 1); } #end Context.paintObject.transform.buildMatrix(); Context.paintObject.name = n; paintObjects = [Context.paintObject]; while (materials.length > 0) materials.pop().unload(); Data.getMaterial("Scene", "Material", function(m: iron.data.MaterialData) { materials.push(new MaterialSlot(m)); }); Context.material = materials[0]; brushes = [new BrushSlot()]; Context.brush = brushes[0]; History.reset(); MaterialParser.parsePaintMaterial(); RenderUtil.makeMaterialPreview(); for (a in assets) Data.deleteImage(a.file); assets = []; assetNames = []; assetId = 0; Context.ddirty = 4; UISidebar.inst.hwnd.redraws = 2; UISidebar.inst.hwnd1.redraws = 2; UISidebar.inst.hwnd2.redraws = 2; if (resetLayers) { while (layers.length > 0) layers.pop().unload(); var layer = new LayerSlot(); layers.push(layer); Context.setLayer(layer); iron.App.notifyOnRender(Layers.initLayers); } if (current != null) current.begin(false); UISidebar.inst.savedEnvmap = UISidebar.inst.defaultEnvmap; Scene.active.world.envmap = UISidebar.inst.emptyEnvmap; Scene.active.world.raw.envmap = "World_radiance.k"; UISidebar.inst.showEnvmapHandle.selected = UISidebar.inst.showEnvmap = false; Scene.active.world.probe.radiance = UISidebar.inst.defaultRadiance; Scene.active.world.probe.radianceMipmaps = UISidebar.inst.defaultRadianceMipmaps; Scene.active.world.probe.irradiance = UISidebar.inst.defaultIrradiance; Scene.active.world.probe.raw.strength = 4.0; }); } public static function importMaterial() { UIFiles.show("arm,blend", false, function(path: String) { path.endsWith(".blend") ? ImportBlend.runMaterial(path) : ImportArm.runMaterial(path); }); } public static function importBrush() { UIFiles.show("arm", false, function(path: String) { ImportArm.runBrush(path); }); } public static function importMesh() { UIFiles.show(Path.meshFormats.join(","), false, function(path: String) { importMeshBox(path); }); } public static function importMeshBox(path: String) { UIBox.showCustom(function(ui: Zui) { if (ui.tab(Id.handle(), tr("Import Mesh"))) { if (path.toLowerCase().endsWith(".obj")) { UISidebar.inst.splitBy = ui.combo(Id.handle(), [ tr("Object"), tr("Group"), tr("Material"), tr("UDIM Tile"), ], tr("Split By"), true); if (ui.isHovered) ui.tooltip(tr("Split .obj mesh into objects")); } if (path.toLowerCase().endsWith(".fbx")) { UISidebar.inst.parseTransform = ui.check(Id.handle({selected: UISidebar.inst.parseTransform}), tr("Parse Transforms")); if (ui.isHovered) ui.tooltip(tr("Load per-object transforms from .fbx")); } if (path.toLowerCase().endsWith(".fbx") || path.toLowerCase().endsWith(".blend")) { UISidebar.inst.parseVCols = ui.check(Id.handle({selected: UISidebar.inst.parseVCols}), tr("Parse Vertex Colors")); if (ui.isHovered) ui.tooltip(tr("Import vertex color data")); } ui.row([0.5, 0.5]); if (ui.button(tr("Cancel"))) { UIBox.show = false; } if (ui.button(tr("Import")) || ui.isReturnDown) { UIBox.show = false; App.redrawUI(); ImportMesh.run(path); } } }); UIBox.clickToHide = false; // Prevent closing when going back to window from file browser } public static function reimportMesh() { if (Project.meshAssets != null && Project.meshAssets.length > 0) { ImportMesh.run(Project.meshAssets[0], false); Log.info("Mesh reimported."); } else importAsset(); } public static function importAsset(filters: String = null) { if (filters == null) filters = Path.textureFormats.join(",") + "," + Path.meshFormats.join(","); UIFiles.show(filters, false, function(path: String) { ImportAsset.run(path); }); } } typedef TProjectFormat = { public var version: String; @:optional public var brush_nodes: Array; @:optional public var brush_icons: Array; @:optional public var material_nodes: Array; @:optional public var material_icons: Array; @:optional public var assets: Array; // texture_assets @:optional public var layer_datas: Array; @:optional public var mesh_datas: Array; @:optional public var mesh_assets: Array; } typedef TLayerData = { public var res: Int; // Width pixels public var bpp: Int; // Bits per pixel public var texpaint: haxe.io.Bytes; public var texpaint_nor: haxe.io.Bytes; public var texpaint_pack: haxe.io.Bytes; public var texpaint_mask: haxe.io.Bytes; public var uv_scale: Float; public var uv_rot: Float; public var uv_type: Int; public var opacity_mask: Float; public var material_mask: Int; public var object_mask: Int; public var blending: Int; public var parent: Int; } typedef TAsset = { public var id: Int; public var name: String; public var file: String; }