将unity地形转换为mesh——插件版本

插件下载:https://assetstore.unity.com/packages/tools/terrain/terrain-to-mesh-47276?aid=1011lGkb&utm_source=aff

editor tool

Menu/Window/VacuumShaders/Terrain To Mesh
在这里插入图片描述

source tab
for converting terrain assets into mesh just drag and drop them from Hierarchy and Project windows here.

only unity terrain assets are supported.

在这里插入图片描述
show/hide all buttons enable/disable terrain visibility in the scene window. have no effect if selected terrains are assets from project.

after converting terrain prefab file is created at location
在这里插入图片描述

if selection contains multiple objects they all will be saved inside one Sub-Folder.

在这里插入图片描述

each prefab is saved in separate folder with the same name as prefab file.

export maps tab
enables exporting terrain Splatmaps, Basemap and Heightmap textures. files are saved in the same folder as the main prefab.
在这里插入图片描述

  1. splatmap散斑图——uncompressed RGBA texture used by unity terrain engine for blending paint textures. one splatmap can blend 4 pain textures. splatmaps can be exported in PNG and TGA (requires encode to TGA asset) formats.

  2. basemap——all paint textures used by unity terrain engine are baked into one final texture. diffuse and normal textures are exported separately.
    basemap can be baked only if terrain uses unity’s built-in terrain shaders.

  3. heightmap——grayscale texture with terrain height data.
    格式说明:
    original——export original heightmap
    remap——before exporting terrain heightmap data is remapped to be inside [0,1] range.

export trees tab
exports tree prefabs used by unity terrain asset. exported trees are original tree prefabs and have no unity terrain lod system like : billboard, distance fade, etc.

export unity mesh tab

converts source terrain into unity mesh(.asset file)
a) chunk count horizontal/vertical——source terrain can be split in any amount of chunks. chunk count defines generated mesh asset count. mesh asset files are saved in the same folder as the main prefab.

b) vertex count horizontal/vertical ——defines vertex count per chunk. max allowed vertex count is 65.000

c) mesh compression——compressing meshes saves space in the built game, but more compression introduces more artifacts in vertex data. for multi-chunk meshes editor automatically adds CompressedMeshLoader script for fixing visible edge artifacts (high compressed meshes) in run-time.

d) attach MeshCollider——generates separate mesh file to be used as MeshCollider. mesh vertex density is calculated from the main mesh’s vertex count horizontal/vertical value.

depending on mesh resolution loading it into unity’s physics core may may be time consuming.

export obj mesh tab
converts terrain into .obj file format. can be imported into any 3d modeling software for additional editing.

人人素材网
simingtu
https://www.rrcg.cn/thread-16744559-1-1.html

t4m插件

很遗憾这个插件asset store已经不更新了
https://assetstore.unity.com/packages/tools/terrain/t4m-source-codes-edition-582

在这里插入图片描述

MeshMaker
SimpleLOD
T4M
ClientRes

先来t4m的工具

经过测试,t4m在unity 2018上已经报错了,不支持substance材质了,下载插件也还是报错,所以退到2017学习先。

ConvertUTerrain
我觉得这个U-》unity的意思
转换unity的Terrain

void ConvertUTerrain()
{
	if (terrainName=="")
		terrainName = CurrentSelect.name; 

CurrentSelect是哪里赋值的呢?

void OnGUI () 
{
		CurrentSelect= Selection.activeTransform;

即为当前选择的对象

if (!System.IO.Directory.Exists(T4MPrefabFolder+"Terrains/"))
{
	System.IO.Directory.CreateDirectory(T4MPrefabFolder+"Terrains/");
}

这个代码是什么意思?
意思是如果没有指定的文件夹就创建。T4MPrefabFolder在哪里定义?

string T4MPrefabFolder = "Assets/T4MOBJ/";

创建地形保存的文件夹。

if (!System.IO.Directory.Exists(T4MPrefabFolder+"Terrains/Material/"))
{
	System.IO.Directory.CreateDirectory(T4MPrefabFolder+"Terrains/Material/");
} 

创建材质保存的文件夹。

if (!System.IO.Directory.Exists(T4MPrefabFolder+"Terrains/Texture/"))
{
	System.IO.Directory.CreateDirectory(T4MPrefabFolder+"Terrains/Texture/");
} 

创建贴图保存的文件夹。

if (!System.IO.Directory.Exists(T4MPrefabFolder+"Terrains/Meshes/"))
{
	System.IO.Directory.CreateDirectory(T4MPrefabFolder+"Terrains/Meshes/");
}

创建mesh保存的文件夹。

AssetDatabase.Refresh(); 

刷新资源列表。

terrain = CurrentSelect.GetComponent <Terrain>().terrainData;
int w = terrain.heightmapWidth;
int h = terrain.heightmapHeight;

得到当前地形,得到高度图的宽高。

Vector3 meshScale = terrain.size;
meshScale = new Vector3(meshScale.x / (h - 1) * tRes, meshScale.y, meshScale.z / (w - 1) * tRes);
Vector2 uvScale = new Vector2((float)(1.0 / (w - 1)), (float)(1.0 / (h - 1)));

这里有个变量不太明确:tRes。
tRes = (HeightmapWidth) / T4MResolution;

T4MResolution=90
前者为高度图的宽度。
这里没有太明确的意思,后面在回来看。

Vector2 uvScale = new Vector2((float)(1.0 / (w - 1)), (float)(1.0 / (h - 1)));
float[,] tData = terrain.GetHeights(0, 0, w, h);

得到uv缩放,和高度图数据。

其实这段代码,和我之前的那个转mesh的代码是很类似的,我觉得原作者肯定是抄写t4m的了。

w = (int)((w - 1) / tRes + 1);
h = (int)((h - 1) / tRes + 1);

这里我们还是举具体的数字比较清楚。

w和h为高度图的分辨率,比如是513X513的
而tRes = (HeightmapWidth) / T4MResolution;
HeightmapWidth就是高度图的宽度=w=513
T4MResolution=90
ok:
在这里插入图片描述

Vector3[] tVertices = new Vector3[w * h];
Vector2[] tUV = new Vector2[w * h];
int[] tPolys = new int[(w - 1) * (h - 1) * 6];

也就是最终生成了90x90个点。
uv呢?也是这么多个点。

三角形的索引个数,这里为啥是w-1,和h-1呢?
请看下图:
在这里插入图片描述
上图公式4x4个顶点。

而三角形个数为18个,18个三角形则需要54个顶点。
所以是(4-1)*(4-1)*6=54

ok,搞清楚这个是前提,我们继续。

int y = 0;
int x = 0;
 for (y = 0; y < h; y++)
 {
     for (x = 0; x < w; x++)
     {
         tVertices[y * w + x] = Vector3.Scale(meshScale, new Vector3(x, tData[(int)(x * tRes), (int)(y * tRes)], y));
         tUV[y * w + x] = Vector2.Scale(new Vector2(y * tRes, x * tRes), uvScale);
     }
 }
tData[(int)(x * tRes), (int)(y * tRes)]

为取得高度图的高度,被稀疏了下。
最后在meshScale下,得到原始地图的大小。

y = 0;
x = 0;
int index = 0;
for (y = 0; y < h - 1; y++)
{
    for (x = 0; x < w - 1; x++)
    {
        tPolys[index++] = (y * w) + x;
        tPolys[index++] = ((y + 1) * w) + x;
        tPolys[index++] = (y * w) + x + 1;

        tPolys[index++] = ((y + 1) * w) + x;
        tPolys[index++] = ((y + 1) * w) + x + 1;
        tPolys[index++] = (y * w) + x + 1;
    }
}

这个得到三角形的索引了。

bool ExportNameSuccess = false;
int num = 1;
string Next;
do
{
    Next = terrainName + num;

    if (!System.IO.File.Exists(T4MPrefabFolder + "Terrains/" + terrainName + ".prefab"))
    {
        FinalExpName = terrainName;
        ExportNameSuccess = true;
    }
    else if (!System.IO.File.Exists(T4MPrefabFolder + "Terrains/" + Next + ".prefab"))
    {
        FinalExpName = Next;
        ExportNameSuccess = true;
    }
    num++;
} while (!ExportNameSuccess);

从num等于1开始,判断是否有重名的,为其找到一个唯一的名字while结束。

 StreamWriter sw = new StreamWriter(FinalExpName + ".obj");
 try
   {

       sw.WriteLine("# T4M File");
       System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
       counter = tCount = 0;
       totalCount = (int)((tVertices.Length * 2 + (tPolys.Length / 3)) / progressUpdateInterval);

写文件,上来写了个注释#T4M file
然后是计算进度条的最大值是多少,用于显示进度条。

for (int i = 0; i < tVertices.Length; i++)
{
    UpdateProgress();
    StringBuilder sb = new StringBuilder("v ", 20);
    sb.Append(tVertices[i].x.ToString()).Append(" ").
       Append(tVertices[i].y.ToString()).Append(" ").
       Append(tVertices[i].z.ToString());
    sw.WriteLine(sb);
}

这个是写顶点数据

for (int i = 0; i < tUV.Length; i++)
{
    UpdateProgress();
    StringBuilder sb = new StringBuilder("vt ", 22);
    sb.Append(tUV[i].x.ToString()).Append(" ").Append(tUV[i].y.ToString());
    sw.WriteLine(sb);
}

这个是写uv数据

for (int i = 0; i < tPolys.Length; i += 3)
{
     UpdateProgress();
     StringBuilder sb = new StringBuilder("f ", 43);
     sb.Append(tPolys[i] + 1).Append("/").Append(tPolys[i] + 1).Append(" ").
        Append(tPolys[i + 1] + 1).Append("/").Append(tPolys[i + 1] + 1).Append(" ").
        Append(tPolys[i + 2] + 1).Append("/").Append(tPolys[i + 2] + 1);
     sw.WriteLine(sb);
 }

这个是写三角形信息。

catch (Exception err)
        {
            Debug.Log("Error saving file: " + err.Message);
        }
        sw.Close();
        AssetDatabase.SaveAssets();

捕捉异常,关闭流,刷新资源。

string path = T4MPrefabFolder + "Terrains/Texture/" + FinalExpName + ".png";
        bool isFirst = true;
        //Control Texture Creation or Recuperation
        string AssetName = AssetDatabase.GetAssetPath(CurrentSelect.GetComponent<Terrain>().terrainData) as string;
        UnityEngine.Object[] AssetName2 = AssetDatabase.LoadAllAssetsAtPath(AssetName);

加载当前的地形数据TerrainData。

if (AssetName2 != null && AssetName2.Length > 1 && keepTexture)
{
    for (var b = 0; b < AssetName2.Length; b++)
    {
        if (AssetName2[b].name == "SplatAlpha 1" || AssetName2[b].name == "SplatAlpha 0")
        {
            if (isFirst)
            {
                isFirst = false;
                Texture2D texture = AssetName2[b] as Texture2D;
                byte[] bytes = texture.EncodeToPNG();
                File.WriteAllBytes(path, bytes);
                AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
            }
        }
    }
}

这段代码,有问题,因为你看哈,如果有两个splat贴图,那么AssetName2.Length=3
在这里插入图片描述
第一个是New Terrain
第二个是SplatAlpha 0
第三个是SplatAlpha 1
而上段代码,用了isFirst作为控制,那么只能写一个图片。

所以t4m只能支持4个贴图的融合,超过4个不行。
上面是保存贴图的方法,如果不保存呢,也就是:
在这里插入图片描述
才执行上面的操作。

否则执行else:

       else
        {
            Texture2D NewMaskText = new Texture2D(512, 512, TextureFormat.ARGB32, true);
            Color[] ColorBase = new Color[512 * 512];
            for (var t = 0; t < ColorBase.Length; t++)
            {
                ColorBase[t] = new Color(1, 0, 0, 0); //只写入一个红色,说明只有一个贴图
            }
            NewMaskText.SetPixels(ColorBase);
            byte[] data = NewMaskText.EncodeToPNG();
            File.WriteAllBytes(path, data);
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
        }

这写入了一个红色的信息,也就是一张贴图。

 AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
 UpdateProgress();

刷新资源,更新进度条。

 TextureImporter TextureI = AssetImporter.GetAtPath(path) as TextureImporter;
        if (TextureI != null)
        {
            isCreateTexture = true;
            TextureI.textureType = TextureImporterType.Default;
            TextureImporterPlatformSettings tSetting = new TextureImporterPlatformSettings();
            tSetting.overridden = true;
            tSetting.format = TextureImporterFormat.RGBA32;
            TextureI.SetPlatformTextureSettings(tSetting);

            //TextureI.textureFormat = TextureImporterFormat.ARGB32;
            TextureI.isReadable = true;
            TextureI.anisoLevel = 9;
            TextureI.mipmapEnabled = false;
            TextureI.wrapMode = TextureWrapMode.Clamp;
            AssetDatabase.Refresh();

            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);

            UpdateProgress();
        }

更改刚才写入的那个贴图的样式。

Material Tmaterial;
Tmaterial = new Material(Shader.Find("T4MShaders/ShaderModel2/Diffuse/T4M 4 Textures"));
AssetDatabase.CreateAsset(Tmaterial, T4MPrefabFolder + "Terrains/Material/" + FinalExpName + ".mat");
AssetDatabase.ImportAsset(T4MPrefabFolder + "Terrains/Material/" + FinalExpName + ".mat", ImportAssetOptions.ForceUpdate);
AssetDatabase.Refresh();

创建一个新的材质。

 if (keepTexture)
 {
      SplatPrototype[] texts = CurrentSelect.GetComponent<Terrain>().terrainData.splatPrototypes;
      for (int e = 0; e < texts.Length; e++)
      {
          if (e < 4)
          {
              Tmaterial.SetTexture("_Splat" + e, texts[e].texture);
              Tmaterial.SetTextureScale("_Splat" + e, texts[e].tileSize * 8.9f);
          }
      }
  }

  //Attribution de la Texture Control sur le materiau
  Texture test = (Texture)AssetDatabase.LoadAssetAtPath(path, typeof(Texture));
  Tmaterial.SetTexture("_Control", test);

为材质设置贴图数据,CurrentSelect.GetComponent().terrainData.splatPrototypes;得到当前使用了哪几个几个融合的贴图。

最后再为地形设置一个融合的贴图。

UpdateProgress();
FileUtil.CopyFileOrDirectory(FinalExpName + ".obj", T4MPrefabFolder + "Terrains/Meshes/" + FinalExpName + ".obj");
FileUtil.DeleteFileOrDirectory(FinalExpName + ".obj");

更新,以及拷贝obj文件。

ok,到这里已经基本上分析完毕了。

在这里插入图片描述

t4m只能只是4个贴图的融合。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值