Unity3d导出Recast geomset.txt

Unity3d导出Recast geomset.txt

Unity3d导出Recast geomset.txt

(金庆的专栏)

Recast Demo 输入需要 geomset.txt 文件来指定区域类型。

以ObjExporter.cs为基础,编写Unity3d地图导出工具,同时导出obj文件和geomset.txt.

geomset.txt 示例

# Obj file.
f scene_level_0101.unity_56.obj

# Convex volumes.
# v nVertex area hmin hmax
# Area:
#    0: Default
#    1: Not Walkable
#    2: Jump
#    3: Enemy

# wall
v 4 1 -19.10179 6.521788
-77.54077 -19.10179 60.79453
-74.40015 6.521788 57.64596
-73.83923 6.521788 58.20547
-76.97985 -19.10179 61.35404

场景:


Recast 加载:


代码:


/*
Based on ObjExporter.cs:
http://wiki.unity3d.com/index.php?title=ObjExporter
This "wrapper" lets you export map to .OBJ and geomset.txt directly from the editor menu.
Obj mesh file and geomset.txt are used to generate nav mesh for server in Recast.
 
This should be put in your "Editor"-folder.
Use menu "Custom->Export map".
Exported models are put in a folder called
"ExportedMap" in the root of your Unity-project.
*/
/*  输出 obj 文件是mesh保存文件, geomset.txt 是RecastDemo工具的输入文件, 
 * 将这2个文件复制到 RecastDeme 执行目录, 按数字 0 加载.
 
*/
using  UnityEngine;
using  UnityEditor;
using  System.Collections;
using  System.Collections.Generic;
using  System.IO;
using  System.Text;
using  System;
 
public   class  EditorMapExporter : ScriptableObject
{
    
private   static   int  vertexOffset  =   0 ;
    
private   static   string  targetFolder  =   " ExportedMap " ;
    
private   static  StringBuilder sbLog  =   new  StringBuilder ();
    
private   static   string  geomsetFileName  =   " geomset.txt " ;

    
private   static   void  FlushLog ()
    {
        
if  ( ! CreateTargetFolder ())
            
return ;

        
using  (StreamWriter sw  =   new  StreamWriter(targetFolder  +   " / "   +   " MapExporter.log " true )) {
            sw.Write (sbLog.ToString ());
        }
        sbLog 
=   new  StringBuilder ();
    }

    
private   static   string  MeshToString (MeshFilter mf)
    {
        Mesh m 
=  mf.sharedMesh;
 
        StringBuilder sb 
=   new  StringBuilder ();
 
        sb.Append (
" " ).Append (mf.name).Append ( " \n " );
        sb.Append (
" \n " );
        
foreach  (Vector3 lv  in  m.vertices) {
            Vector3 wv 
=  mf.transform.TransformPoint (lv);
 
            
// This is sort of ugly - inverting x-component since we're in
            
// a different coordinate system than "everyone" is "used to".
            sb.Append ( string .Format ( " v {0} {1} {2}\n " - wv.x, wv.y, wv.z));
        }
        sb.Append (
" \n " );
 
        
for  ( int  material = 0 ; material  <  m.subMeshCount; material  ++ ) {
            
int [] triangles  =  m.GetTriangles (material);
            
for  ( int  i = 0 ; i < triangles.Length; i += 3 ) {
                
// Because we inverted the x-component, we also needed to alter the triangle winding.
                sb.Append ( string .Format ( " f {1} {0} {2}\n "
                    triangles [i] 
+   1   +  vertexOffset, triangles [i  +   1 +   1   +  vertexOffset, triangles [i  +   2 +   1   +  vertexOffset));
            }
        }
        sb.Append (
" \n " );
 
        vertexOffset 
+=  m.vertices.Length;
 
        
return  sb.ToString ();
    }
 
    
private   static   void  Clear ()
    {
        vertexOffset 
=   0 ;
    }
 
    
private   static   void  MeshesToFile (MeshFilter[] mf,  string  filename)
    {
        Clear ();
        
string  folder  =  targetFolder;
        
using  (StreamWriter sw  =   new  StreamWriter(folder  +   " / "   +  filename)) {
            
for  ( int  i  =   0 ; i  <  mf.Length; i ++ ) {
                sw.Write (MeshToString (mf [i]));
            }
        }
        EditorUtility.DisplayDialog (
" Map exported " " Exported  "   +  mf.Length  +   "  objects to  "   +  filename,  " OK " );
    }
 
    
private   static   bool  CreateTargetFolder ()
    {
        
try  {
            System.IO.Directory.CreateDirectory (targetFolder);
        } 
catch  {
            EditorUtility.DisplayDialog (
" Error! " " Failed to create target folder! " " OK " );
            
return   false ;
        }
 
        
return   true ;
    }
 
    
//  获取场景中的所有游戏对象, 选出所有NavigationStatic对象.
    
//  静态光源等无MeshFilter的将被忽略.
     private   static  GameObject[] GetNavStaticObjects ()
    {
        sbLog.Append (
" Get navigation static objects:\n " );
        ArrayList navStaticObjList 
=   new  ArrayList ();
        GameObject [] gos 
=  (GameObject[])FindObjectsOfType ( typeof (GameObject));
        
foreach  (GameObject go   in  gos) {
            
//  暂无OffMeshLinkGeneration
             if  ( ! GameObjectUtility.AreStaticEditorFlagsSet (go, StaticEditorFlags.NavigationStatic))
                
continue ;
            Component[] meshfilter 
=  go.transform.GetComponents ( typeof (MeshFilter));
            
if  ( 0   ==  meshfilter.Length)
                
continue ;

            navStaticObjList.Add (go);
            sbLog.AppendFormat (
" Name: {0}, MeshFilters: {1}\n " , go.name, meshfilter.Length);
        }

        
return  (GameObject[])navStaticObjList.ToArray ( typeof (GameObject));  
    }

    
//  获取场景中的所有游戏对象, 选出所有NavigationStatic对象.
    
//  静态光源等无MeshFilter的将被忽略.
     private   static  MeshFilter[] GetMeshFilters (GameObject[] gos)
    {
        ArrayList mfList 
=   new  ArrayList ();
        
for  ( int  i  =   0 ; i  <  gos.Length; i ++ ) {
            GameObject go 
=  gos [i];
            Component[] meshfilter 
=  go.transform.GetComponents ( typeof (MeshFilter));
            
for  ( int  m  =   0 ; m  <  meshfilter.Length; m ++ ) {
                mfList.Add (meshfilter [m]);
            }
        }
        
return  (MeshFilter[])mfList.ToArray ( typeof (MeshFilter));
    }

    [MenuItem (
" Custom/Export Map v4 " )]
    
static   void  ExportWholeSelectionToSingle ()
    {
        
if  ( ! CreateTargetFolder ())
            
return ;

        sbLog.AppendFormat (
" NavMeshLayerNames:\n " );
        
string [] nmLayerNames  =  GameObjectUtility.GetNavMeshLayerNames ();
        
for  ( int  i  =   0 ; i  <  nmLayerNames.Length; i ++ ) {
            sbLog.AppendFormat (
" {0}: {1}\n " ,
                                nmLayerNames[i],
                                GameObjectUtility.GetNavMeshLayerFromName(nmLayerNames[i]));
        }
        sbLog.Append (
" \n " );

        
// 获取场景中的所有游戏对象, 选出所有NavigationStatic对象
        GameObject[] gos  =  GetNavStaticObjects ();
        MeshFilter[] mf 
=  GetMeshFilters (gos);
        
string  objFileName  =  GetObjSaveFileName (mf.Length);
        MeshesToFile (mf, objFileName);
        SaveGeomsetFile (mf, objFileName);

        FlushLog ();
    }

    
private   static   string  GetObjSaveFileName( int  exportedObjects)
    {
        
string  filename  =  EditorApplication.currentScene  +   " _ "   +  exportedObjects;
        
int  stripIndex  =  filename.LastIndexOf ( ' / ' ); // FIXME: Should be Path.PathSeparator
         if  (stripIndex  >=   0 )
            filename 
=  filename.Substring (stripIndex  +   1 ).Trim ();
        
return  filename  +   " .obj " ;
    }

    
private   static   void  SaveGeomsetFile(MeshFilter[] mf,  string  objFileName)
    {
        
using  (StreamWriter sw  =   new  StreamWriter(targetFolder  +   " / "   +  geomsetFileName)) {
            sw.Write (
" # Obj file.\n " );
            sw.Write (
" "   +  objFileName  +   " \n\n " );
            
//  暂无 Offmesh connector
            
//  Convex volumes
            ConvexVolumes cvs  =   new  ConvexVolumes(mf);
            sw.Write (cvs.ToString ());
        }
    }

    
class  ConvexVolumes
    {
        
private  ArrayList cvList  =   new  ArrayList ();
        
        
public  ConvexVolumes(MeshFilter[] mf)
        {
            
for  ( int  i  =   0 ; i  <  mf.Length; i ++ ) {
                cvList.Add (
new  ConvexVolume (mf[i]));
            }
        }
        
        
public   string  ToString()
        {
            StringBuilder sb 
=   new  StringBuilder ();
            sb.Append (
" # Convex volumes.\n " );
            sb.Append (
" # v nVertex area hmin hmax\n " );
            sb.Append (
" # Area:\n " );
            
string [] nmLayerNames  =  GameObjectUtility.GetNavMeshLayerNames ();
            
for  ( int  i  =   0 ; i  <  nmLayerNames.Length; i ++ ) {
                sb.AppendFormat (
" #\t{0}: {1}\n " ,
                                 GameObjectUtility.GetNavMeshLayerFromName(nmLayerNames[i]),
                                 nmLayerNames[i]);
            }
            sb.Append (
" \n " );

            
foreach (ConvexVolume cv  in  cvList) 
            { 
                sb.Append(cv.ToString() 
+   " \n " ); 
            } 
            
return  sb.ToString ();
        }
    }
    
    
class  ConvexVolume
    {
        
private   string  name;
        
private   int  areaType;
        
private   float  minh;
        
private   float  maxh;
        
private  ArrayList vertList;
        
        
public  ConvexVolume(MeshFilter mf)
        {
            name 
=  mf.gameObject.name;
            areaType 
=  GameObjectUtility.GetNavMeshLayer (mf.gameObject);
            sbLog.AppendFormat (
" ConvexVolume: name = {0}, area = {1}\n " ,
                                mf.gameObject.name, areaType);
            Vector3[] allVertices 
=  GetAllVertices (mf);
            GetHeight (allVertices);
            ConvexHull (allVertices);
        }
        
        
public   string  ToString()
        {
            StringBuilder sb 
=   new  StringBuilder ();
            sb.AppendFormat (
" # {0}\n " , name);
            sb.AppendFormat (
" v {0} {1} {2} {3}\n " , vertList.Count, areaType, minh, maxh);
            
foreach  (Vector3 v  in  vertList) {
                sb.AppendFormat (
" {0} {1} {2}\n " , v.x, v.y, v.z);
            }
            
return  sb.ToString ();
        }
        
        
private  Vector3[] GetAllVertices(MeshFilter mf)
        {
            Mesh m 
=  mf.sharedMesh;
            ArrayList vertList 
=   new  ArrayList ();
            
            
foreach  (Vector3 lv  in  m.vertices) {
                Vector3 wv 
=  mf.transform.TransformPoint (lv);
                
// This is sort of ugly - inverting x-component
                Vector3 wv2  =   new  Vector3( - wv.x, wv.y, wv.z);
                vertList.Add (wv2);
            }
            
return  (Vector3[])vertList.ToArray ( typeof (Vector3));
        }
        
        
private   void  ConvexHull(Vector3[] v)
        {
            vertList 
=   new  ArrayList();
            
            
//  Find lower-leftmost point.
             int  org_hull  =   0 ;
            
for  ( int  h  =   1 ; h  <  v.Length;  ++ h)
                
if  (CompPoint(v[h], v[org_hull]))
                    org_hull 
=  h;
            
            
//  Gift wrap hull.
             int  endpt  =   0 ;
            
int  hull  =  org_hull;
            
int  count  =   0 ;
            
do
            {
                count
++ ;
                
if  (count  >   999999 ) {
                    EditorUtility.DisplayDialog (
" Convex Hull Error " " Something is wrong!!! " " OK " );
                    
break ;
                }

                vertList.Add (v[hull]);
                
//  sbLog.AppendFormat ("Got hull {0} : {1}\n", hull, v[hull].ToString ());
                endpt  =   0 ;
                
for  ( int  j  =   1 ; j  <  v.Length;  ++ j)
                    
if  (endpt  ==  hull  ||  IsLeft (v[hull], v[endpt], v[j]))
                        endpt 
=  j;
                hull 
=  endpt;
            }
            
while  (endpt  !=  org_hull  &&  count  <=   24 );
            FlushLog ();
        }
        
        
//  Returns true if 'c' is left of line 'a'-'b'.
         private   bool  IsLeft(Vector3 a, Vector3 b, Vector3 c)
        { 
            
float  u1  =  b.x  -  a.x;
            
float  v1  =  b.z  -  a.z;
            
float  u2  =  c.x  -  a.x;
            
float  v2  =  c.z  -  a.z;
            
return  u1  *  v2  -  v1  *  u2  <   0 ;
        }
        
        
//  Returns true if 'a' is more lower-left than 'b'.
         private   bool  CompPoint(Vector3 a, Vector3 b)
        {
            
if  (a.x  <  b.x)  return   true ;
            
if  (a.x  >  b.x)  return   false ;
            
if  (a.z  <  b.z)  return   true ;
            
if  (a.z  >  b.z)  return   false ;
            
return   false ;
        }

        
private   void  GetHeight(Vector3[] vertices)
        {
            minh 
=   float .MaxValue;
            maxh 
=   float .MinValue;
            
foreach  (Vector3 v  in  vertices) {
                
if  (minh  >  v.y)
                    minh 
=  v.y;
                
if  (maxh  <  v.y)
                    maxh 
=  v.y;
            }
        }
    }
}

你可能感兴趣的:(Unity3d导出Recast geomset.txt)