Untitled
unknown
plain_text
a year ago
22 kB
7
Indexable
using UnityEngine;
using System.Collections;
using System.Text;
using System.Collections.Generic;
using FE = AsciiFBXExporter.FBXExporter;
namespace AsciiFBXExporter
{
public class FBXUnityMeshGetter
{
/// <summary>
/// Gets all the meshes and outputs to a string (even grabbing the child of each gameObject)
/// </summary>
/// <returns>The mesh to string.</returns>
/// <param name="gameObj">GameObject Parent.</param>
/// <param name="materials">Every Material in the parent that can be accessed.</param>
/// <param name="objects">The StringBuidler to create objects for the FBX file.</param>
/// <param name="connections">The StringBuidler to create connections for the FBX file.</param>
/// <param name="parentObject">Parent object, if left null this is the top parent.</param>
/// <param name="parentModelId">Parent model id, 0 if top parent.</param>
public static long GetMeshToString(GameObject gameObj,
Material[] materials,
ref StringBuilder objects,
ref StringBuilder connections,
GameObject parentObject = null,
long parentModelId = 0)
{
StringBuilder tempObjectSb = new StringBuilder();
StringBuilder tempConnectionsSb = new StringBuilder();
long geometryId = FBXExporter.GetRandomFBXId();
long modelId = FBXExporter.GetRandomFBXId();
// Sees if there is a mesh to export and add to the system
MeshFilter filter = gameObj.GetComponent<MeshFilter>();
SkinnedMeshRenderer skinnedMesh = gameObj.GetComponent<SkinnedMeshRenderer>();
//if(filter != null)
//Debug.Log(filter.gameObject.name + " " + filter.GetComponent<MeshRenderer>().enabled);
// The mesh to export is this level's mesh that is going to be exported
Mesh meshToExport = null;
if (!gameObj.activeInHierarchy)
return -1;
Debug.Log(gameObj.name + " OOO " + gameObj.activeInHierarchy);
if (filter != null && gameObj.activeInHierarchy)
{
if(filter.gameObject.activeInHierarchy)
{
if (filter.GetComponent<MeshRenderer>() != null && filter.GetComponent<MeshRenderer>().enabled && filter.gameObject.name != "Cube")
{
//Debug.Log(filter.gameObject.name +" if");
meshToExport = filter.sharedMesh;
}
else
{
//Debug.Log(filter.gameObject.name +" else");
}
}
}
//else if(skinnedMesh != null) // If this object has a skinned mesh on it, bake that mesh into whatever pose it is at and add it as a new mesh to export
//{
// meshToExport = new Mesh();
// skinnedMesh.BakeMesh(meshToExport);
//}
string meshName = gameObj.name;
// A NULL parent means that the gameObject is at the top
string isMesh = "Null";
if(meshToExport != null)
{
meshName = meshToExport.name;
isMesh = "Mesh";
}
if (filter != null)
{
if(filter.sharedMesh == null)
{
// The MeshFilter has no mesh assigned, so treat it like an FBX Null node.
filter = null;
}
else
{
meshName = filter.sharedMesh.name;
isMesh = "Mesh";
}
}
// If we've got a skinned mesh without a name, give it a random name
if(meshName == "" && skinnedMesh != null)
meshName = "Skinned Mesh " + Random.Range(0, 1000000);
if(parentModelId == 0)
tempConnectionsSb.AppendLine("\t;Model::" + meshName + ", Model::RootNode");
else
tempConnectionsSb.AppendLine("\t;Model::" + meshName + ", Model::USING PARENT");
tempConnectionsSb.AppendLine("\tC: \"OO\"," + modelId + "," + parentModelId);
tempConnectionsSb.AppendLine();
tempObjectSb.AppendLine("\tModel: " + modelId + ", \"Model::" + gameObj.name + "\", \"" + isMesh + "\" {");
tempObjectSb.AppendLine("\t\tVersion: 232");
tempObjectSb.AppendLine("\t\tProperties70: {");
tempObjectSb.AppendLine("\t\t\tP: \"RotationOrder\", \"enum\", \"\", \"\",4");
tempObjectSb.AppendLine("\t\t\tP: \"RotationActive\", \"bool\", \"\", \"\",1");
tempObjectSb.AppendLine("\t\t\tP: \"InheritType\", \"enum\", \"\", \"\",1");
tempObjectSb.AppendLine("\t\t\tP: \"ScalingMax\", \"Vector3D\", \"Vector\", \"\",0,0,0");
tempObjectSb.AppendLine("\t\t\tP: \"DefaultAttributeIndex\", \"int\", \"Integer\", \"\",0");
// ===== Local Translation Offset =========
Vector3 position = gameObj.transform.localPosition;
tempObjectSb.Append("\t\t\tP: \"Lcl Translation\", \"Lcl Translation\", \"\", \"A+\",");
// Append the X Y Z coords to the system
tempObjectSb.AppendFormat("{0},{1},{2}", FE.FBXFormat(position.x * -1), FE.FBXFormat(position.y), FE.FBXFormat(position.z));
tempObjectSb.AppendLine();
// Rotates the object correctly from Unity space
Vector3 localRotation = gameObj.transform.localEulerAngles;
tempObjectSb.AppendFormat("\t\t\tP: \"Lcl Rotation\", \"Lcl Rotation\", \"\", \"A+\",{0},{1},{2}", FE.FBXFormat(localRotation.x), FE.FBXFormat(localRotation.y * -1), FE.FBXFormat(-1 * localRotation.z));
tempObjectSb.AppendLine();
// Adds the local scale of this object
Vector3 localScale = gameObj.transform.localScale;
tempObjectSb.AppendFormat("\t\t\tP: \"Lcl Scaling\", \"Lcl Scaling\", \"\", \"A\",{0},{1},{2}", FE.FBXFormat(localScale.x), FE.FBXFormat(localScale.y), FE.FBXFormat(localScale.z));
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t\tP: \"currentUVSet\", \"KString\", \"\", \"U\", \"map1\"");
tempObjectSb.AppendLine("\t\t}");
tempObjectSb.AppendLine("\t\tShading: T");
tempObjectSb.AppendLine("\t\tCulling: \"CullingOff\"");
tempObjectSb.AppendLine("\t}");
// Adds in geometry if it exists, if it it does not exist, this is a empty gameObject file and skips over creating mesh links and geometry
if(meshToExport != null)
{
Mesh mesh = meshToExport;
// =================================
// General Geometry Info
// =================================
// Generate the geometry information for the mesh created
tempObjectSb.AppendLine("\tGeometry: " + geometryId + ", \"Geometry::\", \"Mesh\" {");
// ===== WRITE THE VERTICIES =====
Vector3[] verticies = mesh.vertices;
int vertCount = mesh.vertexCount * 3; // <= because the list of points is just a list of comma seperated values, we need to multiply by three
tempObjectSb.AppendLine("\t\tVertices: *" + vertCount + " {");
tempObjectSb.Append("\t\t\ta: ");
for(int i = 0; i < verticies.Length; i++)
{
if(i > 0)
tempObjectSb.Append(",");
// Points in the verticies. We also reverse the x value because Unity has a reverse X coordinate
tempObjectSb.AppendFormat("{0},{1},{2}", FE.FBXFormat(verticies[i].x * -1), FE.FBXFormat(verticies[i].y), FE.FBXFormat(verticies[i].z));
}
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t} ");
// ======= WRITE THE TRIANGLES ========
int triangleCount = mesh.triangles.Length;
int[] triangles = mesh.triangles;
tempObjectSb.AppendLine("\t\tPolygonVertexIndex: *" + triangleCount + " {");
// Write triangle indexes
tempObjectSb.Append("\t\t\ta: ");
for(int i = 0; i < triangleCount; i += 3)
{
if(i > 0)
tempObjectSb.Append(",");
// To get the correct normals, must rewind the triangles since we flipped the x direction
tempObjectSb.AppendFormat("{0},{1},{2}",
triangles[i],
triangles[i + 2],
(triangles[i + 1] * -1) - 1); // <= Tells the poly is ended
}
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t} ");
tempObjectSb.AppendLine("\t\tGeometryVersion: 124");
tempObjectSb.AppendLine("\t\tLayerElementNormal: 0 {");
tempObjectSb.AppendLine("\t\t\tVersion: 101");
tempObjectSb.AppendLine("\t\t\tName: \"\"");
tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"Direct\"");
// ===== WRITE THE NORMALS ==========
Vector3[] normals = mesh.normals;
tempObjectSb.AppendLine("\t\t\tNormals: *" + (triangleCount * 3) + " {");
tempObjectSb.Append("\t\t\t\ta: ");
for(int i = 0; i < triangleCount; i += 3)
{
if(i > 0)
tempObjectSb.Append(",");
// To get the correct normals, must rewind the normal triangles like the triangles above since x was flipped
Vector3 newNormal = normals[triangles[i]];
tempObjectSb.AppendFormat("{0},{1},{2},",
FE.FBXFormat(newNormal.x * -1), // Switch normal as is tradition
FE.FBXFormat(newNormal.y),
FE.FBXFormat(newNormal.z));
newNormal = normals[triangles[i + 2]];
tempObjectSb.AppendFormat("{0},{1},{2},",
FE.FBXFormat(newNormal.x * -1), // Switch normal as is tradition
FE.FBXFormat(newNormal.y),
FE.FBXFormat(newNormal.z));
newNormal = normals[triangles[i + 1]];
tempObjectSb.AppendFormat("{0},{1},{2}",
FE.FBXFormat(newNormal.x * -1), // Switch normal as is tradition
FE.FBXFormat(newNormal.y),
FE.FBXFormat(newNormal.z));
}
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t\t}");
tempObjectSb.AppendLine("\t\t}");
// ===== WRITE THE COLORS =====
bool containsColors = mesh.colors.Length == verticies.Length;
if(containsColors)
{
Color[] colors = mesh.colors;
Dictionary<Color, int> colorTable = new Dictionary<Color, int>(); // reducing amount of data by only keeping unique colors.
int idx = 0;
// build index table of all the different colors present in the mesh
for (int i = 0; i < colors.Length; i++)
{
if (!colorTable.ContainsKey(colors[i]))
{
colorTable[colors[i]] = idx;
idx++;
}
}
tempObjectSb.AppendLine("\t\tLayerElementColor: 0 {");
tempObjectSb.AppendLine("\t\t\tVersion: 101");
tempObjectSb.AppendLine("\t\t\tName: \"Col\"");
tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
tempObjectSb.AppendLine("\t\t\tColors: *" + colorTable.Count * 4 + " {");
tempObjectSb.Append("\t\t\t\ta: ");
bool first = true;
foreach (KeyValuePair<Color, int> color in colorTable)
{
if (!first)
tempObjectSb.Append(",");
tempObjectSb.AppendFormat("{0},{1},{2},{3}", FE.FBXFormat(color.Key.r), FE.FBXFormat(color.Key.g), FE.FBXFormat(color.Key.b), FE.FBXFormat(color.Key.a));
first = false;
}
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t\t}");
// Color index
tempObjectSb.AppendLine("\t\t\tColorIndex: *" + triangles.Length + " {");
tempObjectSb.Append("\t\t\t\ta: ");
for (int i = 0; i < triangles.Length; i += 3)
{
if (i > 0)
tempObjectSb.Append(",");
// Triangles need to be fliped for the x flip
int index1 = triangles[i];
int index2 = triangles[i + 2];
int index3 = triangles[i + 1];
// Find the color index related to that vertice index
index1 = colorTable[colors[index1]];
index2 = colorTable[colors[index2]];
index3 = colorTable[colors[index3]];
tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
}
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t\t}");
tempObjectSb.AppendLine("\t\t}");
}
// ================ UV CREATION =========================
// -- UV 1 Creation
int uvLength = mesh.uv.Length;
Vector2[] uvs = mesh.uv;
tempObjectSb.AppendLine("\t\tLayerElementUV: 0 {"); // the Zero here is for the first UV map
tempObjectSb.AppendLine("\t\t\tVersion: 101");
tempObjectSb.AppendLine("\t\t\tName: \"UVSet0\"");
tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
tempObjectSb.AppendLine("\t\t\tUV: *" + uvLength * 2 + " {");
tempObjectSb.Append("\t\t\t\ta: ");
for(int i = 0; i < uvLength; i++)
{
if(i > 0)
tempObjectSb.Append(",");
tempObjectSb.AppendFormat("{0},{1}", FE.FBXFormat(uvs[i].x), FE.FBXFormat(uvs[i].y));
}
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t\t}");
// UV tile index coords
tempObjectSb.AppendLine("\t\t\tUVIndex: *" + triangleCount +" {");
tempObjectSb.Append("\t\t\t\ta: ");
for(int i = 0; i < triangleCount; i += 3)
{
if(i > 0)
tempObjectSb.Append(",");
// Triangles need to be fliped for the x flip
int index1 = triangles[i];
int index2 = triangles[i + 2];
int index3 = triangles[i + 1];
tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
}
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t\t}");
tempObjectSb.AppendLine("\t\t}");
// -- UV 2 Creation
if (mesh.uv2 != null && mesh.uv2.Length != 0)
{
uvLength = mesh.uv2.Length;
uvs = mesh.uv2;
tempObjectSb.AppendLine("\t\tLayerElementUV: 1 {"); // the Zero here is for the first UV map
tempObjectSb.AppendLine("\t\t\tVersion: 101");
tempObjectSb.AppendLine("\t\t\tName: \"UVSet1\"");
tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygonVertex\"");
tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
tempObjectSb.AppendLine("\t\t\tUV: *" + uvLength * 2 + " {");
tempObjectSb.Append("\t\t\t\ta: ");
for (int i = 0; i < uvLength; i++)
{
if (i > 0)
tempObjectSb.Append(",");
tempObjectSb.AppendFormat("{0},{1}", FE.FBXFormat(uvs[i].x), FE.FBXFormat(uvs[i].y));
}
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t\t}");
// UV tile index coords
tempObjectSb.AppendLine("\t\t\tUVIndex: *" + triangleCount + " {");
tempObjectSb.Append("\t\t\t\ta: ");
for (int i = 0; i < triangleCount; i += 3)
{
if (i > 0)
tempObjectSb.Append(",");
// Triangles need to be fliped for the x flip
int index1 = triangles[i];
int index2 = triangles[i + 2];
int index3 = triangles[i + 1];
tempObjectSb.AppendFormat("{0},{1},{2}", index1, index2, index3);
}
tempObjectSb.AppendLine();
tempObjectSb.AppendLine("\t\t\t}");
tempObjectSb.AppendLine("\t\t}");
}
// TODO: Add more UV saving if FBX supports it
// -- Smoothing
// TODO: Smoothing doesn't seem to do anything when importing. This maybe should be added. -KBH
// ============ MATERIALS =============
tempObjectSb.AppendLine("\t\tLayerElementMaterial: 0 {");
tempObjectSb.AppendLine("\t\t\tVersion: 101");
tempObjectSb.AppendLine("\t\t\tName: \"\"");
tempObjectSb.AppendLine("\t\t\tMappingInformationType: \"ByPolygon\"");
tempObjectSb.AppendLine("\t\t\tReferenceInformationType: \"IndexToDirect\"");
int totalFaceCount = 0;
// So by polygon means that we need 1/3rd of how many indicies we wrote.
int numberOfSubmeshes = mesh.subMeshCount;
StringBuilder submeshesSb = new StringBuilder();
// For just one submesh, we set them all to zero
if(numberOfSubmeshes == 1)
{
int numFaces = triangles.Length / 3;
for(int i = 0; i < numFaces; i++)
{
submeshesSb.Append("0,");
totalFaceCount++;
}
}
else
{
List<int[]> allSubmeshes = new List<int[]>();
// Load all submeshes into a space
for(int i = 0; i < numberOfSubmeshes; i++)
allSubmeshes.Add(mesh.GetIndices(i));
// TODO: Optimize this search pattern
for(int i = 0; i < triangles.Length; i += 3)
{
for(int subMeshIndex = 0; subMeshIndex < allSubmeshes.Count; subMeshIndex++)
{
bool breaker = false;
for(int n = 0; n < allSubmeshes[subMeshIndex].Length; n += 3)
{
if(triangles[i] == allSubmeshes[subMeshIndex][n]
&& triangles[i + 1] == allSubmeshes[subMeshIndex][n + 1]
&& triangles[i + 2] == allSubmeshes[subMeshIndex][n + 2])
{
submeshesSb.Append(subMeshIndex.ToString());
submeshesSb.Append(",");
totalFaceCount++;
break;
}
if(breaker)
break;
}
}
}
Debug.Log(submeshesSb[submeshesSb.Length - 1]);
if(submeshesSb[submeshesSb.Length - 1] == ',')
submeshesSb.Remove(submeshesSb.Length - 1, 1);
}
tempObjectSb.AppendLine("\t\t\tMaterials: *" + totalFaceCount + " {");
tempObjectSb.Append("\t\t\t\ta: ");
tempObjectSb.AppendLine(submeshesSb.ToString());
tempObjectSb.AppendLine("\t\t\t} ");
tempObjectSb.AppendLine("\t\t}");
// ============= INFORMS WHAT TYPE OF LATER ELEMENTS ARE IN THIS GEOMETRY =================
tempObjectSb.AppendLine("\t\tLayer: 0 {");
tempObjectSb.AppendLine("\t\t\tVersion: 100");
tempObjectSb.AppendLine("\t\t\tLayerElement: {");
tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementNormal\"");
tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
tempObjectSb.AppendLine("\t\t\t}");
tempObjectSb.AppendLine("\t\t\tLayerElement: {");
tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementMaterial\"");
tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
tempObjectSb.AppendLine("\t\t\t}");
tempObjectSb.AppendLine("\t\t\tLayerElement: {");
tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementTexture\"");
tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
tempObjectSb.AppendLine("\t\t\t}");
if(containsColors)
{
tempObjectSb.AppendLine("\t\t\tLayerElement: {");
tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementColor\"");
tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
tempObjectSb.AppendLine("\t\t\t}");
}
tempObjectSb.AppendLine("\t\t\tLayerElement: {");
tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementUV\"");
tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 0");
tempObjectSb.AppendLine("\t\t\t}");
// inserts UV2s into the file export
if (mesh.uv2 != null && mesh.uv2.Length != 0)
{
tempObjectSb.AppendLine("\t\t}");
tempObjectSb.AppendLine("\t\tLayer: 1 {");
tempObjectSb.AppendLine("\t\t\tVersion: 100");
tempObjectSb.AppendLine("\t\t\tLayerElement: {");
tempObjectSb.AppendLine("\t\t\t\tType: \"LayerElementUV\"");
tempObjectSb.AppendLine("\t\t\t\tTypedIndex: 1");
tempObjectSb.AppendLine("\t\t\t}");
}
tempObjectSb.AppendLine("\t\t}");
tempObjectSb.AppendLine("\t}");
// Add the connection for the model to the geometry so it is attached the right mesh
tempConnectionsSb.AppendLine("\t;Geometry::, Model::" + mesh.name);
tempConnectionsSb.AppendLine("\tC: \"OO\"," + geometryId + "," + modelId);
tempConnectionsSb.AppendLine();
// Add the connection of all the materials in order of submesh
MeshRenderer meshRenderer = gameObj.GetComponent<MeshRenderer>();
if(meshRenderer != null)
{
Material[] allMaterialsInThisMesh = meshRenderer.sharedMaterials;
for(int i = 0; i < allMaterialsInThisMesh.Length; i++)
{
Material mat = allMaterialsInThisMesh[i];
if(mat == null)
{
Debug.LogError("ERROR: the game object " + gameObj.name + " has an empty material on it. This will export problematic files. Please fix and reexport");
continue;
}
int referenceId = Mathf.Abs(mat.GetInstanceID());
tempConnectionsSb.AppendLine("\t;Material::" + mat.name + ", Model::" + mesh.name);
tempConnectionsSb.AppendLine("\tC: \"OO\"," + referenceId + "," + modelId);
tempConnectionsSb.AppendLine();
}
}
}
// Recursively add all the other objects to the string that has been built.
for(int i = 0; i < gameObj.transform.childCount; i++)
{
GameObject childObject = gameObj.transform.GetChild(i).gameObject;
FBXUnityMeshGetter.GetMeshToString(childObject, materials, ref tempObjectSb, ref tempConnectionsSb, gameObj, modelId);
}
objects.Append(tempObjectSb.ToString());
connections.Append(tempConnectionsSb.ToString());
return modelId;
}
}
}Editor is loading...
Leave a Comment