原文链接

使用方法请参考原文链接。导出的Obj文件可以作为RecastNavigation的RecastDemo.exe程序的输入文件。

脚本修改如下,支持Unity 5.3.5f1。仅修改了原文链接代码的Unity版本兼容性。

EditorObjExporter.cs

using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System;
struct ObjMaterial
{
public string name;
public string textureName;
}
public class EditorObjExporter : ScriptableObject
{
private static int vertexOffset = 0;
private static int normalOffset = 0;
private static int uvOffset = 0;
//User should probably be able to change this. It is currently left as an excercise for
//the reader.
private static string targetFolder = "ExportedObj";
private static string MeshToString(MeshFilter mf, Dictionary<string, ObjMaterial> materialList)
{
Mesh m = mf.sharedMesh;
Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;
StringBuilder sb = new StringBuilder();
sb.Append("g ").Append(mf.name).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");
foreach (Vector3 lv in m.normals)
{
Vector3 wv = mf.transform.TransformDirection(lv);
sb.Append(string.Format("vn {0} {1} {2}\n", -wv.x, wv.y, wv.z));
}
sb.Append("\n");
foreach (Vector3 v in m.uv)
{
sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));
}
for (int material = 0; material < m.subMeshCount; material++)
{
sb.Append("\n");
sb.Append("usemtl ").Append(mats[material].name).Append("\n");
sb.Append("usemap ").Append(mats[material].name).Append("\n");
//See if this material is already in the materiallist.
try
{
ObjMaterial objMaterial = new ObjMaterial();
objMaterial.name = mats[material].name;
if (mats[material].mainTexture)
objMaterial.textureName = AssetDatabase.GetAssetPath(mats[material].mainTexture);
else
objMaterial.textureName = null;
materialList.Add(objMaterial.name, objMaterial);
}
catch (ArgumentException)
{
//Already in the dictionary
}
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}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n",
triangles[i] + 1 + vertexOffset, triangles[i + 1] + 1 + normalOffset, triangles[i + 2] + 1 + uvOffset));
}
}
vertexOffset += m.vertices.Length;
normalOffset += m.normals.Length;
uvOffset += m.uv.Length;
return sb.ToString();
}
private static void Clear()
{
vertexOffset = 0;
normalOffset = 0;
uvOffset = 0;
}
private static Dictionary<string, ObjMaterial> PrepareFileWrite()
{
Clear();
return new Dictionary<string, ObjMaterial>();
}
private static void MaterialsToFile(Dictionary<string, ObjMaterial> materialList, string folder, string filename)
{
using (StreamWriter sw = new StreamWriter(folder + "/" + filename + ".mtl"))
{
foreach (KeyValuePair<string, ObjMaterial> kvp in materialList)
{
sw.Write("\n");
sw.Write("newmtl {0}\n", kvp.Key);
sw.Write("Ka  0.6 0.6 0.6\n");
sw.Write("Kd  0.6 0.6 0.6\n");
sw.Write("Ks  0.9 0.9 0.9\n");
sw.Write("d  1.0\n");
sw.Write("Ns  0.0\n");
sw.Write("illum 2\n");
if (kvp.Value.textureName != null)
{
string destinationFile = kvp.Value.textureName;
int stripIndex = destinationFile.LastIndexOf('/');//FIXME: Should be Path.PathSeparator;
if (stripIndex >= 0)
destinationFile = destinationFile.Substring(stripIndex + 1).Trim();
string relativeFile = destinationFile;
destinationFile = folder + "/" + destinationFile;
Debug.Log("Copying texture from " + kvp.Value.textureName + " to " + destinationFile);
try
{
//Copy the source file
File.Copy(kvp.Value.textureName, destinationFile);
}
catch
{
}
sw.Write("map_Kd {0}", relativeFile);
}
sw.Write("\n");
}
}
}
private static void MeshToFile(MeshFilter mf, string folder, string filename)
{
Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();
using (StreamWriter sw = new StreamWriter(folder + "/" + filename + ".obj"))
{
sw.Write("mtllib ./" + filename + ".mtl\n");
sw.Write(MeshToString(mf, materialList));
}
MaterialsToFile(materialList, folder, filename);
}
private static void MeshesToFile(MeshFilter[] mf, string folder, string filename)
{
Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();
using (StreamWriter sw = new StreamWriter(folder + "/" + filename + ".obj"))
{
sw.Write("mtllib ./" + filename + ".mtl\n");
for (int i = 0; i < mf.Length; i++)
{
sw.Write(MeshToString(mf[i], materialList));
}
}
MaterialsToFile(materialList, folder, filename);
}
private static bool CreateTargetFolder()
{
try
{
System.IO.Directory.CreateDirectory(targetFolder);
}
catch
{
EditorUtility.DisplayDialog("Error!", "Failed to create target folder!", "");
return false;
}
return true;
}
[MenuItem("Custom/Export/Export all MeshFilters in selection to separate OBJs")]
static void ExportSelectionToSeparate()
{
if (!CreateTargetFolder())
return;
Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
if (selection.Length == 0)
{
EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
return;
}
int exportedObjects = 0;
for (int i = 0; i < selection.Length; i++)
{
Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));
for (int m = 0; m < meshfilter.Length; m++)
{
exportedObjects++;
MeshToFile((MeshFilter)meshfilter[m], targetFolder, selection[i].name + "_" + i + "_" + m);
}
}
if (exportedObjects > 0)
EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
else
EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
}
[MenuItem("Custom/Export/Export whole selection to single OBJ")]
static void ExportWholeSelectionToSingle()
{
if (!CreateTargetFolder())
return;
Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
if (selection.Length == 0)
{
EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
return;
}
int exportedObjects = 0;
ArrayList mfList = new ArrayList();
for (int i = 0; i < selection.Length; i++)
{
Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));
for (int m = 0; m < meshfilter.Length; m++)
{
exportedObjects++;
mfList.Add(meshfilter[m]);
}
}
if (exportedObjects > 0)
{
MeshFilter[] mf = new MeshFilter[mfList.Count];
for (int i = 0; i < mfList.Count; i++)
{
mf[i] = (MeshFilter)mfList[i];
}
string filename = EditorSceneManager.GetActiveScene().name + "_" + exportedObjects;
int stripIndex = filename.LastIndexOf('/');//FIXME: Should be Path.PathSeparator
if (stripIndex >= 0)
filename = filename.Substring(stripIndex + 1).Trim();
MeshesToFile(mf, targetFolder, filename);
EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects to " + filename, "");
}
else
EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
}
[MenuItem("Custom/Export/Export each selected to single OBJ")]
static void ExportEachSelectionToSingle()
{
if (!CreateTargetFolder())
return;
Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);
if (selection.Length == 0)
{
EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
return;
}
int exportedObjects = 0;
for (int i = 0; i < selection.Length; i++)
{
Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));
MeshFilter[] mf = new MeshFilter[meshfilter.Length];
for (int m = 0; m < meshfilter.Length; m++)
{
exportedObjects++;
mf[m] = (MeshFilter)meshfilter[m];
}
MeshesToFile(mf, targetFolder, selection[i].name + "_" + i);
}
if (exportedObjects > 0)
{
EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
}
else
EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
}
}


将Unity场景以Wavefront Obj格式导出相关推荐

  1. 将Unity地形以Wavefront Obj格式导出

    原文链接 使用方法请参考原文链接.导出的Obj文件可以作为RecastNavigation的RecastDemo.exe程序的输入文件. 对于已经创建好的Unity Scene,使用原文链接给出的Ed ...

  2. Unity将场景和物体导出为.obj格式文件

    项目中美术有需求,需要将Unity场景中的预设物导出为3DMax可用的.obj格式的文件,所以就写了一个编辑器工具类,根据所选择的场景物体导出,比例关系为 Unity 1米 = 3DMax 1厘米 工 ...

  3. Unity场景素材导出为 FBX文件的方法

    系列文章目录 一.Unity场景素材导出为 FBX文件的方法:http://t.csdn.cn/Xyjxe 二.Unity场景素材导出为 OBJ文件的方法:http://t.csdn.cn/08RY3 ...

  4. STL、PLY、OBJ格式分析

    2018-05-18 这三种3D格式文件时我们在做CAD或者游戏开发中基本上都会遇到的.所以,如果想要把这些文件转化成自己的程序中想要操作的对象,一般都需要对这些文件进行解析.这些格式时公开的,如ST ...

  5. blender 导出 obj 格式,3dsmax 导入3ds max obj 格式数据

    blender 导出 obj 格式,3dsmax 导入3ds max obj 格式数据 blender 导出 3dsmax 导入

  6. 3dmaxobj导出选项_3dmax将模型导出成OBJ格式和贴图的一些注意点

    3dmax建模,如果将3D模型导出成其他软件也能够打开的格式,OBJ是一个非常好的格式选择.3dmax导出obj格式文件时缺少贴图,模型/贴图/材质/渲染都没有问题,导出来的obj为什么就没有贴图呢? ...

  7. 使用blender做一个地球模型,贴上UV纹理,导出Obj格式

    使用blender做一个地球模型,贴上UV纹理,导出Obj格式 Table of Contents 1 blender 1.1 图文教程 1.2 export to objloader, coordi ...

  8. 将三维模型(obj)导出js格式供threeJS中调用

    前言 前段时间自己做过将在3Dmax中画的三维模型转换为js格式,通过threeJS中调用显示,最近又在做相关的项目,在这写个笔记吧. 1. 转换前准备 准备obj格式的三维模型 首先你肯定得需要一个 ...

  9. 67 Three.js 导入OBJ格式的模型

    简介 OBJ是一种简单的三维文件格式,由Wavefront Technologies创建.它是使用最广泛的三维文件格式,用来定义对象的几何体.MTL文件常同OBJ文件一起使用. Three.js还有一 ...

最新文章

  1. 从今天开始收集一些经典的算法。
  2. 微生物组学研究的可再现性、可重现性、稳定性与普适性
  3. gta5显示nat较为严格_为何严格治理下雾霾天仍频发?哈尔滨市环保局解答重污染天3大疑问...
  4. android app固定dp,Android屏幕适配—被偷走的dp
  5. 封装性的基本使用练习1
  6. Numpy——常用的排序函数
  7. mysql完全卸载大全
  8. 独家披露51CTO被黑过程:数据库已小范围流传
  9. notepad++ 快捷键大全
  10. AxureRP7.0基础教程系列 部件详解Text Area 文本段落
  11. Android 友盟分享(截图指定的View分享)
  12. 【软件资源】VS2013软件安装全教程!(附VS各版本下载地址)
  13. 猴子摘香蕉问题python_[转载]猴子摘香蕉问题的状态空间表示法
  14. MATLAB函数文件的使用
  15. 码农十分钟的音律概述 纯律 五度相生律 十二平均律
  16. 使用Centreon监控HP惠普服务器硬件状态
  17. 关于科傻软件的使用感受
  18. 微信气泡主题设置_微信主题! 米老鼠微信主题气泡设置教程方法
  19. 第2章 Python 分支结构
  20. STM32-通用定时器-定时器中断

热门文章

  1. 张朝阳喊话俞敏洪:为什么还不退休?
  2. Sigmod 和 Logit
  3. 【全开源+免费更新】doodoo.js项目结构
  4. MyBatis框架(二):多对一查询、一对多查询、ResultMap、动态SQL
  5. 答题对战方案java_使用WebSocket实现实时多人答题对战游戏
  6. python可以写什么视觉特效_Python实现视觉特效:一行命令给头像自动戴上口罩的案例...
  7. 8.2 知识蒸馏方法概述
  8. echarts 的初始化
  9. 中餐菜单分类名称创意_餐厅菜单的种类分类
  10. 百鸡问题用计算机思维,大力出奇迹:当古代数学难题遇到计算机