我正在开发一个橱柜设计中心,我正在寻找一种方法来导出我在 Unity 中设计的 3D 模型。需要导出的所有网格体和材质都位于一个父级中(参见图片)。具体来说,我想导出与 SketchUp 兼容的 3d 文件(不支持 .fbx)。你知道如何做到这一点吗?
我尝试导出 .obj 文件,但我在导出的文件中只得到几何图形,而不是彩色材质,这是该方法的脚本。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using System.Collections.Generic;
public class OBJExporter : MonoBehaviour
{
public bool onlySelectedObjects = false;
public bool applyPosition = true;
public bool applyRotation = true;
public bool applyScale = true;
public bool generateMaterials = true;
public bool exportTextures = true;
public bool splitObjects = true;
public bool autoMarkTexReadable = false;
public bool objNameAddIdNum = false;
string exportPath = "";
private string lastExportFolder;
private string versionString = "v2.0";
public Transform targetObject;
string MaterialToString(Material m)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("newmtl " + m.name);
// Add properties for URP Lit material
// Base color
if (m.HasProperty("_BaseColor"))
{
Color baseColor = m.GetColor("_BaseColor");
//Debug.Log(ColorToHex(baseColor));
sb.AppendLine("Kd " + baseColor.r.ToString() + " " + baseColor.g.ToString() + " " + baseColor.b.ToString());
if (baseColor.a < 1.0f)
{
sb.AppendLine("Tr " + (1f - baseColor.a).ToString());
sb.AppendLine("d " + baseColor.a.ToString());
}
}
sb.AppendLine("illum 2");
return sb.ToString();
}
string TryExportTexture(string propertyName, Material m)
{
if (m.HasProperty(propertyName))
{
Texture t = m.GetTexture(propertyName);
}
return "false";
}
Vector3 MultiplyVec3s(Vector3 v1, Vector3 v2)
{
return new Vector3(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z);
}
Vector3 RotateAroundPoint(Vector3 point, Vector3 pivot, Quaternion angle)
{
return angle * (point - pivot) + pivot;
}
private string ConstructOBJString(int index)
{
string idxString = index.ToString();
return idxString + "/" + idxString + "/" + idxString;
}
void Export()
{
exportPath = Application.dataPath + "/testobj.obj";
//init stuff
Dictionary<string, bool> materialCache = new Dictionary<string, bool>();
var exportFileInfo = new System.IO.FileInfo(exportPath);
lastExportFolder = exportFileInfo.Directory.FullName;
string baseFileName = System.IO.Path.GetFileNameWithoutExtension(exportPath);
//get list of required export things
MeshFilter[] sceneMeshes;
if (onlySelectedObjects)
{
List<MeshFilter> tempMFList = GetAllMeshFiltersInHierarchy(targetObject);
sceneMeshes = tempMFList.ToArray();
}
else
{
sceneMeshes = FindObjectsOfType(typeof(MeshFilter)) as MeshFilter[];
}
if (Application.isPlaying)
{
foreach (MeshFilter mf in sceneMeshes)
{
MeshRenderer mr = mf.gameObject.GetComponent<MeshRenderer>();
}
}
//work on export
StringBuilder sb = new StringBuilder();
StringBuilder sbMaterials = new StringBuilder();
sb.AppendLine("# Export of " + Application.loadedLevelName);
sb.AppendLine("# from Aaro4130 OBJ Exporter " + versionString);
if (generateMaterials)
{
sb.AppendLine("mtllib " + baseFileName + ".mtl");
}
float maxExportProgress = (float)(sceneMeshes.Length + 1);
int lastIndex = 0;
for (int i = 0; i < sceneMeshes.Length; i++)
{
string meshName = sceneMeshes[i].gameObject.name;
float progress = (float)(i + 1) / maxExportProgress;
MeshFilter mf = sceneMeshes[i];
MeshRenderer mr = sceneMeshes[i].gameObject.GetComponent<MeshRenderer>();
if (splitObjects)
{
string exportName = meshName;
if (objNameAddIdNum)
{
exportName += "_" + i;
}
sb.AppendLine("g " + exportName);
}
if (mr != null && generateMaterials)
{
Material[] mats = mr.sharedMaterials;
for (int j = 0; j < mats.Length; j++)
{
Material m = mats[j];
if (!materialCache.ContainsKey(m.name))
{
materialCache[m.name] = true;
sbMaterials.Append(MaterialToString(m));
sbMaterials.AppendLine();
}
}
}
//export the meshhh :3
Mesh msh = mf.sharedMesh;
int faceOrder = (int)Mathf.Clamp((mf.gameObject.transform.lossyScale.x * mf.gameObject.transform.lossyScale.z), -1, 1);
//export vector data (FUN :D)!
foreach (Vector3 vx in msh.vertices)
{
Vector3 v = vx;
if (applyScale)
{
v = MultiplyVec3s(v, mf.gameObject.transform.lossyScale);
}
if (applyRotation)
{
v = RotateAroundPoint(v, Vector3.zero, mf.gameObject.transform.rotation);
}
if (applyPosition)
{
v += mf.gameObject.transform.position;
}
v.x *= -1;
sb.AppendLine("v " + v.x + " " + v.y + " " + v.z);
}
foreach (Vector3 vx in msh.normals)
{
Vector3 v = vx;
if (applyScale)
{
v = MultiplyVec3s(v, mf.gameObject.transform.lossyScale.normalized);
}
if (applyRotation)
{
v = RotateAroundPoint(v, Vector3.zero, mf.gameObject.transform.rotation);
}
v.x *= -1;
sb.AppendLine("vn " + v.x + " " + v.y + " " + v.z);
}
foreach (Vector2 v in msh.uv)
{
sb.AppendLine("vt " + v.x + " " + v.y);
}
for (int j = 0; j < msh.subMeshCount; j++)
{
if (mr != null && j < mr.sharedMaterials.Length)
{
string matName = mr.sharedMaterials[j].name;
sb.AppendLine("usemtl " + matName);
}
else
{
sb.AppendLine("usemtl " + meshName + "_sm" + j);
}
int[] tris = msh.GetTriangles(j);
for (int t = 0; t < tris.Length; t += 3)
{
int idx2 = tris[t] + 1 + lastIndex;
int idx1 = tris[t + 1] + 1 + lastIndex;
int idx0 = tris[t + 2] + 1 + lastIndex;
if (faceOrder < 0)
{
sb.AppendLine("f " + ConstructOBJString(idx2) + " " + ConstructOBJString(idx1) + " " + ConstructOBJString(idx0));
}
else
{
sb.AppendLine("f " + ConstructOBJString(idx0) + " " + ConstructOBJString(idx1) + " " + ConstructOBJString(idx2));
}
}
}
lastIndex += msh.vertices.Length;
}
//write to disk
System.IO.File.WriteAllText(exportPath, sb.ToString());
if (generateMaterials)
{
System.IO.File.WriteAllText(exportFileInfo.Directory.FullName + "\\" + baseFileName + ".mtl", sbMaterials.ToString());
}
}
public List<MeshFilter> GetAllMeshFiltersInHierarchy(Transform parent)
{
List<MeshFilter> meshFilters = new List<MeshFilter>();
FindMeshFilters(parent, meshFilters);
return meshFilters;
}
private void FindMeshFilters(Transform parent, List<MeshFilter> meshFilters)
{
MeshFilter meshFilter = parent.GetComponent<MeshFilter>();
if (meshFilter != null && meshFilter.gameObject.activeSelf)
{
meshFilters.Add(meshFilter);
}
foreach (Transform child in parent)
{
FindMeshFilters(child, meshFilters);
}
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
Export();
}
}
string ColorToHex(Color color)
{
Color32 color32 = color;
return string.Format("#{0:X2}{1:X2}{2:X2}", color32.r, color32.g, color32.b);
}
}
这不是一个单一的问题 为此,您必须实现 3D 转换器,因为您要将统一材质等内容与 obj 网格和纹理混合在一起。 您应该根据要导出的文件格式及其限制来创建材料和每个元素:
文件结构
顶点数据
o 几何顶点 (v) o 纹理顶点 (vt) o 顶点法线 (vn) o 参数空间顶点 (vp) 自由曲线/曲面属性 o 曲线或曲面类型的有理或非有理形式: 基础矩阵、Bezier、B 样条、Cardinal、Taylor(cstype) o 度 (deg) o 基础矩阵 (bmat) o 步长(步长)
元素
o 点 (p) o 线 (l) o 面 (f) o 曲线(曲线) o 2D 曲线 (curv2) o 表面(冲浪)
自由曲线/曲面体声明
o 参数值 (parm) o 外部修剪环(修剪) o 内修边环(孔) o 特殊曲线(scrv) o 特殊点 (sp) o 结束语句(结束)
自由曲面之间的连接
o 连接(con)
分组
o 组名 (g) o 平滑组 o 合并基团(mg) o 对象名称 (o)
显示/渲染属性
o 斜角插补(bevel) o 颜色插值 (c_interp) o 溶解插值 (d_interp) o 细节层次 (lod) o 材料名称 (usemtl) o 材料库(mtllib) o 阴影投射 (shadow_obj) o 光线追踪 (trace_obj) o 曲线近似技术(ctech) o 表面近似技术(stech)