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 ("g ").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 ("f " + 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的更多相关文章

  1. Unity3d导出场景地图寻路

    Unity3d导出场景地图寻路(金庆的专栏)Unity3d中用无渲染的透明盒子摆出地面和阻档区域.        this.renderer.enabled = false;所有这些盒子设为Navig ...

  2. GJM :Unity3d导出eclipse工程,导入Android Studio

    unity3d导出eclipse工程,导入Android Studio 标签: unity3Dandroid studio 2016-08-11 10:42 398人阅读 评论(1) 收藏 举报 分类 ...

  3. Excel 导出指定行为txt文件(VBA,宏)

    要从Excel 多个sheet内导出指定行为txt文件,懒得用C#了,写个VBA宏 Sub Export() Dim FileName As Variant Dim Sep As String Dim ...

  4. 导出OpenID为txt文件的方法

    导出OpenID为txt文件的方法 public function export(){ $shop = M("Shop"); $arr = $shop->field('ope ...

  5. mysql data local的使用导入与导出数据到.txt

    一.先创建表 CREATE TABLE stu(id INT UNSIGNED AUTO_INCREMENT,NAME VARCHAR(15) UNIQUE, /* 唯一约束 , 可以不填写,如果填写 ...

  6. Egret3D研究报告(二)从Unity3D导出场景到Egret3D

    Egret3D暂时没有场编的计划,但是我们知道unity3D是一个很好的场编. 有一些游戏即使不是使用Unity3D开发,也使用Unity3D做场编.这里就不点名了,而且并不在少数. 我们就这么干. ...

  7. 解决Unity3D导出apk失败:Failed to re-package resources

    前几天把系统重装了一下,重新安装Unity3D和Android Studio之后发现过去的文件都不能导出了. 错误信息主要包括: CommandInvokationFailure: Failed to ...

  8. C#中打日志导出日志到txt文本

    /// <summary> /// 打日志 /// </summary> /// <param name="log"></param> ...

  9. C# listview控件右击导出数据到txt文本

    private void 导出成功点击ToolStripMenuItem_Click(object sender, EventArgs e) { ) { MessageBox.Show("列 ...

随机推荐

  1. java中关于&、&&、|、||之间的区别和运算

    关于&.&&.|.||之间的区别和运算 在逻辑运算中: 普通与&:要判断所有的判断的条件 短路与&&:如果前面一个判断条件出现false,则后续的判断条 ...

  2. [python]使用django快速生成自己的博客小站,含详细部署方法

    前言 人生苦短,我用python 这是之前经常听到的一句笑谈.因为新公司很多业务是用的python语言,所以这几天也一直在学习python的一些东西. 作为一个之前一直java后端的开发人员,对比ja ...

  3. python3全栈开发-什么是粘包、粘包现象、如何解决粘包

    一.粘包现象 让我们基于tcp先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig) 注意注意注意: res=subprocess.Popen(cmd.decode( ...

  4. [HNOI 2016]最小公倍数

    Description 题库链接 给定一张 \(N\) 个顶点 \(M\) 条边的无向图(顶点编号为 \(1,2,\cdots,n\) ),每条边上带有权值.所有权值都可以分解成 \(2^a\time ...

  5. [Codeforces]871D Paths

    失踪OJ回归. 毕竟这样的数论没做过几道,碰上一些具体的应用还是无所适从啊.小C还是借助这题大致摸索一下莫比乌斯函数吧. Description 有n个点,标号为1~n,为这n个点建一张无向图.两个点 ...

  6. bzoj1597[Usaco2008 Mar]土地购买 斜率优化dp

    1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5524  Solved: 2074[Submit] ...

  7. 10分钟 5步 发布以太坊 ERC20 代币

    1.安装 METAMASK Brings Ethereum to your browser 一个可以浏览器上进行操作的以太坊钱包,推荐 Chrome. Chrome 插件安装地址: https://c ...

  8. iOS 定位简单使用

    一.配置 导入库CoreLocation. 2.info.plist配置key NSLocationWhenInUseUsageDescription和NSLocationAlwaysUsageDes ...

  9. Ubuntu一些常用的软件安装及配置

    软件 安装 Vim echo "y" | sudo apt-get install vim 安装搜狗输入法 这个我在虚拟机里面尝试了好多遍,不断恢复备份然后重试.终于有了这个纯靠命 ...

  10. 学习C语言第一天!

    整理心得笔记: 1)c语言程序由函数构成,每个函数可以实现一个或多个功能.  2)一个正规程序可以有多个函数,但是有且只有一个主函数.  3)函数只有在被调用的时候才执行,主函数由系统调用执行.  4 ...