1. 程式人生 > >Unity地形匯出為.obj模型

Unity地形匯出為.obj模型

我在Uniyt 3D中建立的真實地形想儲存為模型以備以後使用,經過在網上艱辛的搜尋(呵呵。。。),終於找到一個方法,經過實驗驗證,絕對真實可靠!有圖有真相!

先上程式碼(O(_)O哈哈~)。

原始碼來自於這兒:http://blog.sina.com.cn/s/blog_7812d64701010f7h.html,感謝他的分享!

下面為ExportTerrain.js指令碼:

import System.IO;  
import System.Text;  
  
enum SaveFormat {Triangles, Quads}  
enum SaveResolution {Full, Half, Quarter, Eighth, Sixteenth}  
  
class ExportTerrain extends EditorWindow   
{  
    var saveFormat = SaveFormat.Triangles;  
    var saveResolution = SaveResolution.Half;  
    static var terrain : TerrainData;  
    static var terrainPos : Vector3;  
       
    var tCount : int;  
    var counter : int;  
    var totalCount : int;  
      
    @MenuItem ("Terrain/Export To Obj...")  
    static function Init ()   
    {  
        terrain = null;  
       var terrainObject : Terrain = Selection.activeObject as Terrain;  
        if (!terrainObject)   
        {  
           terrainObject = Terrain.activeTerrain;  
        }  
        if (terrainObject)   
        {  
            terrain = terrainObject.terrainData;  
            terrainPos = terrainObject.transform.position;  
        }  
        EditorWindow.GetWindow(ExportTerrain).Show();  
    }  
       
    function OnGUI ()   
    {  
       if (!terrain)   
       {  
            GUILayout.Label("No terrain found");  
            if (GUILayout.Button("Cancel"))   
            {  
                EditorWindow.GetWindow(ExportTerrain).Close();  
            }  
           return;  
     }  
        saveFormat = EditorGUILayout.EnumPopup("Export Format", saveFormat);  
       saveResolution = EditorGUILayout.EnumPopup("Resolution", saveResolution);  
           
        if (GUILayout.Button("Export"))   
        {  
            Export();  
        }  
    }  
       
    function Export ()   
    {  
        var fileName = EditorUtility.SaveFilePanel("Export .obj file", "", "Terrain", "obj");  
       var w = terrain.heightmapWidth;  
        var h = terrain.heightmapHeight;  
        var meshScale = terrain.size;  
        var tRes = Mathf.Pow(2, parseInt(saveResolution));  
        meshScale = Vector3(meshScale.x/(w-1)*tRes, meshScale.y, meshScale.z/(h-1)*tRes);  
        var uvScale = Vector2(1.0/(w-1), 1.0/(h-1));  
        var tData = terrain.GetHeights(0, 0, w, h);  
           
        w = (w-1) / tRes + 1;  
        h = (h-1) / tRes + 1;  
       var tVertices = new Vector3[w * h];  
        var tUV = new Vector2[w * h];  
        if (saveFormat == SaveFormat.Triangles)   
        {  
            var tPolys = new int[(w-1) * (h-1) * 6];  
        }  
        else   
        {  
           tPolys = new int[(w-1) * (h-1) * 4];  
        }  
           
        // Build vertices and UVs  
        for (y = 0; y < h; y++)   
        {  
            for (x = 0; x < w; x++)   
            {  
                tVertices[y*w + x] = Vector3.Scale(meshScale, Vector3(x, tData[x*tRes,y*tRes], y)) + terrainPos;  
                tUV[y*w + x] = Vector2.Scale(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 (y = 0; y < h-1; y++)   
            {  
                for (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 (y = 0; y < h-1; y++)   
            {  
                for (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  
        try   
        {  
            var sw = new StreamWriter(fileName);  
            sw.WriteLine("# Unity terrain OBJ File");  
               
            // Write vertices  
            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 (i = 0; i < tVertices.Length; i++)   
            {  
                UpdateProgress();  
                var sb = 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 (i = 0; i < tUV.Length; i++)   
           {  
                UpdateProgress();  
               sb = 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 (i = 0; i < tPolys.Length; i += 3)   
                {  
                    UpdateProgress();  
                   sb = 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 (i = 0; i < tPolys.Length; i += 4)   
                {  
                    UpdateProgress();  
                    sb = 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 (err)   
        {  
            Debug.Log("Error saving file: " + err.Message);  
        }  
       sw.Close();  
           
        terrain = null;  
        EditorUtility.ClearProgressBar();  
        EditorWindow.GetWindow(ExportTerrain).Close();  
    }  
       
    function UpdateProgress ()   
    {  
        if (counter++ == 1000)   
        {  
            counter = 0;  
            EditorUtility.DisplayProgressBar("Saving...", "", Mathf.InverseLerp(0, totalCount, ++tCount));  
        }  
        }     
 }    

將上面的指令碼放在Unity項的目錄資原始檔夾的Editor裡。

重新整理一下選單欄,會發現多了一個Terrain的選單。

先在場景中選中地形物件,如果沒選,他將用於當前場景中可用的地形。

然後從Terrain選單下選擇Export To Obj...


接下來會彈出一個框,在這裡你可以選擇要匯出四邊形網路結構還是三角形網路結構,還可以選擇要匯出的地形的解析度,有高中低...。最後點選Export,選擇要儲存的位置和檔名,.obj檔案將被匯出。

注意:如果選擇大面積的Full地形匯出,最終.obj檔案將非常大,而且也要匯出很久。


下邊是我實踐過的例子:

Uniyt 4.6.2中建立的真實地形(釣魚島哈):


匯出.obj格式的模型(模型和貼圖):


Maya 2013中開啟如下:


哎呀,怎麼是這個樣子?我只想要釣魚島,該怎麼辦?只要在Maya裡簡單處理一下就好了。

選中模型,右鍵選擇“面”:


框選中模型整體,然後切換到側檢視,按著Shift鍵,用滑鼠框選突出的部分。(注意:要稍微比水平面高一點。)


Delete刪除,大功告成!


好啦,給他加上貼圖,匯出為FBX檔案,再放回Unity看看(釣魚島和達山島)。


PS.obj模型在maya中開啟是沒有貼圖的,需要重新為其附貼圖。