本文章由cartzhang编写,转载请注明出处。 所有权利保留。
文章链接:http://blog.csdn.net/cartzhang/article/details/60878354
作者:cartzhang

一、前言

美术想要一个把unity中*.asset的模型导出来,导成3D Max可以打开的模式,fbx或obj.

需要导出的格式:


图1

也就是需要一个工具,个人觉得这个问题,肯定之前Unity的前辈就有解决方法了。于是乎网上一通下载和测试。

二、解包工具集合

网络上找来了各种测试,但是没有一个适合我的,很多都是失败,打不开。
参考宣雨松的博客,找了还是没有结果。


图3

解包工具有很多种类,
disunity github地址: https://github.com/ata4/disunity

还有就是AssetAssetsExport,还有Unity Studio.
别人的博客里面都有比较多的介绍和说明,这里就详细说了。

最后还网上wiki里,找到了一个合适的我自己的解包。
http://wiki.unity3d.com/index.php?title=ObjExporter

三、初步成果

找到了一个网站:http://wiki.unity3d.com/index.php?title=ObjExporter

可以导出部分对象。
如下图:


图0

而原来unity中模型是这个样子的。


图4

导出的只有武器和头盔,没有人物主体body.

四、bug修改

其实也不能算bug,也许人家没有这样的需要呢。

 Component[] meshfilter = selection[i].GetComponentsInChildren<MeshFilter>();MeshFilter[] mf = new MeshFilter[meshfilter.Length];int m = 0;for (; m < meshfilter.Length; m++){exportedObjects++;mf[m] = (MeshFilter)meshfilter[m];}

代码中是要查找所有组件中的MeshFilter,发现SkinnedMeshRender组件居然没有这个MeshFilter这个组件,所以总会导出少一个,而这个居然是人的主体。


图5

本来说让美术自己添加一个MeshFilter组件,然后根据mesh render中的mesh自己来添加一个对应的mesh.

既然是程序,那就想办法,思路很明显,既然是有meshrender,就从这入手呗。

代码还是不难度。

// 没有meshFilter,添加一个meshFilter.SkinnedMeshRenderer[] meshfilterRender = selection[i].GetComponentsInChildren<SkinnedMeshRenderer>();for (int j = 0; j < meshfilterRender.Length; j++){   if (meshfilterRender[j].GetComponent<MeshFilter>() == null){meshfilterRender[j].gameObject.AddComponent<MeshFilter>();meshfilterRender[j].GetComponent<MeshFilter>().sharedMesh = Instantiate(meshfilterRender[j].sharedMesh);}}

这样修改过,就会自动在没有MeshFilter,但是有skinnedMeshRender组件的节点下,添加一个MeshFilter,然后就可以正常导出成.obj文件,与.FBX是类似的,都可以被3D max编辑使用。


图7

最后的在VS中看的模型,因为没有安装3Dmax.


图6

虽然看起来简陋,但是满足他们小需要,就好了。

贴出主要的代码:

/*
Based on ObjExporter.cs, this "wrapper" lets you export to .OBJ directly from the editor menu.This should be put in your "Editor"-folder. Use by selecting the objects you want to export, and select
the appropriate menu item from "Custom->Export". Exported models are put in a folder called
"ExportedObj" in the root of your Unity-project. Textures should also be copied and placed in the
same folder.
N.B. there may be a bug so if the custom option doesn't come up refer to this thread http://answers.unity3d.com/questions/317951/how-to-use-editorobjexporter-obj-saving-script-fro.html Updated for Unity 5.32017-03-07
@cartzhang
fixed can not create obj file in folder.
*/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){Debug.Assert(null != mf);Mesh m = mf.sharedMesh;Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;StringBuilder sb = new StringBuilder();if (null == m)return sb.ToString();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);elseobjMaterial.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 + Path.DirectorySeparatorChar + 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(Path.DirectorySeparatorChar);if (stripIndex >= 0)destinationFile = destinationFile.Substring(stripIndex + 1).Trim();string relativeFile = destinationFile;destinationFile = folder + Path.DirectorySeparatorChar + destinationFile;Debug.Log("Copying texture from " + kvp.Value.textureName + " to " + destinationFile);try{//Copy the source fileFile.Copy(kvp.Value.textureName, destinationFile);}catch{}sw.Write("map_Kd {0}", relativeFile);}sw.Write("\n\n\n");}}}private static void MeshToFile(MeshFilter mf, string folder, string filename){Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + 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 + Path.DirectorySeparatorChar + 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 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(Path.DirectorySeparatorChar);if (stripIndex >= 0)filename = filename.Substring(stripIndex + 1).Trim();MeshesToFile(mf, targetFolder, filename);EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects to " + filename, "");}elseEditorUtility.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++){// 没有meshFilter,添加一个meshFilter.SkinnedMeshRenderer[] meshfilterRender = selection[i].GetComponentsInChildren<SkinnedMeshRenderer>();for (int j = 0; j < meshfilterRender.Length; j++){   if (meshfilterRender[j].GetComponent<MeshFilter>() == null){meshfilterRender[j].gameObject.AddComponent<MeshFilter>();meshfilterRender[j].GetComponent<MeshFilter>().sharedMesh = Instantiate(meshfilterRender[j].sharedMesh);}}Component[] meshfilter = selection[i].GetComponentsInChildren<MeshFilter>();MeshFilter[] mf = new MeshFilter[meshfilter.Length];int m = 0;for (; 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", "");}elseEditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");}}

可以直接复制到直接的项目中使用。

五、怎么使用呢?

首先把代码拷贝到项目中,直接下载工程也行。

步骤一


图8

步骤二


图10

步骤三


图11

就可以使用你的模型编辑工具来查看了。

五、源码和示例工程

源码地址:

https://github.com/cartzhang/unity_lab/blob/master/ExportFbx/UnityAssetExportFBX/Assets/Editor/OBJExport/EditorObjExporter.cs

示例工程地址:
https://github.com/cartzhang/unity_lab/tree/master/ExportFbx/UnityAssetExportFBX

博客图片地址:
https://github.com/cartzhang/unity_lab/tree/master/ExportFbx/Img

Github readme:
https://github.com/cartzhang/unity_lab/blob/master/ExportFbx/Unity%20asset%E6%96%87%E4%BB%B6%20%E5%AF%BC%E5%87%BAOBJ.md

六、参考

【1】http://www.xuanyusong.com/archives/3618

【2】https://forums.inxile-entertainment.com/viewtopic.php?t=13724

【3】http://www.cnblogs.com/Niger123/p/4261763.html

【4】http://prog3.com/sbdm/download/download/akof1314/9097153

【5】http://wiki.unity3d.com/index.php?title=ObjExporter

【6】https://github.com/KellanHiggins/UnityFBXExporter/tree/master/Assets/Packages/UnityFBXExporter

七,最后但不是不重要

Asset导出成FBX的格式:https://github.com/cartzhang/UnityFBXExporter

与上面介绍的不是一个方法,但是思路都一样。这个源码可以把纹理和材质都匹配上去,当然我也做了稍微的修改,修复了之前的小bug。

非常感谢,欢迎留言!!

Unity3D Asset文件导出3DMax 可编辑格式相关推荐

  1. 将指定路径下的所有SVG文件导出成PNG等格式的图片(缩略图或原图大小)

    原文:将指定路径下的所有SVG文件导出成PNG等格式的图片(缩略图或原图大小) WPF的XAML文档(Main.xaml): <Window x:Class="SVG2Image.Ma ...

  2. obj文件编辑软件_工程动画制作 | MAX文件导出obj、fbx格式在Bentley软件中应用

    一.利用3dmax打开max模型,利用材质球吸取材质. 看材质是否为标准材质(standard),如若是,继续下一步操作.如若不是,修改材质球属性,保留贴图为子材质,更改为标准材质. 二.处理模型面数 ...

  3. 3ds max文件导出osg或者ive格式

    osg/osgEarth系列文章目录 文章目录 osg/osgEarth系列文章目录 前言 参考 前言 首先下载插件osgexp Osgexp的下载地址 安装上之后,如果3ds max导出里面已经可以 ...

  4. 3dsmax导出html,3dsmax导出模型(怎么把3DMax文件导出CAD图纸?)

    3Dmax带路径动画的模型怎么导出 3dsmax带路径动画的模型导出设置如上图: 1    在时间输出里选择活动时间段或范围. 2    渲染输出文件格式选择.avi ,勾选保存文件. 怎么把3DMa ...

  5. c#实现将Excel文件导出为csv和UTF8格式的txt文件

    工作中为了方便,经常使用excel表进行编辑,然后转换为程序用的csv和txt文件. 用c#做了个小工具,生成的exe文件放到需要转换的excel文件目录,可以实在自动读取该目录下的所有xlsx文件, ...

  6. Revit文件导出svg格式思考

    Revit没有直接导出svg格式文件的方法,但是动动脑子,还是会有一些不太靠谱的方法,可以完成这件事情.说它不太靠谱是因为这些方法非官方,并且这些方法用起来存在这样或那样的问题,很难百分百满足既定需求 ...

  7. ArcGIS导出AI或EPS格式的地图图片并在Adobe Illustrator中继续编辑

      本文介绍在ArcGIS下属的ArcMap软件中,将绘制好的地图导出为.ai或者.eps格式文件,并在Adobe Illustrator软件中进一步编辑地图,并最终导出为图片格式的结果文件的方法. ...

  8. ML之FE:基于FE特征工程对RentListingInquries数据集进行预处理并导出为三种格式文件(csv格式/txt格式/libsvm稀疏txt格式)

    ML之FE:基于FE特征工程对RentListingInquries数据集进行预处理并导出为三种格式文件(csv格式/txt格式/libsvm稀疏txt格式) 目录 输出结果 设计思路 核心代码 输出 ...

  9. asp.net导出excel-一行代码实现excel、xml、pdf、word、html、csv等7种格式文件导出功能而且美观-SNF快速开发平台...

    分享: 腾讯微博  新浪微博   搜狐微博   网易微博  腾讯朋友  百度贴吧  豆瓣   QQ好友  人人网 作者:王春天  原文地址:http://www.cnblogs.com/spring_ ...

  10. EasyUI入门9 EasyUI+NPOI+QrCode实现带二维码复杂格式excel文件导出

    概述 导出excel文件,而且文件带有复杂的表头格式并有二维码信息.前端界面是easyui,后端是.net(用ashx方式),使用了NPOI和Qrcode,本示例还增加了页面参数传值,实现的思路很简单 ...

最新文章

  1. Compound Interest Calculator4.0
  2. dotweb——go语言的一个微型web框架(二)启动dotweb
  3. JVM插桩之三:javaagent介绍及javassist介绍
  4. mysql myisam 锁机制_MySQL--MyISAM之锁机制
  5. Android逆向笔记-Proguard混淆Android代码以及去打印日志信息
  6. 除了云原生,2021 年还有这八大趋势值得关注
  7. 如何避免单元测试陷阱?
  8. SQL基础知识总结(SQL必知必会)
  9. 宽带无线通信OFDM技术
  10. py的征途2之简例分享
  11. 攻防世界hello _pwn总结
  12. 用了一个多月时间治好了自己的胸膜炎
  13. 基于python的论文摘要怎么写_Python实现文章摘要的提取方式
  14. 【ROS基础】.launch文件语法记录
  15. 百度地图迁徙大数据_百度地图迁徙大数据:北上广深城内出行年后首次大幅增长...
  16. 这五个方法能提高程序员工作效率
  17. GitLab 之 历史版本回退
  18. html页面中该插件不受支持,该插件不受支持怎么解决
  19. 哈工大2018秋高级语言程序设计课程大作业
  20. Linux内核的五大模块

热门文章

  1. 【FatFs】FAT32文件系统协议总结(理论+实践)
  2. 2D地图tile纹理自动拼接算法
  3. 领域驱动实践总结(基本理论总结与分析+架构分析与代码设计V+具体应用设计分析)
  4. 速成! | 遗传算法详解及其MATLAB实现
  5. Photoshop插件-黑白(三)-脚本开发-PS插件
  6. Java学习笔记【2】:抛出异常
  7. LTspice基础教程-004.系统自带仿真库介绍
  8. Apple和Ruby近况:Rails的iPhone配置实用工具和Ruby的SproutCore工具
  9. 鸿蒙升级之前APP没有,升级鸿蒙2.0之后,没有原生的日历app了
  10. 开源进销存管理系统学习教程