博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Unity3D Asset文件导出3DMax 可编辑格式
阅读量:6901 次
发布时间:2019-06-27

本文共 11054 字,大约阅读时间需要 36 分钟。

本文章由cartzhang编写,转载请注明出处。 全部权利保留。

文章链接:
作者:cartzhang

一、前言

美术想要一个把unity中*.asset的模型导出来,导成3D Max能够打开的模式。fbx或obj.
须要导出的格式:

这里写图片描写叙述

图1
也就是须要一个工具,个人认为这个问题。肯定之前Unity的前辈就有解决方法了。于是乎网上一通下载和測试。

二、解包工具集合

网络上找来了各种測试。可是没有一个适合我的。非常多都是失败。打不开。

參考宣雨松的博客。找了还是没有结果。

这里写图片描写叙述

图3
解包工具有非常多种类,
disunity github地址:
还有就是AssetAssetsExport,还有Unity Studio.
别人的博客里面都有比較多的介绍和说明。这里就具体说了。

最后还网上wiki里,找到了一个合适的我自己的解包。

title=ObjExporter

三、初步成果

找到了一个站点:

title=ObjExporter

能够导出部分对象。

例如以下图:

这里写图片描写叙述

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

这里写图片描写叙述

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

四、bug改动

事实上也不能算bug,或许人家没有这种须要呢。

Component[] meshfilter = selection[i].GetComponentsInChildren
(); 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
(); for (int j = 0; j < meshfilterRender.Length; j++) { if (meshfilterRender[j].GetComponent
() == null) { meshfilterRender[j].gameObject.AddComponent
(); meshfilterRender[j].GetComponent
().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 selectthe 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 thesame 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@cartzhangfixed 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
materialList) { Debug.Assert(null != mf); Mesh m = mf.sharedMesh; Material[] mats = mf.GetComponent
().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); 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
PrepareFileWrite() { Clear(); return new Dictionary
(); } private static void MaterialsToFile(Dictionary
materialList, string folder, string filename) { using (StreamWriter sw = new StreamWriter(folder + Path.DirectorySeparatorChar + filename + ".mtl")) { foreach (KeyValuePair
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 file File.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
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
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, ""); } 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++) { // 没有meshFilter,加入一个meshFilter. SkinnedMeshRenderer[] meshfilterRender = selection[i].GetComponentsInChildren
(); for (int j = 0; j < meshfilterRender.Length; j++) { if (meshfilterRender[j].GetComponent
() == null) { meshfilterRender[j].gameObject.AddComponent
(); meshfilterRender[j].GetComponent
().sharedMesh = Instantiate(meshfilterRender[j].sharedMesh); } } Component[] meshfilter = selection[i].GetComponentsInChildren
(); 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", ""); } else EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", ""); }}

能够直接复制到直接的项目中使用。

五、怎么使用呢?

首先把代码复制到项目中,直接下载project也行。
步骤一

这里写图片描写叙述

图8
步骤二

这里写图片描写叙述

图10
步骤三

这里写图片描写叙述

图11
就能够使用你的模型编辑工具来查看了。

五、源代码和演示样例project

源代码地址:

演示样例project地址:
博客图片地址:
Github readme:

六、參考

【1】

【2】

【3】

【4】

【5】

title=ObjExporter

【6】

七。最后但不是不重要

Asset导出成FBX的格式:
与上面介绍的不是一个方法。可是思路都一样。这个源代码能够把纹理和材质都匹配上去,当然我也做了略微的改动。修复了之前的小bug。

非常感谢,欢迎留言!!

你可能感兴趣的文章
poj 2389 大整数乘法
查看>>
redis数据类型
查看>>
JSON.stringify JSON.parse
查看>>
第三次作业
查看>>
13-标准文档流
查看>>
就业指导第三次作业
查看>>
vscode格式化设置
查看>>
接口测试工具-Jmeter使用笔记(五:正则表达式提取器)
查看>>
VBA-从周课表统计节次
查看>>
(二十三)控制转移指令
查看>>
编译时设置动态链接库路径
查看>>
Java基础代码库:ACM的A+B问题
查看>>
日志分析工具ELK(二)
查看>>
js中eval详解
查看>>
软件设计
查看>>
HDU 5753 Permutation Bo
查看>>
【问题集】VS新建项目——失败——弹出“未将对象引用设置到对象的实例”...
查看>>
blog.yiz96.com
查看>>
centos 创建桌面双击启动程序(更改图标)
查看>>
jq 的grep(); 数组筛选方法
查看>>