From b4209992763a90f7ce1ad61614c00a65c2467622 Mon Sep 17 00:00:00 2001 From: Malik12tree <82341209+Malik12tree@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:09:27 +0200 Subject: [PATCH] MTools 2.0.0 (#493) * Modularize MTools * Allow loading font files for the Text Mesh generator * Text Mesh generator: Commit dependencies * Better English * Fix Polyhedron generator * Split MTools tools from operators * Improved plugin about documentation * Implement checkboxes for amend menus that simulated numbers as toggles * Use a math parser for XYZ surface generator * XYZ surface generator: Use better more smaller expression evaluator * XYZ surface generator: Include variables as sliders * Scatter: Improve scattered mesh rotation to accurately follow face * Rewrite utilities * Triangles To Quads: Improved Algorithm * Expand/Shrink Selection: Enable for face and edge modes. * Triangulate Faces: Fix counter clock wise polygons not working correctly * Make use of reusable ThreeJS objects scoped for each action file. * Add Bridge Loops Action * Expand/Shrink: Add selection based on selection mode * Bridge Loops: Detect face holes * Improve about builder * Bridge Loops: More educated edge loop picker * Bridge Loops: Organize unorganized loops * Bridge Loops: Path blending + docs * Create a Logo * Final Touches * Update plugin metadata * Bridge Edge Loops: Add Order Option * Bridge Edge Loops: Automatically detect if loop should be reversed * Remove link behavior in about + remove SVG icon --- plugins.json | 12 +- plugins/mesh_tools.js | 2322 -- plugins/mesh_tools/about.md | 783 + plugins/mesh_tools/icon.png | Bin 0 -> 3958 bytes plugins/mesh_tools/mesh_tools.js | 22677 ++++++++++++++++ src/mesh_tools/.gitignore | 0 src/mesh_tools/.vscode/settings.json | 6 + src/mesh_tools/TODO.md | 31 + src/mesh_tools/assets/actions.json | 493 + src/mesh_tools/assets/actions/array.png | Bin 0 -> 40948 bytes .../actions/bridge_edge_loops_1_after.png | Bin 0 -> 59717 bytes .../actions/bridge_edge_loops_2_after.png | Bin 0 -> 59554 bytes .../actions/bridge_edge_loops_before.png | Bin 0 -> 42066 bytes .../assets/actions/laplacian_smooth_after.png | Bin 0 -> 49587 bytes .../actions/laplacian_smooth_before.png | Bin 0 -> 40513 bytes src/mesh_tools/assets/actions/poke_after.png | Bin 0 -> 75464 bytes src/mesh_tools/assets/actions/poke_before.png | Bin 0 -> 38315 bytes src/mesh_tools/assets/actions/polyhedron.png | Bin 0 -> 19048 bytes src/mesh_tools/assets/actions/scatter.png | Bin 0 -> 58510 bytes src/mesh_tools/assets/actions/split_edges.png | Bin 0 -> 19893 bytes .../assets/actions/subdivide_after.png | Bin 0 -> 17044 bytes .../assets/actions/subdivide_before.png | Bin 0 -> 11283 bytes .../assets/actions/terrain_solid.png | Bin 0 -> 171307 bytes .../assets/actions/terrain_wire.png | Bin 0 -> 192685 bytes src/mesh_tools/assets/actions/text.png | Bin 0 -> 6908 bytes .../assets/actions/to_sphere_1_after.png | Bin 0 -> 38303 bytes .../assets/actions/to_sphere_1_before.png | Bin 0 -> 41239 bytes .../assets/actions/to_sphere_2_after.png | Bin 0 -> 28511 bytes .../assets/actions/to_sphere_2_before.png | Bin 0 -> 26036 bytes src/mesh_tools/assets/actions/torus_knot.png | Bin 0 -> 55387 bytes .../assets/actions/triangulate_after.png | Bin 0 -> 30706 bytes .../assets/actions/triangulate_before.png | Bin 0 -> 25396 bytes .../assets/actions/tris_to_quad_after.png | Bin 0 -> 25699 bytes .../assets/actions/tris_to_quad_before.png | Bin 0 -> 30322 bytes .../assets/actions/uv_project_view_solid.png | Bin 0 -> 26472 bytes .../assets/actions/uv_project_view_uv.png | Bin 0 -> 60042 bytes .../uv_turnaround_projection_solid.png | Bin 0 -> 21529 bytes .../actions/uv_turnaround_projection_uv.png | Bin 0 -> 24094 bytes src/mesh_tools/assets/actions/xyz.png | Bin 0 -> 65620 bytes src/mesh_tools/assets/icon.png | Bin 0 -> 3958 bytes src/mesh_tools/assets/icon.svg | 237 + src/mesh_tools/assets/roboto_regular.json | 1504 + src/mesh_tools/assets/xyz_presets.jsonc | 349 + src/mesh_tools/builders/about.plugin.js | 199 + src/mesh_tools/jsconfig.json | 7 + src/mesh_tools/package-lock.json | 3102 +++ src/mesh_tools/package.json | 27 + src/mesh_tools/rollup.config.js | 33 + src/mesh_tools/src/actions.js | 56 + src/mesh_tools/src/generators/index.js | 4 + .../quickprimitives/polyhedron.action.js | 57 + .../quickprimitives/quickprimitives.action.js | 3 + .../quickprimitives/torusknot.action.js | 63 + .../src/generators/terrain.action.js | 432 + .../generators/terrain_style_editor.action.js | 177 + .../src/generators/text_mesh.action.js | 151 + .../xyz_math_surface_function.action.js | 302 + src/mesh_tools/src/index.js | 102 + .../src/operators/array_elements.action.js | 54 + src/mesh_tools/src/operators/index.js | 4 + .../src/operators/scatter.action.js | 100 + .../src/operators/split_edges.action.js | 96 + .../src/operators/subdivide.action.js | 170 + .../src/tools/bridge_edge_loops.action.js | 498 + .../src/tools/expand_selection.action.js | 21 + src/mesh_tools/src/tools/index.js | 4 + .../src/tools/laplacian_smooth.action.js | 66 + src/mesh_tools/src/tools/poke.action.js | 50 + .../src/tools/shrink_selection.action.js | 25 + src/mesh_tools/src/tools/to_sphere.action.js | 67 + .../src/tools/triangulate.action.js | 43 + .../src/tools/tris_to_quad.action.js | 134 + .../src/tools/uv_mapping/uv_mapping.action.js | 3 + .../uv_mapping/uv_project_view.action.js | 62 + .../uv_turnaround_projection.action.js | 130 + src/mesh_tools/src/utils/facetype.js | 94 + src/mesh_tools/src/utils/geometry.js | 83 + src/mesh_tools/src/utils/perlin.js | 76 + src/mesh_tools/src/utils/terrain_gen.js | 117 + .../src/utils/threejs_interoperability.js | 65 + src/mesh_tools/src/utils/utils.js | 797 + src/mesh_tools/src/utils/vector.js | 92 + 82 files changed, 33652 insertions(+), 2328 deletions(-) delete mode 100644 plugins/mesh_tools.js create mode 100644 plugins/mesh_tools/about.md create mode 100644 plugins/mesh_tools/icon.png create mode 100644 plugins/mesh_tools/mesh_tools.js create mode 100644 src/mesh_tools/.gitignore create mode 100644 src/mesh_tools/.vscode/settings.json create mode 100644 src/mesh_tools/TODO.md create mode 100644 src/mesh_tools/assets/actions.json create mode 100644 src/mesh_tools/assets/actions/array.png create mode 100644 src/mesh_tools/assets/actions/bridge_edge_loops_1_after.png create mode 100644 src/mesh_tools/assets/actions/bridge_edge_loops_2_after.png create mode 100644 src/mesh_tools/assets/actions/bridge_edge_loops_before.png create mode 100644 src/mesh_tools/assets/actions/laplacian_smooth_after.png create mode 100644 src/mesh_tools/assets/actions/laplacian_smooth_before.png create mode 100644 src/mesh_tools/assets/actions/poke_after.png create mode 100644 src/mesh_tools/assets/actions/poke_before.png create mode 100644 src/mesh_tools/assets/actions/polyhedron.png create mode 100644 src/mesh_tools/assets/actions/scatter.png create mode 100644 src/mesh_tools/assets/actions/split_edges.png create mode 100644 src/mesh_tools/assets/actions/subdivide_after.png create mode 100644 src/mesh_tools/assets/actions/subdivide_before.png create mode 100644 src/mesh_tools/assets/actions/terrain_solid.png create mode 100644 src/mesh_tools/assets/actions/terrain_wire.png create mode 100644 src/mesh_tools/assets/actions/text.png create mode 100644 src/mesh_tools/assets/actions/to_sphere_1_after.png create mode 100644 src/mesh_tools/assets/actions/to_sphere_1_before.png create mode 100644 src/mesh_tools/assets/actions/to_sphere_2_after.png create mode 100644 src/mesh_tools/assets/actions/to_sphere_2_before.png create mode 100644 src/mesh_tools/assets/actions/torus_knot.png create mode 100644 src/mesh_tools/assets/actions/triangulate_after.png create mode 100644 src/mesh_tools/assets/actions/triangulate_before.png create mode 100644 src/mesh_tools/assets/actions/tris_to_quad_after.png create mode 100644 src/mesh_tools/assets/actions/tris_to_quad_before.png create mode 100644 src/mesh_tools/assets/actions/uv_project_view_solid.png create mode 100644 src/mesh_tools/assets/actions/uv_project_view_uv.png create mode 100644 src/mesh_tools/assets/actions/uv_turnaround_projection_solid.png create mode 100644 src/mesh_tools/assets/actions/uv_turnaround_projection_uv.png create mode 100644 src/mesh_tools/assets/actions/xyz.png create mode 100644 src/mesh_tools/assets/icon.png create mode 100644 src/mesh_tools/assets/icon.svg create mode 100644 src/mesh_tools/assets/roboto_regular.json create mode 100644 src/mesh_tools/assets/xyz_presets.jsonc create mode 100644 src/mesh_tools/builders/about.plugin.js create mode 100644 src/mesh_tools/jsconfig.json create mode 100644 src/mesh_tools/package-lock.json create mode 100644 src/mesh_tools/package.json create mode 100644 src/mesh_tools/rollup.config.js create mode 100644 src/mesh_tools/src/actions.js create mode 100644 src/mesh_tools/src/generators/index.js create mode 100644 src/mesh_tools/src/generators/quickprimitives/polyhedron.action.js create mode 100644 src/mesh_tools/src/generators/quickprimitives/quickprimitives.action.js create mode 100644 src/mesh_tools/src/generators/quickprimitives/torusknot.action.js create mode 100644 src/mesh_tools/src/generators/terrain.action.js create mode 100644 src/mesh_tools/src/generators/terrain_style_editor.action.js create mode 100644 src/mesh_tools/src/generators/text_mesh.action.js create mode 100644 src/mesh_tools/src/generators/xyz_math_surface_function.action.js create mode 100644 src/mesh_tools/src/index.js create mode 100644 src/mesh_tools/src/operators/array_elements.action.js create mode 100644 src/mesh_tools/src/operators/index.js create mode 100644 src/mesh_tools/src/operators/scatter.action.js create mode 100644 src/mesh_tools/src/operators/split_edges.action.js create mode 100644 src/mesh_tools/src/operators/subdivide.action.js create mode 100644 src/mesh_tools/src/tools/bridge_edge_loops.action.js create mode 100644 src/mesh_tools/src/tools/expand_selection.action.js create mode 100644 src/mesh_tools/src/tools/index.js create mode 100644 src/mesh_tools/src/tools/laplacian_smooth.action.js create mode 100644 src/mesh_tools/src/tools/poke.action.js create mode 100644 src/mesh_tools/src/tools/shrink_selection.action.js create mode 100644 src/mesh_tools/src/tools/to_sphere.action.js create mode 100644 src/mesh_tools/src/tools/triangulate.action.js create mode 100644 src/mesh_tools/src/tools/tris_to_quad.action.js create mode 100644 src/mesh_tools/src/tools/uv_mapping/uv_mapping.action.js create mode 100644 src/mesh_tools/src/tools/uv_mapping/uv_project_view.action.js create mode 100644 src/mesh_tools/src/tools/uv_mapping/uv_turnaround_projection.action.js create mode 100644 src/mesh_tools/src/utils/facetype.js create mode 100644 src/mesh_tools/src/utils/geometry.js create mode 100644 src/mesh_tools/src/utils/perlin.js create mode 100644 src/mesh_tools/src/utils/terrain_gen.js create mode 100644 src/mesh_tools/src/utils/threejs_interoperability.js create mode 100644 src/mesh_tools/src/utils/utils.js create mode 100644 src/mesh_tools/src/utils/vector.js diff --git a/plugins.json b/plugins.json index 228191bd..ec1220d4 100644 --- a/plugins.json +++ b/plugins.json @@ -577,15 +577,15 @@ "variant": "both" }, "mesh_tools": { + "new_repository_format": true, "title": "MTools", - "icon": "fas.fa-vector-square", "author": "Malik12tree", - "description": "Adds helpful Mesh Modeling Tools, Operators and Generators!", - "version": "1.0.7", - "min_version": "4.7.0", - "about": "
To Sphere | Casts selected vertices into a sphere based on an influence |
Laplacian Smooth | Smoothens selected vertices by averaging the position of neighboring vertices |
Poke Faces | Creates a Fan out of selected faces |
Triangles To Quads | Trys to dissolve adjacent triangles into a quad |
Triangulate Faces | Cuts a face into triangles |
Project From View | Creates a UV map based on the vertices' position on the screen from the camera |
Cubic Projection | Creates a UV map based on the sides of a cube |
Subdivide | Splits the faces of a mesh into smaller faces, giving it a smooth appearance |
Split Edges | Splits and duplicates edges within a mesh, breaking 'links' between faces around those split edges |
Scatter | Scatters selected meshes on the active mesh |
Array | Creates an array of copies of the base object, with each copy being offset from the previous one |
Terrain | Generates Terrains procedurally with fully customized settings |
Text Mesh | Generate a mesh representation of a text with Opentype Fonts and custom settings. An OpenTypeFont is a format for scalable computer fonts that are stored in JSON files, a good converter is gero3.github.io/facetype.js |
XYZ Math Surface Function | Creates an xyz surface based on given inputs containing 23 already-made presets! |
Polyhedron Primitive | Generate the basic 4 regular polyhedron with custom detail |
Torus Knot Primitive | Generates a p-q torus knot with custom settings |
To Sphere | Casts selected vertices into a sphere based on an influence |
Laplacian Smooth | Smoothens selected vertices by averaging the position of neighboring vertices |
Poke Faces | Creates a Fan out of selected faces |
Triangles To Quads | Trys to dissolve adjacent triangles into a quad |
Triangulate Faces | Cuts a face into triangles |
Project From View | Creates a UV map based on the vertices' position on the screen from the camera |
Cubic Projection | Creates a UV map based on the sides of a cube |
Subdivide | Splits the faces of a mesh into smaller faces, giving it a smooth appearance |
Split Edges | Splits and duplicates edges within a mesh, breaking 'links' between faces around those split edges |
Scatter | Scatters selected meshes on the active mesh |
Array | Creates an array of copies of the base object, with each copy being offset from the previous one |
Terrain | Generates Terrains procedurally with fully customized settings |
Text Mesh | Generate a mesh representation of a text with Opentype Fonts and custom settings. An OpenTypeFont is a format for scalable computer fonts that are stored in JSON files, a good converter is gero3.github.io/facetype.js |
XYZ Math Surface Function | Creates an xyz surface based on given inputs containing 23 already-made presets! |
Polyhedron Primitive | Generate the basic 4 regular polyhedron with custom detail |
Torus Knot Primitive | Generates a p-q torus knot with custom settings |
when converting a font into a typeface font using the link above, make sure to disable "Reverse font direction".
` - - ], - form: { - text: {label:"Text", type:'textarea', value: "Hello, World!"}, - file: {label:"OpenType Font (Optional)", type:'file', extensions: ['json'],filetype: 'JSON', readtype:"text"}, - size: {label:"Size", type:"number", value:8,min:0}, - height: {label:"Thickness", type:"number", value:2,min:0}, - curveSegments: {label:"Resoultion", type:"number", value:1,min:0}, - _: "_", - bevelThickness: {label:"bevelThickness", type:"number", value:0,min:0}, - bevelSize:{label:"bevelSize", type:"number", value:8,min:0}, - bevelOffset:{label:"bevelOffset", type:"number", value:0,min:0}, - bevelSegments :{label:"bevelSegments", type:"number", value:1,min:0}, - }, - onConfirm(out){ - let content; - if (!out.file) { - content = RobotoRegular; - } else { - content = this.form.file.content; - if (!content.includes(`"glyphs"`)) { - Blockbench.showQuickMessage("Not a valid OpenType font") - throw new Error("Not a valid OpenType font"); - } - } - - let base64 = "data:text/plain;base64," + utoa(content); - const loader = new THREE.FontLoader(); - - loader.load(base64, function ( font ) { - function runEdit(s, amended=false) { - let elements = []; - Undo.initEdit({elements, selection: true}, amended); - const geometry = new THREE.TextGeometry( out.text, { - font: font, - size: s.size, - height: s.height, - curveSegments: s.curveSegments, - bevelEnabled: s.bevelThickness > 0, - bevelThickness: s.bevelThickness/16, - bevelSize: s.bevelSize/16, - bevelOffset: s.bevelOffset/16, - bevelSegments: s.bevelSegments - }); - let mesh = ThreeToBB.nonIndexed(geometry); - - mesh.init(); - elements.push(mesh); - mesh.select(); - Undo.finishEdit('MTools: Generate Mesh'); - } - runEdit(out); - - let s = out; // lazyness moment - Undo.amendEdit({ - size: {label:"Size", type:"number", value:s.size,min:0}, - height: {label:"Thickness", type:"number", value:s.height,min:0}, - curveSegments: {label:"Resoultion", type:"number", value:s.curveSegments,min:0}, - bevelThickness: {label:"bevelThickness", type:"number", value:s.bevelThickness,min:0}, - bevelSize:{label:"bevelSize", type:"number", value:s.bevelSize,min:0}, - bevelOffset:{label:"bevelOffset", type:"number", value:s.bevelOffset,min:0}, - bevelSegments :{label:"bevelSegments", type:"number", value:s.bevelSegments,min:0}, - }, form => { - runEdit(form,true) - }) - }); - } - }).show(); - $("#mt_typeface")[0].onclick = function() { - Blockbench.openLink("http://gero3.github.io/facetype.js/"); - } - } - }) - - let mtMolangParser; - let currentMTVariablesTextContent = ''; - let molangScript = $('script[src="lib/molang.umd.js"]')[0]; - //https://stackoverflow.com/questions/148441/how-can-i-get-the-content-of-the-file-specified-as-the-src-of-a-script-tag - function printScriptTextContent(script, cb) { - var xhr = new XMLHttpRequest(); - xhr.open("GET",script.src) - xhr.onreadystatechange = _ => { - if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) cb(xhr.responseText); - }; - xhr.send(); - } - - // this is so stupid - // just an add-in of more functions - printScriptTextContent(molangScript, function(f) { - eval(f.replace('Molang', '_Molang_').replace(`case"random_integer":return new u(125,s[0],s[1],s[2])`, `case"random_integer":return new u(125,s[0],s[1],s[2]);case 'acosh':return new u(126, s[0]);case 'asinh':return new u(127, s[0]);case 'atanh':return new u(128, s[0]);case 'cosh':return new u(129, s[0]);case 'sinh':return new u(130, s[0]);case 'tan':return new u(131, s[0]);case'tanh':return new u(132, s[0])`).replace('case 125:return r.randomInt(v(n.a),v(n.b))', 'case 125:return r.randomInt(v(n.a),v(n.b));case 126:return Math.acosh(v(n.a));case 127:return Math.asinh(v(n.a));case 128:return Math.atanh(v(n.a));case 129:return Math.cosh(v(n.a));case 130:return Math.sinh(v(n.a));case 131:return Math.tan(v(n.a));case 132:return Math.tanh(v(n.a))')) - mtMolangParser = new _Molang_(); - mtMolangParser.use_radians=true; - - // stolen from Animator.MolangParser.variableHandler; - mtMolangParser.variableHandler = function(variable) { - var inputs = currentMTVariablesTextContent.split(`\n`); - var i = 0; - while (i < inputs.length) { - let key, val; - [key, val] = inputs[i].split(/=(.+)/); - key = key.replace(/[\s;]/g, ''); - if (key === variable && val !== undefined) { - val = val.trim(); - return val[0] == `'` ? val : mtMolangParser.parse(val); - } - i++; - } - } - }); - const mathFuncs = ["abs","acos","asin","atan","atan2","ceil","clamp","cos","die_roll","die_roll_integer","exp","floor","hermite_blend","lerp","lerprotate","ln","max","min","min_angle","mod","pi","pow","random","random_integer","round","sin","sqrt","trunc","acosh","asinh","atanh","cosh","sinh","tan","tanh"]; - function mtParse(string) { - mathFuncs.forEach(f => { - if (f == 'pi') - string = string.replaceAll(f, "math." + f); - else - string = string.replaceAll(f+'(', "math." + f+'('); - }); - return string.replaceAll('amath.', 'a'); - } - - // Special thanks to the Blender3d addon "Add Mesh - Extra Objects" for the inspiration! :) - // and the presets of the addon, at: https://github.com/elfnor/blender_XYZ_surface_presets - // :) - const xyzpresets = { - TwistedTorus: {x:"-cos(p.u)*(6-(5/4 + sin(3*p.v))*sin(p.v-3*p.u))",y:"-(6-(5/4 + sin(3*p.v))*sin(p.v-3*p.u))*sin(p.u)",z:"cos(p.v-3*p.u)*(5/4+sin(3*p.v))",scale:1,uRange:[0,6.2831854820251465],uDivs:32,uWrap:!0,vRange:[0,6.2831854820251465],vDivs:16,vWrap:!0,vClose:!0}, - Bonbon: {x:"(p.u-3.3379)",y:"cos(p.u)*sin(p.v)",z:"cos(p.u)*cos(p.v)",scale:2,uRange:[0,6.2831854820251465],uDivs:16,uWrap:!1,vRange:[0,6.2831854820251465],vDivs:16,vWrap:!1,vClose:!1}, - Boy: {x:"(sq2 * cos(2*p.u)*pow(cos(p.v),2) + cos(p.u)*sin(2*p.v)) / (2 - alpha *sq2*sin(3*p.u)*sin(2*p.v))",y:"(sq2 * sin(2*p.u)*pow(cos(p.v),2) - sin(p.u)*sin(2*p.v)) / (2 - alpha *sq2*sin(3*p.u)*sin(2*p.v))",z:"(3*pow(cos(p.v),2)) / (2 - alpha*sq2*sin(3*p.u)*sin(2*p.v))",scale:4,uRange:[-1.5707963705062866,1.5707963705062866],uDivs:16,uWrap:!1,vRange:[0,3.1415927410125732],vDivs:32,vWrap:!1,vClose:!1,variables:"sq2 = 1.4142135623730951\nalpha=1"}, - Hexahedron: {x:"pow(cos(p.v),3)*pow(cos(p.u),3)",y:"pow(sin(p.u),3)",z:"pow(sin(p.v),3)*pow(cos(p.u),3)",scale:8,uRange:[-1.2999999523162842,1.2999999523162842],uDivs:16,uWrap:!1,vRange:[0,6.2831854820251465],vDivs:16,vWrap:!1,vClose:!1}, - Klein: {x:"(3*(1+sin(p.v)) + 2*(1-cos(p.v)/2)*cos(p.u))*cos(p.v)",y:"(4+2*(1-cos(p.v)/2)*cos(p.u))*sin(p.v)",z:"-2*(1-cos(p.v)/2)*sin(p.u)",scale:1,uRange:[0,6.2831854820251465],uDivs:16,uWrap:!0,vRange:[0,6.2831854820251465],vDivs:16,vWrap:!1,vClose:!1}, - Moebius: {x:"(cos(p.v)+p.u*cos(p.v/2)*cos(p.v))",y:"(p.u*sin(p.v/2))",z:"(sin(p.v)+p.u*cos(p.v/2)*sin(p.v))",scale:4,uRange:[-.4000000059604645,.4000000059604645],uDivs:4,uWrap:!1,vRange:[0,6.2831854820251465],vDivs:16,vWrap:!1,vClose:!1}, - Breather: {x:"-p.u + (2*rr*cosh(alpha*p.u)*sinh(alpha*p.u))/denom",y:"(2*ww*cosh(alpha*p.u)*(-(ww*cos(p.v)*cos(ww*p.v)) - sin(p.v)*sin(ww*p.v)))/denom",z:"(2*ww*cosh(alpha*p.u)*(-(ww*sin(p.v)*cos(ww*p.v)) + cos(p.v)*sin(ww*p.v)))/denom",scale:2,uRange:[-13.2,13.2],uDivs:16,uWrap:!1,vRange:[-37.4,37.4],vDivs:32,vWrap:!1,vClose:!1,variables:"alpha = 0.4\nrr= 1 - pow(alpha,2)\nww = sqrt(rr)\ndenom = alpha*( pow(ww*cosh(alpha*p.u),2) + pow(alpha*sin(ww*p.v),2) )"}, - RidgedTorus: {x:"outerradius*cos(p.u)+(ridgepower*sin(numofridges*p.u)+innerradius)*cos(p.u)*cos(p.v)",y:"outerradius*sin(p.u)+(ridgepower*sin(numofridges*p.u)+innerradius)*sin(p.u)*cos(p.v)",z:"(ridgepower*sin(numofridges*p.u)+innerradius)*sin(p.v)",scale:1,uRange:[0,6.2831854820251465],uDivs:32,uWrap:!1,vRange:[0,6.2831854820251465],vDivs:8,vWrap:!1,vClose:!1,variables:"outerradius = 5\nridgepower = 0.6\ninnerradius = 2\nnumofridges = 10"}, - CliffordTorus: {x:"cos(p.u+p.v)/(sq2+cos(p.v-p.u))",y:"sin(p.v-p.u)/(sq2+cos(p.v-p.u))",z:"sin(p.u+p.v)/(sq2+cos(p.v-p.u))",scale:4,uRange:[0,3.1415927410125732],uDivs:8,uWrap:!1,vRange:[0,6.2831854820251465],vDivs:32,vWrap:!1,vClose:!1,variables:"sq2 = 1.4142135623730951"}, - Cyclide: {x: "(dd*(cc - aa*cos(p.u)*cos(p.v) ) + bb*bb*cos(p.u)) / denom",y: "(bb*sin(p.u)*(aa-dd*cos(p.v) ) )/denom",z: "(bb*sin(p.v)*(cc*cos(p.u)-dd ) )/denom",scale: 4,uRange: [0,6.2831854820251465],uDivs: 16,uWrap: false,vRange: [0,6.2831854820251465],vDivs: 8,vWrap: false,vClose: false,variables: `aa = 1\nbb = 0.98\ncc = 0.199\ndd = 0.3\ndenom = (aa-cc*cos(p.u)*cos(p.v))`}, - Shell: {x:"(cos(p.v)*(1+cos(p.u))*sin(p.v/8))",y:"(sin(p.u)*sin(p.v/8)+cos(p.v/8)*1.5)",z:"(sin(p.v)*(1+cos(p.u))*sin(p.v/8))",scale:4,uRange:[0,6.2831854820251465],uDivs:8,uWrap:!0,vRange:[0,12.566370964050293],vDivs:32,vWrap:!1,vClose:!1}, - Catalan: {x:"p.u-sin(p.u)*cosh(p.v)",y:"4*sin(1/2*p.u)*sinh(p.v/2)",z:"1-cos(p.u)*cosh(p.v)",scale:1,uRange:[-3.1415927410125732,9.42477798461914],uDivs:24,uWrap:!1,vRange:[-2,2],vDivs:8,vWrap:!1,vClose:!1}, - Dini: {x:"radius*cos(p.u)*sin(p.v)",y:"2*(((cos(p.v)+ln(tan(p.v/2)+1E-2)) + twistrot*p.u)+3.4985)",z:"radius*sin(p.u)*sin(p.v)",scale:1,uRange:[0,4*Math.PI],uDivs:16,uWrap:!1,vRange:[0,2],vDivs:8,vWrap:!1,vClose:!1,variables:"radius = 4\ntwistrot=0.2"}, - Catenoid: {x:"2*cosh(p.v/2)*cos(p.u)",y:"p.v",z:"2*cosh(p.v/2)*sin(p.u)",scale:1,uRange:[-3.1415927410125732,3.1415927410125732],uDivs:24,uWrap:!0,vRange:[-3.1415927410125732,3.1415927410125732],vDivs:8,vWrap:!1,vClose:!1}, - Cochlea: {x:"p.v*cos(p.u)",y:"p.v*sin(p.u)",z:"(0.4*p.u-2.5383)",scale:4,uRange:[0,12.566370964050293],uDivs:16,uWrap:!1,vRange:[0,2],vDivs:16,vWrap:!1,vClose:!1}, - Cosinus: {x:"p.u",y:"sin(pi* ( pow(p.u,2) + pow(p.v,2) ) )/2",z:"p.v",scale:8,uRange:[-1,1],uDivs:16,uWrap:!1,vRange:[-1,1],vDivs:16,vWrap:!1,vClose:!1}, - Enneper: {x:"p.u -pow(p.u,3)/3 + p.u*pow(p.v,2)",y:"pow(p.u,2) - pow(p.v,2)",z:"p.v -pow(p.v,3)/3 + p.v*pow(p.u,2)",scale:1,uRange:[-2,2],uDivs:8,uWrap:!1,vRange:[-2,2],vDivs:8,vWrap:!1,vClose:!1}, - Helicoidal: {x:"sinh(p.v)*sin(p.u)",y:"3*p.u",z:"-sinh(p.v)*cos(p.u)",scale:1,uRange:[-3.1415927410125732,3.1415927410125732],uDivs:16,uWrap:!1,vRange:[-3.1415927410125732,3.1415927410125732],vDivs:8,vWrap:!1,vClose:!1}, - Helix: {x:"(1-0.1*cos(p.v))*cos(p.u)/0.1",y:"0.1*(sin(p.v) + p.u/1.7 -10)/0.1 + 5",z:"(1-0.1*cos(p.v))*sin(p.u)/0.1",scale:1,uRange:[0,12.566370964050293],uDivs:32,uWrap:!1,vRange:[0,6.2831854820251465],vDivs:8,vWrap:!1,vClose:!1}, - Hyperhelicoidal: {x:"(sinh(p.v)*cos(3*p.u))/(1+cosh(p.u)*cosh(p.v))",y:"(cosh(p.v)*sinh(p.u))/(1+cosh(p.u)*cosh(p.v))",z:"(sinh(p.v)*sin(3*p.u))/(1+cosh(p.u)*cosh(p.v))",scale:8,uRange:[-3.1415927410125732,3.1415927410125732],uDivs:32,uWrap:!1,vRange:[-3.1415927410125732,3.1415927410125732],vDivs:8,vWrap:!1,vClose:!1}, - PseudoCatenoid: {x:"2.2*(2*cosh(p.v/2)*cos(p.u))",y:"1.51166 * (2*cosh(p.v/2)*sin(p.u) * sin((2.2*(2*cosh(p.v/2)*cos(p.u)) - -11.0404)*2*pi*1/22.0513) + 1.8*(p.v) * cos((2.2*(2*cosh(p.v/2)*cos(p.u)) - -11.0404)*2*pi*1/22.0513))",z:"1.51166 * (2*cosh(p.v/2)*sin(p.u) * cos((2.2*(2*cosh(p.v/2)*cos(p.u)) - -11.0404)*2*pi*1/22.0513) - 1.8*(p.v) * sin((2.2*(2*cosh(p.v/2)*cos(p.u)) - -11.0404)*2*pi*1/22.0513))",scale:1,uRange:[-3.1415927410125732,3.1415927410125732],uDivs:32,uWrap:!1,vRange:[-3.1415927410125732,3.1415927410125732],vDivs:14,vWrap:!1,vClose:!1}, - PseudoSphere: {x:"cos(p.u)*cos(p.v)+sin((sin(p.u)+1)*2*pi)",y:"4*sin(p.u)",z:"cos(p.u)*sin(p.v)+cos((sin(p.u)+1)*2*pi)",scale:2,uRange:[-1.5707963705062866,1.5707963705062866],uDivs:32,uWrap:!1,vRange:[0,6.2831854820251465],vDivs:8,vWrap:!1,vClose:!1}, - Sine: {x:"sin(p.u)",y:"sin(p.v)",z:"sin(p.u+p.v)",scale:8,uRange:[0,6.2831854820251465],uDivs:16,uWrap:!0,vRange:[0,6.2831854820251465],vDivs:16,vWrap:!0,vClose:!0}, - Snake: {x:"1.2*(1 -p.v/(2*pi))*cos(3*p.v)*(1 + cos(p.u)) + 3*cos(3*p.v)",y:"9*p.v/(2*pi) + 1.2*(1 - p.v/(2*pi))*sin(p.u)",z:"1.2*(1 -p.v/(2*pi))*sin(3*p.v)*(1 + cos(p.u)) + 3*sin(3*p.v)",scale:1,uRange:[0,6.2831854820251465],uDivs:7,uWrap:!1,vRange:[0,6.2831854820251465],vDivs:42,vWrap:!1,vClose:!1}, - SteroSphere: {x:"(2*p.u/(p.u*p.u+p.v*p.v+1))",y:"((p.u*p.u+p.v*p.v-1)/(p.u*p.u+p.v*p.v+1))",z:"(2*p.v/(p.u*p.u+p.v*p.v+1))",scale:8,uRange:[-2,2],uDivs:16,uWrap:!1,vRange:[-2,2],vDivs:16,vWrap:!1,vClose:!1}, - Torus: {x:"(1+0.5*cos(p.u))*cos(p.v)",y:"0.5*sin(p.u)",z:"(1+0.5*cos(p.u))*sin(p.v)",scale:6,uRange:[0,6.2831854820251465],uDivs:8,uWrap:!1,vRange:[0,6.2831854820251465],vDivs:12,vWrap:!1,vClose:!1} - }; - new Action("xyzmathsurfacefunction", { - name: "XYZ Math Surface", - icon: "fas.fa-brain", - description: "Creates an xyz surface based on given inputs. Also contains already-made 23 presets!", - click(){ - let options = {}; - for (const key in xyzpresets) { - options[key] = key; - } - let msettings = localStorage.getItem('mt_xyzSettings'); - let presetBeforeUpdate; - if (msettings != null) { - msettings = JSON.parse(msettings); - presetBeforeUpdate = msettings.preset; - } else { - msettings = xyzpresets.TwistedTorus; - presetBeforeUpdate = "TwistedTorus"; - } - let justOpened = true; - let dial = new Dialog ({ - title: "XYZ Math Surface Function", - part_order: ["form","lines"], - buttons: ["Save Settings To Memory", "Confirm","Cancel"], - cancelIndex: 2, - confirmIndex: 1, - width: 650, - onFormChange(data){ - if (justOpened) { // this just for skipping loading saved settings - justOpened=false; - return; - } - if (data.preset == presetBeforeUpdate) return; // stop call stack - presetBeforeUpdate = data.preset; - - this.setFormValues(xyzpresets[data.preset]); - }, - form: { - preset: {label:"Preset", type:"select", options}, - x: {label:"X", type:'text', value:xyzpresets['TwistedTorus'].x}, - y: {label:"Y", type:'text', value:xyzpresets['TwistedTorus'].y}, - z: {label:"Z", type:'text', value:xyzpresets['TwistedTorus'].z}, - scale: {label: "Scale", type:'number', value:1}, - _:"_", - uRange: {label:"U Range", type:'vector', dimensions:2, value: [0,6.2831854820251465]}, - uDivs: {label:"U Divisions", type:'number', min:2, value:32}, - uWrap: {label:"U Wrap", type:"checkbox", value:true}, - vRange: {label:"V Range", type:'vector', dimensions:2, value: [0,6.2831854820251465]}, - vDivs: {label:"V Divisions", type:'number', min:2, value:16}, - vWrap: {label:"V Wrap", type:"checkbox", value:true}, - vClose: {label:"V Close", type:"checkbox", value:true}, - __:"_", - variables: {label:"Variables", type:'textarea', placeholder:"List the variables you want to use via name = value"} - }, - onConfirm(out){ - out.x = mtParse(out.x); - out.y = mtParse(out.y); - out.z = mtParse(out.z); - currentMTVariablesTextContent = mtParse(out.variables); - function runEdit(s, amended=false) { - let elements = []; - Undo.initEdit({elements, selection: true}, amended); - let mesh = new Mesh({vertices:{}}); - - let [umin,umax] = out.uRange; - let usteps = s.uDivs; - let uinc = (umax - umin)/usteps; - - let [vmin,vmax] = out.vRange; - let vsteps = s.vDivs; - let vinc = (vmax - vmin)/vsteps; - - let uRange = usteps+1; - let vRange = vsteps+1; - if (out.uWrap) uRange -= 1; - if (out.vWrap) vRange -= 1; - - let vertices = []; - let uvPositions = []; - for (let j = 0; j < vRange; j++) { - let v = vmin + (j*vinc);; - for (let i = 0; i < uRange; i++) { - let u = umin + (i*uinc); - - mtMolangParser.global_variables = {'p.u': u,'p.v': v}; - - let x = mtMolangParser.parse(out.x) * out.scale; - let y = mtMolangParser.parse(out.y) * out.scale; - let z = mtMolangParser.parse(out.z) * out.scale; - if (isNaN(x) || Math.abs(x) === Infinity) {x = 0}; - if (isNaN(y) || Math.abs(y) === Infinity) {y = 0}; - if (isNaN(z) || Math.abs(z) === Infinity) {z = 0}; - - vertices.push(mesh.addVertices([x,y,z])[0]); - uvPositions.push([i,j]) - } - } - for (let y = 0; y < vsteps; y++) { - for (let x = 0; x < usteps; x++) { - yNext = y+1; - xNext = x+1; - - if (out.vWrap && yNext >= vRange) yNext = 0; - if (out.uWrap && xNext >= uRange) xNext = 0; - - let vertexIndices = [ - (yNext * uRange) + xNext, - (yNext * uRange) + x, - (y * uRange) + x, - (y * uRange) + xNext - ] - let face = new MeshFace(mesh, { - vertices: [ - vertices[vertexIndices[0]], - vertices[vertexIndices[1]], - vertices[vertexIndices[2]], - vertices[vertexIndices[3]], - ], - }); - let uv = [ - uvPositions[vertexIndices[0]].slice().V3_divide(uRange-1,vRange-1).V3_multiply(Project._texture_width,Project._texture_height), - uvPositions[vertexIndices[1]].slice().V3_divide(uRange-1,vRange-1).V3_multiply(Project._texture_width,Project._texture_height), - uvPositions[vertexIndices[2]].slice().V3_divide(uRange-1,vRange-1).V3_multiply(Project._texture_width,Project._texture_height), - uvPositions[vertexIndices[3]].slice().V3_divide(uRange-1,vRange-1).V3_multiply(Project._texture_width,Project._texture_height) - ] - face.uv[face.vertices[0]] = [uv[0][0],uv[0][1]]; - face.uv[face.vertices[1]] = [uv[1][0],uv[1][1]]; - face.uv[face.vertices[2]] = [uv[2][0],uv[2][1]]; - face.uv[face.vertices[3]] = [uv[3][0],uv[3][1]]; - mesh.addFaces(face); - } - } - delete uvPositions; - // fills end caps - if (out.vClose && out.uWrap && !out.vWrap) { - for (let x = 1; x < usteps-1; x++) { - let face1 = new MeshFace(mesh, {vertices: [ - vertices[usteps - 1], - vertices[usteps - 1 - x], - vertices[usteps - 2 - x], - ]}); - let face2 = new MeshFace(mesh, {vertices: [ - vertices[vsteps * uRange], - vertices[vsteps * uRange + x], - vertices[vsteps * uRange + x + 1], - ]}); - mesh.addFaces(face1, face2); - } - } - mesh.init(); - elements.push(mesh); - mesh.select(); - Undo.finishEdit('MTools: Generate Mesh'); - } - runEdit(out); - Undo.amendEdit({ - uDivs: {label: "U divisions", value: out.uDivs, min:2}, - vDivs: {label: "V divisions", value: out.vDivs, min:2} - }, form => { - runEdit(form,true) - }) - } - }) - dial.show(); - if(msettings != null) { - dial.setFormValues(msettings); - } - - let saveBtn = $(`button:contains("Save Settings To Memory")`); - saveBtn.off("click"); - saveBtn.on("click", function() { - let mmsettings = dial.getFormResult(); - localStorage.setItem("mt_xyzSettings", JSON.stringify(mmsettings)); - }); - } - }) - const quickPrimitivesI = "offline_bolt"; - new Action("polyhedron", { - name: "Polyhedron", - icon: quickPrimitivesI, - click(){ - new Dialog ({ - title: "Quick Primitive [ Polyhedron ]", - form: { - select: {label: "Hedron", type:"select", options: {Icosahedron:"Icosahedron", Dodecahedron:"Dodecahedron",Octahedron:"Octahedron", Tetrahedron:"Tetrahedron"}}, - radius: {label: "Radius", value: 8, type:'number'}, - detail: {label: "Detail", value: 2, min:1,max:6, type:'number'} - }, - onConfirm(out){ - function runEdit(s, amended=false) { - let elements = []; - Undo.initEdit({elements, selection: true}, amended); - const geometry = new THREE[out.select + 'BufferGeometry'](s.radius, s.detail-1); - let mesh = ThreeToBB.nonIndexed(geometry); - - mesh.init(); - elements.push(mesh); - mesh.select(); - UVEditor.setAutoSize(null, true, Object.keys(mesh.faces)); - UVEditor.selected_faces.empty(); - Undo.finishEdit('MTools: Generate Mesh'); - } - runEdit(out); - Undo.amendEdit({ - radius: {label: "Radius", value: out.radius}, - detail: {label: "Detail", value: out.detail, min:1,max:6} - }, form => { - runEdit(form,true) - }) - } - }).show(); - } - }) - new Action("torusknot", { - name: "Torus Knot", - icon: quickPrimitivesI, - click(){ - new Dialog ({ - title: "Quick Primitive [ Torus Knot ]", - lines: [`P and Q should be coprime integers meaning non should be divisible by the other.
`], - form: { - r: {label:"Torus Radius", type:"number", value:8}, - t: {label:"Tube Radius", type:"number", value:3.4, step:.2}, - ts: {label:"Tubular Segments", type:"number", value:25}, - rs : {label:"Radial Segments", type:"number", value:5}, - p: {label:"P", type:"number", value:2}, - q : {label:"Q", type:"number", value:3}, - }, - onConfirm(out){ - function runEdit(s, amended=false) { - let elements = []; - Undo.initEdit({elements, selection: true}, amended); - const geometry = new THREE.TorusKnotBufferGeometry(s.r,s.t,s.ts,s.rs,s.p,s.q); - let mesh = ThreeToBB.indexed(geometry, true); - - mesh.init(); - elements.push(mesh); - mesh.select(); - UVEditor.setAutoSize(null, true, Object.keys(mesh.faces)); - UVEditor.selected_faces.empty(); - Undo.finishEdit('MTools: Generate Mesh'); - } - runEdit(out); - let s = out; - - Undo.amendEdit({ - r: {label:"Torus Radius", type:"number", value:s.r}, - t: {label:"Tube Radius", type:"number", value:s.t, step:.2}, - ts: {label:"Tubular Segments", type:"number", value:s.ts}, - rs : {label:"Radial Segments", type:"number", value:s.rs}, - p: {label:"P", type:"number", value:s.p}, - q : {label:"Q", type:"number", value:s.q}, - }, form => { - runEdit(form,true) - }) - } - }).show(); - } - }) - new Action("quickprimitives", { - name: "Quick Primitives", - icon: "fas.fa-shapes", - children: ['polyhedron', 'torusknot'] - }) - - new Action("meshtools_gens", { - name: "MTools Generate", - icon: "fas.fa-vector-square", - children: [ - 'terrain_action', - 'terrainse', - '_', - 'textmesh', - 'xyzmathsurfacefunction', - 'quickprimitives' - ], - condition: { modes: ['edit'], features: ['meshes'] } - }); - new Action("meshtools", { - name: "MTools", - icon: "fas.fa-vector-square", - children: [ - 'to_sphere', - 'laplacian_smooth', - '_', - 'poke', - 'tris_to_quad', - 'triangulate', - '_', - 'uv_mapping', - '_', - 'expand_selection', - 'shrink_selection', - - // - - 'subdivide', - 'split_edges', - '_', - 'scatter', - 'array_elements' - ], - }); - Mesh.prototype.menu.structure.unshift('meshtools'); - MenuBar.addAction('meshtools_gens', 'filter'); - }, - onunload() { - const forceRemove = (id, array) => { - let action = array.find(e => e.id == id); - while (action) { - array.remove(action); - action = array.find(e => e.id == id); - } - } - - BarItems['quickprimitives'].children.forEach(e => BarItems[e]?.delete() ); - BarItems['uv_mapping'].children.forEach(e => BarItems[e]?.delete() ); - BarItems['meshtools'].children.forEach(e => BarItems[e]?.delete() ); - BarItems['meshtools_gens'].children.forEach(e => BarItems[e]?.delete() ); - - - forceRemove('meshtools_gens', MenuBar.menues.tools.structure); - forceRemove('meshtools', Mesh.prototype.menu.structure); - }, -}); -})() \ No newline at end of file diff --git a/plugins/mesh_tools/about.md b/plugins/mesh_tools/about.md new file mode 100644 index 00000000..916a59c5 --- /dev/null +++ b/plugins/mesh_tools/about.md @@ -0,0 +1,783 @@ + +Connects multiple edge loops with faces.
+Casts selected vertices into a smooth, spherical shape with adjustable influence.
+Smoothens selected vertices by averaging the position of neighboring vertices.
+Generates a fan out of a face.
+Attempts to merge adjacent triangles into quadrilaterals.
+Splits selected faces into triangles.
+Projects the selected faces to the UV map from the camera.
+Unwraps the UV map from the 6 sides of a cube.
+Expands the selection with neighboring vertices.
+Shrinks the selection with neighboring vertices.
+Splits the faces of a mesh into smaller faces, giving it a smooth appearance.
+Splits and duplicates edges within a mesh, breaking 'links' between faces around those split edges.
+Scatters selected meshes on the active mesh.
+Generates an array of copies of the base object, with each copy being offset from the previous one.
+Generates terrains procedurally with fully customized settings.
+Configure the Custom color gradient style of the terrain generator.
+Converts text into a 3D object, ideal for creating signs or logos.
+Generates an xyz surface based on mathematical equations containing 23 pre-built presets!
+Generate a polyhedron such as an Icosahedron, a Dodecahedron, an Octahedron or a Tetrahedron.
+Generate a Torus Knot with fully customized settings.
+P and Q should be coprime integers meaning non should be divisible by the other.
`, + ], + form: { + r: { label: "Torus Radius", type: "number", value: 8 }, + t: { label: "Tube Radius", type: "number", value: 3.4, step: 0.2 }, + ts: { label: "Tubular Segments", type: "number", value: 25 }, + rs: { label: "Radial Segments", type: "number", value: 5 }, + p: { label: "P", type: "number", value: 2 }, + q: { label: "Q", type: "number", value: 3 }, + }, + onConfirm(out) { + runEdit(out); + let s = out; + + Undo.amendEdit( + { + r: { label: "Torus Radius", type: "number", value: s.r }, + t: { + label: "Tube Radius", + type: "number", + value: s.t, + step: 0.2, + }, + ts: { label: "Tubular Segments", type: "number", value: s.ts }, + rs: { label: "Radial Segments", type: "number", value: s.rs }, + p: { label: "P", type: "number", value: s.p }, + q: { label: "Q", type: "number", value: s.q }, + }, + (form) => { + runEdit(form, true); + } + ); + }, + }); + action("torusknot", () => { + dialog.show(); + }); + + action("generators"); + + const deletables = []; + /** + * @type {Arrayvf5#67J{cS9bGTNzp$c8yhGRnZM%y2U!VbPhaSMfzq{`Cut*u31XDQzN
zS}fhGt5c`!-0tq<542?(80iVA+D^X|2oW?0<92_imgqmuW?O7hBmAbP{h@#Jx~Yaz
z^FrGuJTS7nFJOL@FMZCt2O0gV4i$zO@FcXc!s8$r_g7B1qY|(MSg3)1>}X0+GPXo!
zFf~Wy#BA!}N|aeecYgU?NRB^CRd$nD`ZqKN^H?Dz@yOXbgn^LjaV#kqc7SEtw>5QM
z&TSZ=8~swa*IL~)1A%^{2p)t7qQ?7{&Bf9k$P|*?9Xa0bZCMIO>ql9aT?5Nzssk5s
z3&X@iKNh%