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.
-
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.
-
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. -
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个贴图的融合。