Unity点选构建Mesh并保存OBJ
发表于2018-06-28
项目中策划可能会有这样的需求,任意挑选某一片区域,表明是有某种用途的。没有特别好的方法前,使用的是用小方格来代替,并最终构建mesh保存下来的方法,这样做程序的就很方便用了。
1、简单shader编写
通过Shader显示我们所编辑的内容
Shader "Custom/BlockShader" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _Size ("Size",Vector) = (64,32,0,0) //在面板上显示长多少格,宽多少格 _Color ("Color", Color) = (0,0,0,0.5) _RimColor ("Rim Color", Color) = (1,1,0,1) _RimPower ("Rim Power", Range(0,0.2)) = 0.01 _HitPoint ("Hit Point",Vector) = (0.0,1,0.0,0.5)//鼠标点击点 _SelectColor ("Select Color",Color) = (0,1,0,1)//选中颜色 _HitColor ("Already hit Color", Color) = (0.2,0.8,0.9,0.5) } SubShader { Tags { "Queue"="Transparent" } CGPROGRAM #pragma surface surf Lambert alpha #pragma target 3.0 sampler2D _MainTex; half4 _Size; fixed4 _Color; fixed4 _RimColor; float _RimPower; float4 _HitPoint; fixed4 _SelectColor; fixed4 _HitColor; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { float2 uv = IN.uv_MainTex; fixed4 c = _Color; float radiox = 1.0 / (_Size.x * 2);//每格大小的一半 float radioy = 1.0 / (_Size.y * 2); float nx = floor(uv.x/(radiox * 2)) * radiox * 2 + radiox;//找到格的中心点 float ny = floor(uv.y/(radioy * 2)) * radioy * 2 + radioy; fixed4 tc = tex2D (_MainTex, float2(nx,ny) ); if(tc.g > 0.85) //因为选中我设置为了绿色(0,1,0,1) { c = _HitColor; //if(uv.x >= nx-radiox && uv.x <nx+ radiox && uv.y >= ny-radioy && uv.y <= ny+radioy) //{ // c = _HitColor; //} } if(uv.x >= _HitPoint.x-radiox && uv.x <_HitPoint.x+ radiox && uv.y >= _HitPoint.y-radioy && uv.y <= _HitPoint.y+radioy) { c = _SelectColor;//选中颜色 } if(fmod(uv.x ,(radiox * 2)) < (radiox * 2) * _RimPower)//边框 { c = _RimColor; } if(fmod(uv.y ,(radioy * 2)) < (radioy * 2) * _RimPower) { c = _RimColor; } o.Albedo = c.rgb; o.Alpha = 0.5; } ENDCG } FallBack "Diffuse" }
下一步,需要每套的脚本控制
2、脚本控制
主要是为了运行时,通过点击生成方格
using System.Collections.Generic; using UnityEngine; /** * * 用于编辑器美术使用脚本,功能为画地形方块 * */ public class TerrianBlockControl : MonoBehaviour { public int width; public int height; public Texture2D texture; #if UNITY_EDITOR private Material material; private MeshRenderer meshRenderer; private RaycastHit hit; private float ratioW; private float ratioH; private int[,] matrix; void Start() { meshRenderer = GetComponent<MeshRenderer>(); material = meshRenderer.material; texture = new Texture2D(width * 3, height * 3); material.SetTexture("_MainTex", texture); ratioW = 1.0f/width; ratioH = 1.0f/height; material.SetVector("_Size", new Vector4(width, height, 0, 0)); matrix = new int[width,height]; MatrixInit(); } public void ChangeSize(int x, int y) { width = x; height = y; ratioW = 1.0f / width; ratioH = 1.0f / height; matrix = new int[width,height]; MatrixInit(); } public void Update() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out hit)) {//算出每个方格的正中心坐标 float x = Mathf.FloorToInt(hit.textureCoord.x/ratioW)*ratioW + ratioW*0.5f; float y = Mathf.FloorToInt(hit.textureCoord.y / ratioH) * ratioH + ratioH * 0.5f; material.SetVector("_HitPoint",new Vector4(x,y,0,0)); } if (Input.GetMouseButtonDown(0)) {//算出所在格子的坐标 int x = Mathf.FloorToInt(hit.textureCoord.x/ratioW) * 3 + 1; int y = Mathf.FloorToInt(hit.textureCoord.y/ratioH) * 3 + 1; Color c = texture.GetPixel(x, y); if (c.g >= 0.9f) { texture.SetPixel(x, y, Color.black); matrix[(x - 1) / 3, (y - 1) / 3] = 0; } else { texture.SetPixel(x, y, Color.green); matrix[(x - 1) / 3, (y - 1) / 3] = 1; } texture.Apply(true); material.SetTexture("_MainTex",texture); } } void MatrixInit() { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { matrix[i, j] = 0; } } } List<rect> rects; [ContextMenu("Save")] public GameObject Save() { rects = new List<rect>(); for (int i = 0; i < width; i++) //合并,把我们已经画好的方格进行合并 { for (int j = 0; j < height; j++) { if (matrix[i, j] == 1) { rect r = new rect(); r.dl = new Vector3(i,j,0); r.dr = new Vector3(i+1,j,0); r.ul = new Vector3(i,j+1,0); r.ur = new Vector3(i+1,j+1); r.length = 1; matrix[i, j] = 0; for (int k = i+1; k < width; k++) //在width方向上进行合并 { if (matrix[k, j] == 1) { r.dr=new Vector3(k+1,j,0); r.ur = new Vector3(k+1,j+1,0); matrix[k, j] = 0; r.length++; } else { break; } } for (int k = i-1; k >= 0; k--) { if (matrix[k, j] == 1) { r.dl = new Vector3(k,j,0); r.ul = new Vector3(k,j+1,0); matrix[k, j] = 0; r.length++; } else { break; } } rects.Add(r); } } } for (int i = 0; i < rects.Count; i++) //把合并过的,相邻的且大小相等的再合并 { if (!rects[i].used && rects[i].length != 0) { for (int j = 0; j < rects.Count; j++) {//没有合并过 不是自身 if (!rects[j].used && i != j && rects[j].length != 0) { if (rects[i].length == rects[j].length) { if (rects[i].dl.x == rects[j].dl.x) { if (rects[i].ul.y - rects[j].ul.y == 1) { rects[i].dl = rects[j].dl; rects[i].dr = rects[j].dr; rects[i].used = true; rects[j].length = 0; } if (rects[i].ul.y - rects[j].ul.y == -1) { rects[i].ul = rects[j].ul; rects[i].ur = rects[j].ur; rects[i].used = true; rects[j].length = 0; } } } } } } if (rects[i].length != 0 && rects[i].ul.y - rects[i].dl.y == 1) rects[i].used = true; } return creat(rects); } class rect { public Vector3 dl; public Vector3 dr; public Vector3 ul; public Vector3 ur; public int length; public bool used; } //创建mesh GameObject creat(List<rect> rects_Old ) { List<rect> rects = new List<rect>(); foreach (var rr in rects_Old) { if(rr.used) rects.Add(rr); } GameObject mp = GameObject.Find("MaskParent"); if(mp == null) mp = new GameObject("MaskParent"); mp.transform.position = Vector3.zero; GameObject go = new GameObject(transform.name); go.transform.parent = mp.transform; go.transform.localPosition = Vector3.zero; MeshFilter mf = go.AddComponent<MeshFilter>(); MeshRenderer mr = go.AddComponent<MeshRenderer>(); mr.material = new Material(Shader.Find("Diffuse")); Mesh mesh = new Mesh(); Vector3[] vectices = new Vector3[rects.Count*4]; int[] trangles = new int[rects.Count * 6]; Vector3 startPos = transform.position; float scalex = transform.localScale.x; float scaley = transform.localScale.y; startPos.x -= scalex * 0.5f; startPos.z -= scaley * 0.5f; int len = 0; for (int i = 0; i < rects.Count; i++) {//添加mesh的Vectices rect r = rects[i]; Vector3 dl = new Vector3(startPos.x + ratioW * r.dl.x * scalex, startPos.z + ratioH * r.dl.y * scaley, 0); Vector3 dr = new Vector3(startPos.x + ratioW * r.dr.x * scalex, startPos.z + ratioH * r.dr.y * scaley, 0); Vector3 ul = new Vector3(startPos.x + ratioW * r.ul.x * scalex, startPos.z + ratioH * r.ul.y * scaley, 0); Vector3 ur = new Vector3(startPos.x + ratioW * r.ur.x * scalex, startPos.z + ratioH * r.ur.y * scaley, 0); vectices[i*4] = dl; vectices[i * 4 + 1] = dr; vectices[i * 4 + 2] = ul; vectices[i * 4 + 3] = ur;//根据上面的点顺时针画三角形 trangles[i*6] = i*4; trangles[i * 6 + 1] = i * 4 +2; trangles[i * 6 + 2] = i * 4 +1; trangles[i * 6 + 3] = i * 4 +1; trangles[i * 6 + 4] = i * 4 +2; trangles[i * 6 + 5] = i * 4 +3; } mesh.vertices = vectices; mesh.triangles = trangles; mesh.RecalculateNormals(); mesh.RecalculateBounds(); mf.mesh = mesh; return go; } #endif }
根据以上两步能得到画好的mesh图形,还需要保存。
3、保存自创Mesh
可能会有些麻烦,参考这篇文章才成功导出成OBJ文件,在unity中生成FBX。
using System; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine; public class ObjExporter { // // Static Methods // public static void MeshToFile(MeshFilter mf, string filename, float scale) { using (StreamWriter streamWriter = new StreamWriter(filename)) { streamWriter.Write(ObjExporter.MeshToString(mf, scale)); } } public static string MeshToString(MeshFilter mf, float scale) { Mesh mesh = mf.mesh; Material[] sharedMaterials = mf.GetComponent<Renderer>().sharedMaterials; Vector2 textureOffset = mf.GetComponent<Renderer>().material.GetTextureOffset("_MainTex"); Vector2 textureScale = mf.GetComponent<Renderer>().material.GetTextureScale("_MainTex"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("mtllib design.mtl").Append("\n"); stringBuilder.Append("g ").Append(mf.name).Append("\n"); Vector3[] vertices = mesh.vertices; for (int i = 0; i < vertices.Length; i++) { Vector3 vector = vertices[i]; stringBuilder.Append(string.Format("v {0} {1} {2}\n", vector.x * scale, vector.z * scale, vector.y * scale)); } stringBuilder.Append("\n"); Dictionary<int, int> dictionary = new Dictionary<int, int>(); if (mesh.subMeshCount > 1) { int[] triangles = mesh.GetTriangles(1); for (int j = 0; j < triangles.Length; j += 3) { if (!dictionary.ContainsKey(triangles[j])) { dictionary.Add(triangles[j], 1); } if (!dictionary.ContainsKey(triangles[j + 1])) { dictionary.Add(triangles[j + 1], 1); } if (!dictionary.ContainsKey(triangles[j + 2])) { dictionary.Add(triangles[j + 2], 1); } } } for (int num = 0; num != mesh.uv.Length; num++) { Vector2 vector2 = Vector2.Scale(mesh.uv[num], textureScale) + textureOffset; if (dictionary.ContainsKey(num)) { stringBuilder.Append(string.Format("vt {0} {1}\n", mesh.uv[num].x, mesh.uv[num].y)); } else { stringBuilder.Append(string.Format("vt {0} {1}\n", vector2.x, vector2.y)); } } for (int k = 0; k < mesh.subMeshCount; k++) { stringBuilder.Append("\n"); if (k == 0) { stringBuilder.Append("usemtl ").Append("Material_design").Append("\n"); } if (k == 1) { stringBuilder.Append("usemtl ").Append("Material_logo").Append("\n"); } int[] triangles2 = mesh.GetTriangles(k); for (int l = 0; l < triangles2.Length; l += 3) { stringBuilder.Append(string.Format("f {0}/{0} {1}/{1} {2}/{2}\n", triangles2[l] + 1, triangles2[l + 1] + 1, triangles2[l + 2] + 1)); } } return stringBuilder.ToString(); } }