using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; namespace UTJ.FbxExporter { public class FbxExporter { public FbxExporter(FbxExporter.ExportOptions opt) { this.m_opt = opt; } ~FbxExporter() { this.Release(); } public void Release() { FbxExporter.fbxeReleaseContext(this.m_ctx); this.m_ctx = FbxExporter.Context.Null; } public bool CreateScene(string name) { this.Release(); if (!this.m_ctx) { this.m_ctx = FbxExporter.fbxeCreateContext(ref this.m_opt); } this.m_nodes = new Dictionary(); return FbxExporter.fbxeCreateScene(this.m_ctx, name); } public void AddNode(GameObject go) { if (go) { this.FindOrCreateNodeTree(go.GetComponent(), new Action(this.ProcessNode)); } } public bool WriteAsync(string path, FbxExporter.Format format) { return FbxExporter.fbxeWriteAsync(this.m_ctx, path, format); } public bool IsFinished() { return FbxExporter.fbxeIsFinished(this.m_ctx); } private void ProcessNode(Transform trans, FbxExporter.Node node) { MeshRenderer component = trans.GetComponent(); SkinnedMeshRenderer component2 = trans.GetComponent(); Terrain component3 = trans.GetComponent(); if (component3) { this.AddTerrain(node, component3); } else if (component2) { this.AddSkinnedMesh(node, component2); } else if (component) { this.AddMesh(node, component); } } private FbxExporter.Node FindOrCreateNodeTree(Transform trans, Action act) { if (!trans) { return FbxExporter.Node.Null; } if (this.m_nodes.ContainsKey(trans)) { return this.m_nodes[trans]; } FbxExporter.Node parent = trans.parent ? this.FindOrCreateNodeTree(trans.parent, act) : FbxExporter.fbxeGetRootNode(this.m_ctx); FbxExporter.Node node = FbxExporter.fbxeCreateNode(this.m_ctx, parent, trans.name); if (this.m_opt.transform) { FbxExporter.fbxeSetTRS(this.m_ctx, node, trans.localPosition, trans.localRotation, trans.localScale); } this.m_nodes.Add(trans, node); if (act != null) { act(trans, node); } return node; } private bool AddMesh(FbxExporter.Node node, Mesh mesh) { if (!mesh || mesh.vertexCount == 0) { return false; } if (!mesh.isReadable) { Debug.LogWarning("Mesh " + mesh.name + " is not readable and be ignored."); return false; } FbxExporter.Topology topology = FbxExporter.Topology.Triangles; PinnedArray pinnedArray = new PinnedArray(mesh.triangles, false); PinnedArray pinnedArray2 = new PinnedArray(mesh.vertices, false); PinnedArray pinnedArray3 = new PinnedArray(mesh.normals, false); if (pinnedArray3.Length == 0) { pinnedArray3 = null; } PinnedArray pinnedArray4 = new PinnedArray(mesh.tangents, false); if (pinnedArray4.Length == 0) { pinnedArray4 = null; } PinnedArray pinnedArray5 = new PinnedArray(mesh.uv, false); if (pinnedArray5.Length == 0) { pinnedArray5 = null; } PinnedArray pinnedArray6 = new PinnedArray(mesh.colors, false); if (pinnedArray6.Length == 0) { pinnedArray6 = null; } FbxExporter.fbxeAddMesh(this.m_ctx, node, pinnedArray2.Length, pinnedArray2, pinnedArray3, pinnedArray4, pinnedArray5, pinnedArray6); FbxExporter.fbxeAddMeshSubmesh(this.m_ctx, node, topology, pinnedArray.Length, pinnedArray, -1); int blendShapeCount = mesh.blendShapeCount; if (blendShapeCount > 0) { PinnedArray v = new PinnedArray(mesh.vertexCount); PinnedArray v2 = new PinnedArray(mesh.vertexCount); PinnedArray v3 = new PinnedArray(mesh.vertexCount); for (int i = 0; i < blendShapeCount; i++) { string blendShapeName = mesh.GetBlendShapeName(i); int blendShapeFrameCount = mesh.GetBlendShapeFrameCount(i); for (int j = 0; j < blendShapeFrameCount; j++) { float blendShapeFrameWeight = mesh.GetBlendShapeFrameWeight(i, j); mesh.GetBlendShapeFrameVertices(i, j, v, v2, v3); FbxExporter.fbxeAddMeshBlendShape(this.m_ctx, node, blendShapeName, blendShapeFrameWeight, v, v2, v3); } } } return true; } private bool AddMesh(FbxExporter.Node node, MeshRenderer mr) { MeshFilter component = mr.gameObject.GetComponent(); return component && this.AddMesh(node, component.sharedMesh); } private bool AddSkinnedMesh(FbxExporter.Node node, SkinnedMeshRenderer smr) { Mesh sharedMesh = smr.sharedMesh; if (!this.AddMesh(node, sharedMesh)) { return false; } Transform[] bones = smr.bones; PinnedArray pinnedArray = new PinnedArray(bones.Length); for (int i = 0; i < bones.Length; i++) { pinnedArray[i] = this.FindOrCreateNodeTree(bones[i], new Action(this.ProcessNode)); } PinnedArray v = new PinnedArray(sharedMesh.boneWeights, false); PinnedArray v2 = new PinnedArray(sharedMesh.bindposes, false); FbxExporter.fbxeAddMeshSkin(this.m_ctx, node, v, pinnedArray.Length, pinnedArray, v2); return true; } private bool AddTerrain(FbxExporter.Node node, Terrain terrain) { TerrainData terrainData = terrain.terrainData; int heightmapWidth = terrainData.heightmapWidth; int heightmapHeight = terrainData.heightmapHeight; float[,] heights = terrainData.GetHeights(0, 0, heightmapWidth, heightmapHeight); int size = heightmapWidth * heightmapHeight; int size2 = (heightmapWidth - 1) * (heightmapHeight - 1) * 2 * 3; PinnedArray pinnedArray = new PinnedArray(size); PinnedArray v = new PinnedArray(size); PinnedArray v2 = new PinnedArray(size); PinnedArray pinnedArray2 = new PinnedArray(size2); FbxExporter.fbxeGenerateTerrainMesh(heights, heightmapWidth, heightmapHeight, terrainData.size, pinnedArray, v, v2, pinnedArray2); FbxExporter.Topology topology = FbxExporter.Topology.Triangles; FbxExporter.fbxeAddMesh(this.m_ctx, node, pinnedArray.Length, pinnedArray, v, IntPtr.Zero, v2, IntPtr.Zero); FbxExporter.fbxeAddMeshSubmesh(this.m_ctx, node, topology, pinnedArray2.Length, pinnedArray2, -1); return true; } [DllImport("FbxExporterCore")] private static extern FbxExporter.Context fbxeCreateContext(ref FbxExporter.ExportOptions opt); [DllImport("FbxExporterCore")] private static extern void fbxeReleaseContext(FbxExporter.Context ctx); [DllImport("FbxExporterCore")] private static extern bool fbxeCreateScene(FbxExporter.Context ctx, string name); [DllImport("FbxExporterCore")] private static extern bool fbxeWriteAsync(FbxExporter.Context ctx, string path, FbxExporter.Format format); [DllImport("FbxExporterCore")] private static extern bool fbxeIsFinished(FbxExporter.Context ctx); [DllImport("FbxExporterCore")] private static extern FbxExporter.Node fbxeGetRootNode(FbxExporter.Context ctx); [DllImport("FbxExporterCore")] private static extern FbxExporter.Node fbxeFindNodeByName(FbxExporter.Context ctx, string name); [DllImport("FbxExporterCore")] private static extern FbxExporter.Node fbxeCreateNode(FbxExporter.Context ctx, FbxExporter.Node parent, string name); [DllImport("FbxExporterCore")] private static extern void fbxeSetTRS(FbxExporter.Context ctx, FbxExporter.Node node, Vector3 t, Quaternion r, Vector3 s); [DllImport("FbxExporterCore")] private static extern void fbxeAddMesh(FbxExporter.Context ctx, FbxExporter.Node node, int num_vertices, IntPtr points, IntPtr normals, IntPtr tangents, IntPtr uv, IntPtr colors); [DllImport("FbxExporterCore")] private static extern void fbxeAddMeshSubmesh(FbxExporter.Context ctx, FbxExporter.Node node, FbxExporter.Topology topology, int num_indices, IntPtr indices, int material); [DllImport("FbxExporterCore")] private static extern void fbxeAddMeshSkin(FbxExporter.Context ctx, FbxExporter.Node node, IntPtr weights, int num_bones, IntPtr bones, IntPtr bindposes); [DllImport("FbxExporterCore")] private static extern void fbxeAddMeshBlendShape(FbxExporter.Context ctx, FbxExporter.Node node, string name, float weight, IntPtr deltaPoints, IntPtr deltaNormals, IntPtr deltaTangents); [DllImport("FbxExporterCore")] private static extern void fbxeGenerateTerrainMesh(float[,] heightmap, int width, int height, Vector3 size, IntPtr dst_vertices, IntPtr dst_normals, IntPtr dst_uv, IntPtr dst_indices); private FbxExporter.ExportOptions m_opt = FbxExporter.ExportOptions.defaultValue; private FbxExporter.Context m_ctx; private Dictionary m_nodes; public struct Context { public static FbxExporter.Context Null { get { FbxExporter.Context result; result.ptr = IntPtr.Zero; return result; } } public static implicit operator bool(FbxExporter.Context v) { return v.ptr != IntPtr.Zero; } public IntPtr ptr; } public struct Node { public static FbxExporter.Node Null { get { FbxExporter.Node result; result.ptr = IntPtr.Zero; return result; } } public static implicit operator bool(FbxExporter.Node v) { return v.ptr != IntPtr.Zero; } public IntPtr ptr; } public enum Format { FbxBinary, FbxAscii, FbxEncrypted, Obj } public enum SystemUnit { Millimeter, Centimeter, Decimeter, Meter, Kilometer } public enum Topology { Points, Lines, Triangles, Quads } public struct ExportOptions { public static FbxExporter.ExportOptions defaultValue { get { return new FbxExporter.ExportOptions { flip_handedness = true, flip_faces = true, quadify = true, quadify_full_search = false, quadify_threshold_angle = 20f, scale_factor = 1f, system_unit = FbxExporter.SystemUnit.Meter, transform = true }; } } public bool flip_handedness; public bool flip_faces; public bool quadify; public bool quadify_full_search; public float quadify_threshold_angle; public float scale_factor; public FbxExporter.SystemUnit system_unit; public bool transform; } } }