diff --git a/Assets/plugins/embed/uv_unwrap.js b/Assets/plugins/embed/uv_unwrap.js index 19813b95..e736b79a 100644 --- a/Assets/plugins/embed/uv_unwrap.js +++ b/Assets/plugins/embed/uv_unwrap.js @@ -6,83 +6,99 @@ class R { let r = new R(); // uv_unwrap.js +function unwrap_mesh(mesh) { + let positions = mesh.posa; + let normals = mesh.nora; + let indices = mesh.inda; + let vertexCount = positions.length / 4; + let indexCount = indices.length; + + a._setVertexCount(vertexCount); + a._setIndexCount(indexCount); + let pa = new Float32Array(r.buffer, a._setPositions(), vertexCount * 3); + let na = new Float32Array(r.buffer, a._setNormals(), vertexCount * 3); + let ia = new Uint32Array(r.buffer, a._setIndices(), indexCount); + + let inv = 1 / 32767; + + for (let i = 0; i < vertexCount; i++) { + pa[i * 3 ] = positions[i * 4 ] * inv; + pa[i * 3 + 1] = positions[i * 4 + 1] * inv; + pa[i * 3 + 2] = positions[i * 4 + 2] * inv; + na[i * 3 ] = normals [i * 2 ] * inv; + na[i * 3 + 1] = normals [i * 2 + 1] * inv; + na[i * 3 + 2] = positions[i * 4 + 3] * inv; + } + for (let i = 0; i < indexCount; i++) { + ia[i] = indices[i]; + } + + a._unwrap(); + + vertexCount = a._getVertexCount(); + indexCount = a._getIndexCount(); + pa = new Float32Array(r.buffer, a._getPositions(), vertexCount * 3); + na = new Float32Array(r.buffer, a._getNormals(), vertexCount * 3); + let ua = new Float32Array(r.buffer, a._getUVs(), vertexCount * 2); + ia = new Uint32Array(r.buffer, a._getIndices(), indexCount); + + let pa16 = new Int16Array(vertexCount * 4); + let na16 = new Int16Array(vertexCount * 2); + let ua16 = new Int16Array(vertexCount * 2); + let ia32 = new Uint32Array(indexCount); + + for (let i = 0; i < vertexCount; i++) { + pa16[i * 4 ] = pa[i * 3 ] / inv; + pa16[i * 4 + 1] = pa[i * 3 + 1] / inv; + pa16[i * 4 + 2] = pa[i * 3 + 2] / inv; + pa16[i * 4 + 3] = na[i * 3 + 2] / inv; + na16[i * 2 ] = na[i * 3 ] / inv; + na16[i * 2 + 1] = na[i * 3 + 1] / inv; + ua16[i * 2 ] = ua[i * 2 ] / inv; + ua16[i * 2 + 1] = ua[i * 2 + 1] / inv; + } + for (let i = 0; i < indexCount; i++) { + ia32[i] = ia[i]; + } + + mesh.posa = pa16; + mesh.nora = na16; + mesh.texa = ua16; + mesh.inda = ia32; + + a._destroy(); +} + let plugin = new arm.Plugin(); let h1 = new zui.Handle(); plugin.drawUI = function(ui) { if (ui.panel(h1, "UV Unwrap")) { if (ui.button("Unwrap Mesh")) { - for (const po of arm.Project.paintObjects) { - let raw = po.data.raw; - let positions = raw.vertex_arrays[0].values; - let normals = raw.vertex_arrays[1].values; - let indices = raw.index_arrays[0].values; - let vertexCount = positions.length / 4; - let indexCount = indices.length; - - a._setVertexCount(vertexCount); - a._setIndexCount(indexCount); - let pa = new Float32Array(r.buffer, a._setPositions(), vertexCount * 3); - let na = new Float32Array(r.buffer, a._setNormals(), vertexCount * 3); - let ia = new Uint32Array(r.buffer, a._setIndices(), indexCount); - - let inv = 1 / 32767; - - for (let i = 0; i < vertexCount; i++) { - pa[i * 3 ] = positions[i * 4 ] * inv; - pa[i * 3 + 1] = positions[i * 4 + 1] * inv; - pa[i * 3 + 2] = positions[i * 4 + 2] * inv; - na[i * 3 ] = normals [i * 2 ] * inv; - na[i * 3 + 1] = normals [i * 2 + 1] * inv; - na[i * 3 + 2] = positions[i * 4 + 3] * inv; - } - for (let i = 0; i < indexCount; i++) { - ia[i] = indices[i]; - } - - a._unwrap(); - - vertexCount = a._getVertexCount(); - indexCount = a._getIndexCount(); - pa = new Float32Array(r.buffer, a._getPositions(), vertexCount * 3); - na = new Float32Array(r.buffer, a._getNormals(), vertexCount * 3); - let ua = new Float32Array(r.buffer, a._getUVs(), vertexCount * 2); - ia = new Uint32Array(r.buffer, a._getIndices(), indexCount); - - let pa16 = new Int16Array(vertexCount * 4); - let na16 = new Int16Array(vertexCount * 2); - let ua16 = new Int16Array(vertexCount * 2); - let ia32 = new Uint32Array(indexCount); - - for (let i = 0; i < vertexCount; i++) { - pa16[i * 4 ] = pa[i * 3 ] / inv; - pa16[i * 4 + 1] = pa[i * 3 + 1] / inv; - pa16[i * 4 + 2] = pa[i * 3 + 2] / inv; - pa16[i * 4 + 3] = na[i * 3 + 2] / inv; - na16[i * 2 ] = na[i * 3 ] / inv; - na16[i * 2 + 1] = na[i * 3 + 1] / inv; - ua16[i * 2 ] = ua[i * 2 ] / inv; - ua16[i * 2 + 1] = ua[i * 2 + 1] / inv; - } - for (let i = 0; i < indexCount; i++) { - ia32[i] = ia[i]; - } - - po.data.raw.vertex_arrays[0].values = pa16; - po.data.raw.vertex_arrays[1].values = na16; - po.data.raw.vertex_arrays[2].values = ua16; - po.data.raw.index_arrays[0].values = ia32; - + var mesh = { + posa: raw.vertex_arrays[0].values, + nora: raw.vertex_arrays[1].values, + texa: null, + inda: raw.index_arrays[0].values + }; + unwrap_mesh(mesh); + raw.vertex_arrays[0].values = mesh.posa; + raw.vertex_arrays[1].values = mesh.nora; + raw.vertex_arrays[2].values = mesh.texa; + raw.index_arrays[0].values = mesh.inda; let geom = po.data.geom; - geom.indices[0] = ia32; + geom.indices[0] = mesh.inda; geom.ready = false; geom.build(); - - a._destroy(); } - arm.MeshUtil.mergeMesh(); } } } + +let unwrappers = arm.MeshUtil.unwrappers; +unwrappers.h["uv_unwrap.js"] = unwrap_mesh; +plugin.delete = function() { + unwrappers.h["uv_unwrap.js"] = null; +}; diff --git a/Assets/plugins/wasm/uv_unwrap.js b/Assets/plugins/wasm/uv_unwrap.js index 9665901f..40a39b65 100644 --- a/Assets/plugins/wasm/uv_unwrap.js +++ b/Assets/plugins/wasm/uv_unwrap.js @@ -28,83 +28,99 @@ function Z(){function b(){if(!X&&(X=!0,a.calledRun=!0,!t)){U(H);U(I);if(a.onRunt if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0 0) { diff --git a/Sources/arm/Project.hx b/Sources/arm/Project.hx index 14a61687..3a905a62 100644 --- a/Sources/arm/Project.hx +++ b/Sources/arm/Project.hx @@ -12,7 +12,7 @@ import iron.data.Data; import iron.object.MeshObject; import iron.Scene; import arm.util.RenderUtil; -import arm.Viewport; +import arm.util.MeshUtil; import arm.sys.File; import arm.sys.Path; import arm.ui.UISidebar; @@ -20,6 +20,7 @@ import arm.ui.UIFiles; import arm.ui.UIBox; import arm.ui.UINodes; import arm.ui.UIHeader; +import arm.ui.BoxPreferences; import arm.data.LayerSlot; import arm.data.BrushSlot; import arm.data.FontSlot; @@ -32,6 +33,7 @@ import arm.io.ImportMesh; import arm.io.ImportTexture; import arm.io.ExportArm; import arm.node.NodesBrush; +import arm.Viewport; import arm.ProjectFormat; import arm.Enums; @@ -435,6 +437,57 @@ class Project { else importAsset(); } + public static function unwrapMeshBox(mesh: Dynamic, done: Void->Void) { + UIBox.showCustom(function(ui: Zui) { + if (ui.tab(Id.handle(), tr("Unwrap Mesh"))) { + + var unwrapPlugins = []; + if (BoxPreferences.filesPlugin == null) { + BoxPreferences.fetchPlugins(); + } + for (f in BoxPreferences.filesPlugin) { + if (f.indexOf("uv_unwrap") >= 0 && f.endsWith(".js")) { + unwrapPlugins.push(f); + } + } + unwrapPlugins.push("equirect"); + + var unwrapBy = ui.combo(Id.handle(), unwrapPlugins, tr("Plugin"), true); + + ui.row([0.5, 0.5]); + if (ui.button(tr("Cancel"))) { + UIBox.show = false; + } + if (ui.button(tr("Unwrap")) || ui.isReturnDown) { + UIBox.show = false; + App.redrawUI(); + function doImport() { + if (unwrapBy == unwrapPlugins.length - 1) { + MeshUtil.equirectUnwrap(mesh); + } + else { + var f = unwrapPlugins[unwrapBy]; + if (Config.raw.plugins.indexOf(f) == -1) { + Config.enablePlugin(f); + Console.info(f + " " + tr("plugin enabled")); + } + MeshUtil.unwrappers.get(f)(mesh); + } + done(); + } + #if (krom_android || krom_ios) + arm.App.notifyOnNextFrame(function() { + Console.toast(tr("Unwrapping mesh")); + arm.App.notifyOnNextFrame(doImport); + }); + #else + doImport(); + #end + } + } + }); + } + public static function importAsset(filters: String = null, hdrAsEnvmap = true) { if (filters == null) filters = Path.textureFormats.join(",") + "," + Path.meshFormats.join(","); UIFiles.show(filters, false, true, function(path: String) { diff --git a/Sources/arm/Strings.hx b/Sources/arm/Strings.hx index b6619ce8..41e2e98b 100644 --- a/Sources/arm/Strings.hx +++ b/Sources/arm/Strings.hx @@ -5,7 +5,7 @@ class Strings { public static function error1(): String { return tr("Error: Unknown asset format"); } public static function error2(): String { return tr("Error: Could not locate texture"); } public static function error3(): String { return tr("Error: Failed to read mesh data"); } - public static function error4(): String { return tr("Error: Mesh has no UVs, generating defaults"); } + // public static function error4(): String { return tr("Error: Mesh has no UVs, generating defaults"); } public static function error5(): String { return tr("Error: Check internet connection to access the cloud"); } public static function info0(): String { return tr("Info: Asset already imported"); } } diff --git a/Sources/arm/io/ImportMesh.hx b/Sources/arm/io/ImportMesh.hx index 64c6bcf3..2b638f4d 100644 --- a/Sources/arm/io/ImportMesh.hx +++ b/Sources/arm/io/ImportMesh.hx @@ -43,10 +43,18 @@ class ImportMesh { var ext = path.substr(path.lastIndexOf(".") + 1); var importer = Path.meshImporters.get(ext); importer(path, function(mesh: Dynamic) { - replaceExisting ? ImportMesh.makeMesh(mesh, path) : ImportMesh.addMesh(mesh); + replaceExisting ? makeMesh(mesh, path) : addMesh(mesh); }); } + Project.meshAssets = [path]; + + #if (krom_android || krom_ios) + kha.Window.get(0).title = path.substring(path.lastIndexOf(Path.sep) + 1, path.lastIndexOf(".")); + #end + } + + static function finishImport() { if (Context.mergedObject != null) { Context.mergedObject.remove(); Data.deleteMesh(Context.mergedObject.data.handle); @@ -69,7 +77,6 @@ class ImportMesh { Context.paintObject.skip_context = "paint"; Context.mergedObject.visible = true; } - Project.meshAssets = [path]; Viewport.scaleToBounds(); @@ -86,10 +93,6 @@ class ImportMesh { #if (kha_direct3d12 || kha_vulkan) arm.render.RenderPathRaytrace.ready = false; #end - - #if (krom_android || krom_ios) - kha.Window.get(0).title = path.substring(path.lastIndexOf(Path.sep) + 1, path.lastIndexOf(".")); - #end } public static function makeMesh(mesh: Dynamic, path: String) { @@ -98,100 +101,101 @@ class ImportMesh { return; } - var raw = rawMesh(mesh); - if (mesh.texa == null) equirectUnwrap(mesh); - raw.vertex_arrays.push({ values: mesh.texa, attrib: "tex", data: "short2norm" }); - if (mesh.cola != null) raw.vertex_arrays.push({ values: mesh.cola, attrib: "col", data: "short4norm", padding: 1 }); + function _makeMesh() { + var raw = rawMesh(mesh); + if (mesh.cola != null) raw.vertex_arrays.push({ values: mesh.cola, attrib: "col", data: "short4norm", padding: 1 }); - new MeshData(raw, function(md: MeshData) { - Context.paintObject = Context.mainObject(); + new MeshData(raw, function(md: MeshData) { + Context.paintObject = Context.mainObject(); - Context.selectPaintObject(Context.mainObject()); - for (i in 0...Project.paintObjects.length) { - var p = Project.paintObjects[i]; - if (p == Context.paintObject) continue; - Data.deleteMesh(p.data.handle); - p.remove(); - } - var handle = Context.paintObject.data.handle; - if (handle != "SceneSphere" && handle != "ScenePlane") { - Data.deleteMesh(handle); - } - - if (clearLayers) { - while (Project.layers.length > 0) { - var l = Project.layers.pop(); - l.unload(); + Context.selectPaintObject(Context.mainObject()); + for (i in 0...Project.paintObjects.length) { + var p = Project.paintObjects[i]; + if (p == Context.paintObject) continue; + Data.deleteMesh(p.data.handle); + p.remove(); + } + var handle = Context.paintObject.data.handle; + if (handle != "SceneSphere" && handle != "ScenePlane") { + Data.deleteMesh(handle); } - Layers.newLayer(false); - iron.App.notifyOnInit(Layers.initLayers); - History.reset(); - } - Context.paintObject.setData(md); - Context.paintObject.name = mesh.name; - Project.paintObjects = [Context.paintObject]; + if (clearLayers) { + while (Project.layers.length > 0) { + var l = Project.layers.pop(); + l.unload(); + } + Layers.newLayer(false); + iron.App.notifyOnInit(Layers.initLayers); + History.reset(); + } - md.handle = raw.name; - Data.cachedMeshes.set(md.handle, md); + Context.paintObject.setData(md); + Context.paintObject.name = mesh.name; + Project.paintObjects = [Context.paintObject]; - Context.ddirty = 4; - UISidebar.inst.hwnd0.redraws = 2; - UISidebar.inst.hwnd1.redraws = 2; - UVUtil.uvmapCached = false; - UVUtil.trianglemapCached = false; - UVUtil.dilatemapCached = false; - }); + md.handle = raw.name; + Data.cachedMeshes.set(md.handle, md); + + Context.ddirty = 4; + UISidebar.inst.hwnd0.redraws = 2; + UISidebar.inst.hwnd1.redraws = 2; + UVUtil.uvmapCached = false; + UVUtil.trianglemapCached = false; + UVUtil.dilatemapCached = false; + + // Wait for addMesh calls to finish + iron.App.notifyOnInit(finishImport); + }); + } + + if (mesh.texa == null) { + Project.unwrapMeshBox(mesh, _makeMesh); + } + else { + _makeMesh(); + } } public static function addMesh(mesh: Dynamic) { - if (mesh.texa == null) equirectUnwrap(mesh); - var raw = rawMesh(mesh); - raw.vertex_arrays.push({ values: mesh.texa, attrib: "tex", data: "short2norm" }); - if (mesh.cola != null) raw.vertex_arrays.push({ values: mesh.cola, attrib: "col", data: "short4norm", padding: 1 }); + function _addMesh() { + var raw = rawMesh(mesh); + if (mesh.cola != null) raw.vertex_arrays.push({ values: mesh.cola, attrib: "col", data: "short4norm", padding: 1 }); - new MeshData(raw, function(md: MeshData) { + new MeshData(raw, function(md: MeshData) { - var object = Scene.active.addMeshObject(md, Context.paintObject.materials, Context.paintObject); - object.name = mesh.name; - object.skip_context = "paint"; + var object = Scene.active.addMeshObject(md, Context.paintObject.materials, Context.paintObject); + object.name = mesh.name; + object.skip_context = "paint"; - // Ensure unique names - for (p in Project.paintObjects) { - if (p.name == object.name) { - p.name += ".001"; - p.data.handle += ".001"; - Data.cachedMeshes.set(p.data.handle, p.data); + // Ensure unique names + for (p in Project.paintObjects) { + if (p.name == object.name) { + p.name += ".001"; + p.data.handle += ".001"; + Data.cachedMeshes.set(p.data.handle, p.data); + } } - } - Project.paintObjects.push(object); + Project.paintObjects.push(object); - md.handle = raw.name; - Data.cachedMeshes.set(md.handle, md); + md.handle = raw.name; + Data.cachedMeshes.set(md.handle, md); - Context.ddirty = 4; - UISidebar.inst.hwnd0.redraws = 2; - UVUtil.uvmapCached = false; - UVUtil.trianglemapCached = false; - UVUtil.dilatemapCached = false; - }); - } + Context.ddirty = 4; + UISidebar.inst.hwnd0.redraws = 2; + UVUtil.uvmapCached = false; + UVUtil.trianglemapCached = false; + UVUtil.dilatemapCached = false; + }); + } - static function equirectUnwrap(mesh: Dynamic) { - Console.error(Strings.error4()); - var verts = Std.int(mesh.posa.length / 4); - mesh.texa = new Int16Array(verts * 2); - var n = new Vec4(); - for (i in 0...verts) { - n.set(mesh.posa[i * 4] / 32767, mesh.posa[i * 4 + 1] / 32767, mesh.posa[i * 4 + 2] / 32767).normalize(); - // Sphere projection - // mesh.texa[i * 2 ] = Math.atan2(n.x, n.y) / (Math.PI * 2) + 0.5; - // mesh.texa[i * 2 + 1] = n.z * 0.5 + 0.5; - // Equirect - mesh.texa[i * 2 ] = Std.int(((Math.atan2(-n.z, n.x) + Math.PI) / (Math.PI * 2)) * 32767); - mesh.texa[i * 2 + 1] = Std.int((Math.acos(n.y) / Math.PI) * 32767); + if (mesh.texa == null) { + Project.unwrapMeshBox(mesh, _addMesh); + } + else { + _addMesh(); } } @@ -200,7 +204,8 @@ class ImportMesh { name: mesh.name, vertex_arrays: [ { values: mesh.posa, attrib: "pos", data: "short4norm" }, - { values: mesh.nora, attrib: "nor", data: "short2norm" } + { values: mesh.nora, attrib: "nor", data: "short2norm" }, + { values: mesh.texa, attrib: "tex", data: "short2norm" } ], index_arrays: [ { values: mesh.inda, material: 0 } diff --git a/Sources/arm/util/MeshUtil.hx b/Sources/arm/util/MeshUtil.hx index 221208d0..cc3240c6 100644 --- a/Sources/arm/util/MeshUtil.hx +++ b/Sources/arm/util/MeshUtil.hx @@ -12,6 +12,8 @@ import arm.Enums; class MeshUtil { + public static var unwrappers: MapVoid> = []; + public static function mergeMesh(paintObjects: Array = null) { if (paintObjects == null) paintObjects = Project.paintObjects; if (paintObjects.length == 0) return; @@ -332,4 +334,19 @@ class MeshUtil { va0[i * 4 + 2] = vertices[i * l + 2]; } } + + public static function equirectUnwrap(mesh: Dynamic) { + var verts = Std.int(mesh.posa.length / 4); + mesh.texa = new Int16Array(verts * 2); + var n = new Vec4(); + for (i in 0...verts) { + n.set(mesh.posa[i * 4] / 32767, mesh.posa[i * 4 + 1] / 32767, mesh.posa[i * 4 + 2] / 32767).normalize(); + // Sphere projection + // mesh.texa[i * 2 ] = Math.atan2(n.x, n.y) / (Math.PI * 2) + 0.5; + // mesh.texa[i * 2 + 1] = n.z * 0.5 + 0.5; + // Equirect + mesh.texa[i * 2 ] = Std.int(((Math.atan2(-n.z, n.x) + Math.PI) / (Math.PI * 2)) * 32767); + mesh.texa[i * 2 + 1] = Std.int((Math.acos(n.y) / Math.PI) * 32767); + } + } }