目录

C# Unity将地形(Terrain)导出成obj文件

C# Unity将地形(Terrain)导出成obj文件

从其他地方搬运过来的,只能到出obj模型,不能导出贴图

csharp 复制代码
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using System;

enum SaveFormat { Triangles, Quads }
enum SaveResolution { Full, Half, Quarter, Eighth, Sixteenth }

class ExportTerrain : EditorWindow
{
    SaveFormat saveFormat = SaveFormat.Triangles;
    SaveResolution saveResolution = SaveResolution.Half;
    static TerrainData terrain;
    static Vector3 terrainPos;

    int tCount ;
    int counter ;
    int totalCount ;

    [MenuItem ("Terrain/Export To Obj...")]
	    static void Init () 
        {
        terrain = null;
        Terrain terrainObject = Selection.activeObject as Terrain;
        if (!terrainObject)
        {
            terrainObject = Terrain.activeTerrain;
        }
        if (terrainObject)
        {
            terrain = terrainObject.terrainData;
            terrainPos = terrainObject.transform.position;
        }
        EditorWindow.GetWindow(typeof(ExportTerrain), false, "MyWindow", true).Show();
    }

    void OnGUI () 
        {
        if (!terrain)
        {
            GUILayout.Label("No terrain found");
            if (GUILayout.Button("Cancel"))
            {
                EditorWindow.GetWindow(typeof(ExportTerrain), false, "MyWindow", true).Close();
            }
            return;
        }
        saveFormat = (SaveFormat)EditorGUILayout.EnumPopup("Export Format", saveFormat);
        saveResolution = (SaveResolution)EditorGUILayout.EnumPopup("Resolution", saveResolution);

        if (GUILayout.Button("Export"))
        {
            Export();
        }
    }

    void Export () 
        {
        String fileName = EditorUtility.SaveFilePanel("Export .obj file", "", "Terrain", "obj");
        int w = terrain.heightmapWidth;
        int h = terrain.heightmapHeight;
        Vector3 meshScale = terrain.size;
        float tRes = Mathf.Pow(2, System.Convert.ToInt32(saveResolution));
        meshScale =new Vector3(meshScale.x / (w - 1) * tRes, meshScale.y, meshScale.z / (h - 1) * tRes);
        Vector2 uvScale = new Vector2(1.0f / (w - 1), 1.0f / (h - 1));
        float[,] tData = terrain.GetHeights(0, 0, w, h);

        w = (int)((w - 1) / tRes) + 1;
        h = (int)((h - 1) / tRes) + 1;
        Vector3[] tVertices = new Vector3[w * h];
        Vector2[] tUV = new Vector2[w * h];
        int[] tPolys;
        if (saveFormat == SaveFormat.Triangles)
        {
            tPolys = new int[(w - 1) * (h - 1) * 6];
        }
        else
        {
            tPolys = new int[(w - 1) * (h - 1) * 4];
        }

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

        var index = 0;
        if (saveFormat == SaveFormat.Triangles)
        {
            // Build triangle indices: 3 indices into vertex array for each triangle
            for (int y = 0; y < h - 1; y++)
            {
                for (int x = 0; x < w - 1; x++)
                {
                    // For each grid cell output two triangles
                    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;
                }
            }
        }
        else
        {
            // Build quad indices: 4 indices into vertex array for each quad
            for (int y = 0; y < h - 1; y++)
            {
                for (int x = 0; x < w - 1; x++)
                {
                    // For each grid cell output one quad
                    tPolys[index++] = (y * w) + x;
                    tPolys[index++] = ((y + 1) * w) + x;
                    tPolys[index++] = ((y + 1) * w) + x + 1;
                    tPolys[index++] = (y * w) + x + 1;
                }
            }
        }

        // Export to .obj
        StreamWriter sw=null;
        try
        {
            sw=new StreamWriter(fileName);
            sw.WriteLine("# Unity terrain OBJ File");

            // Write vertices
            StringBuilder sb;
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
            counter = tCount = 0;
            totalCount = (tVertices.Length * 2 + (saveFormat == SaveFormat.Triangles ? tPolys.Length / 3 : tPolys.Length / 4)) / 1000;
            for (int i = 0; i < tVertices.Length; i++)
            {
                UpdateProgress();
                sb = new StringBuilder("v ", 20);
                // StringBuilder stuff is done this way because it's faster than using the "{0} {1} {2}"etc. format
                // Which is important when you're exporting huge terrains.
                sb.Append(tVertices[i].x.ToString()).Append(" ").
                   Append(tVertices[i].y.ToString()).Append(" ").
                   Append(tVertices[i].z.ToString());
                sw.WriteLine(sb);
            }
            // Write UVs
            for (int i = 0; i < tUV.Length; i++)
            {
                UpdateProgress();
                sb =new StringBuilder("vt ", 22);
                sb.Append(tUV[i].x.ToString()).Append(" ").
                   Append(tUV[i].y.ToString());
                sw.WriteLine(sb);
            }
            if (saveFormat == SaveFormat.Triangles)
            {
                // Write triangles
                for (int i = 0; i < tPolys.Length; i += 3)
                {
                    UpdateProgress();
                    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);
                }
            }
            else
            {
                // Write quads
                for (int i = 0; i < tPolys.Length; i += 4)
                {
                    UpdateProgress();
                    sb =new StringBuilder("f ", 57);
                    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).Append(" ").
                       Append(tPolys[i + 3] + 1).Append("/").Append(tPolys[i + 3] + 1);
                    sw.WriteLine(sb);
                }
            }
        }
        catch (Exception err)
        {
            Debug.Log("Error saving file: " + err.Message);
        }
        if(sw!=null)
            sw.Close();

        terrain = null;
        EditorUtility.ClearProgressBar();
        EditorWindow.GetWindow(typeof(ExportTerrain), false, "MyWindow", true).Close();
    }

    void UpdateProgress () 
        {
        if (counter++ == 1000)
        {
            counter = 0;
            EditorUtility.DisplayProgressBar("Saving...", "", Mathf.InverseLerp(0, totalCount, ++tCount));
        }
    }
}
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
努力学习的小廉14 分钟前
【智能指针】—— 我与C++的不解之缘(三十三)
开发语言·c++
AAA码农烧烤29 分钟前
神经网络入门—井字棋游戏
开发语言·python·游戏
Java小白中的菜鸟39 分钟前
深入理解Java反射
java·开发语言
小付同学呀2 小时前
前端快速入门——JavaScript变量、控制语句
开发语言·前端·javascript
愚润求学3 小时前
【C++】list模拟实现
开发语言·数据结构·c++·list
专注VB编程开发20年5 小时前
写.NET可以指定运行SUB MAIN吗?调用任意一个里面的类时,如何先执行某段初始化代码?
开发语言·vb.net
仙人掌_lz6 小时前
利用python从零实现Byte Pair Encoding(BPE):NLP 中的“变形金刚”
开发语言·python·gpt·自然语言处理·llm·token·deepseek
q567315236 小时前
使用Alamofire下载网站首页内容
开发语言·爬虫·python·scrapy·golang
Aerkui7 小时前
Python标准库-copy
开发语言·python
刚入门的大一新生7 小时前
C++初阶-C++的讲解1
开发语言·c++