Mesh GetOrCreateInstance(FbxMesh fbxMesh) { Mesh mesh; if (m_meshInstances.TryGetValue(fbxMesh, out mesh)) { return(mesh); } // create the unity mesh vertices var nVertices = fbxMesh.GetControlPointsCount(); var unityVertices = new Vector3[nVertices]; for (int i = 0; i < nVertices; ++i) { unityVertices[i] = V3(fbxMesh.GetControlPointAt(i)); } // create the unity mesh triangles. TODO: maybe handle non-triangular faces more intelligently var nPoly = fbxMesh.GetPolygonCount(); var unityTriangles = new List <int>(); for (int polyIndex = 0; polyIndex < nPoly; ++polyIndex) { var polySize = fbxMesh.GetPolygonSize(polyIndex); if (polySize < 3) { // ignore lines and points continue; } // Add the first triangle unityTriangles.Add(fbxMesh.GetPolygonVertex(polyIndex, 0)); unityTriangles.Add(fbxMesh.GetPolygonVertex(polyIndex, 1)); unityTriangles.Add(fbxMesh.GetPolygonVertex(polyIndex, 2)); // If there's more triangles, assume they're a fan around the first vertex. // Not necessarily true, but them's the breaks. for (int i = 3; i < polySize; ++i) { unityTriangles.Add(fbxMesh.GetPolygonVertex(polyIndex, 0)); unityTriangles.Add(fbxMesh.GetPolygonVertex(polyIndex, i - 1)); unityTriangles.Add(fbxMesh.GetPolygonVertex(polyIndex, i)); } } mesh = new Mesh { name = fbxMesh.GetName(), vertices = unityVertices, triangles = unityTriangles.ToArray() }; mesh.RecalculateNormals(); m_meshInstances[fbxMesh] = mesh; return(mesh); }
/// <summary> /// Process mesh data and setup MeshFilter component /// </summary> private void ProcessMesh(FbxNode fbxNode, GameObject unityGo) { FbxMesh fbxMesh = fbxNode.GetMesh(); if (fbxMesh == null) { return; } var unityMesh = new Mesh(); // create mesh var unityVertices = new List <Vector3> (); var unityTriangleIndices = new List <int> (); // transfer vertices for (int i = 0; i < fbxMesh.GetControlPointsCount(); ++i) { FbxVector4 fbxVector4 = fbxMesh.GetControlPointAt(i); Debug.Assert(fbxVector4.X <= float.MaxValue && fbxVector4.X >= float.MinValue); Debug.Assert(fbxVector4.Y <= float.MaxValue && fbxVector4.Y >= float.MinValue); Debug.Assert(fbxVector4.Z <= float.MaxValue && fbxVector4.Z >= float.MinValue); unityVertices.Add(new Vector3((float)fbxVector4.X, (float)fbxVector4.Y, (float)fbxVector4.Z)); } // transfer triangles for (int polyIndex = 0; polyIndex < fbxMesh.GetPolygonCount(); ++polyIndex) { int polySize = fbxMesh.GetPolygonSize(polyIndex); // only support triangles Debug.Assert(polySize == 3); for (int polyVertexIndex = 0; polyVertexIndex < polySize; ++polyVertexIndex) { int vertexIndex = fbxMesh.GetPolygonVertex(polyIndex, polyVertexIndex); unityTriangleIndices.Add(vertexIndex); } } unityMesh.vertices = unityVertices.ToArray(); // TODO: // - support Mesh.SetTriangles - multiple materials per mesh // - support Mesh.SetIndices - other topologies e.g. quads unityMesh.triangles = unityTriangleIndices.ToArray(); unityMesh.RecalculateNormals(); var unityMeshFilter = unityGo.AddComponent <MeshFilter> (); unityMeshFilter.sharedMesh = unityMesh; var unityRenderer = unityGo.AddComponent <MeshRenderer> (); { // Assign the default material (hack!) var unityPrimitive = GameObject.CreatePrimitive(PrimitiveType.Quad); var unityMat = unityPrimitive.GetComponent <MeshRenderer> ().sharedMaterial; unityRenderer.sharedMaterial = unityMat; UnityEngine.Object.DestroyImmediate(unityPrimitive); } }
/// <summary> /// Process UV data and configure the Mesh's UV attributes /// </summary> private void ProcessUVs(FbxMesh fbxMesh, Mesh unityMesh, int maxUVs = 4) { // Import UV sets (maximum defined by maxUVs) int uvsetIndex = 0; // First just try importing diffuse UVs from separate layers // (Maya exports that way) FbxLayerElementUV fbxFirstUVSet = null; FbxLayer fbxFirstUVLayer = null; // NOTE: assuming triangles int polygonIndexCount = fbxMesh.GetPolygonVertexCount(); int vertexCount = fbxMesh.GetControlPointsCount(); int [] polygonVertexIndices = new int [polygonIndexCount]; int j = 0; for (int polyIndex = 0; polyIndex < fbxMesh.GetPolygonCount(); ++polyIndex) { for (int positionInPolygon = 0; positionInPolygon < fbxMesh.GetPolygonSize(polyIndex); ++positionInPolygon) { polygonVertexIndices [j++] = fbxMesh.GetPolygonVertex(polyIndex, positionInPolygon); } } for (int i = 0; i < fbxMesh.GetLayerCount(); i++) { FbxLayer fbxLayer = fbxMesh.GetLayer(i); if (fbxLayer == null) { continue; } FbxLayerElementUV fbxUVSet = fbxLayer.GetUVs(); if (fbxUVSet == null) { continue; } if (fbxFirstUVSet != null) { fbxFirstUVSet = fbxUVSet; fbxFirstUVLayer = fbxLayer; } switch (uvsetIndex) { case 0: unityMesh.uv = ProcessUVSet(fbxUVSet, polygonVertexIndices, vertexCount); break; case 1: unityMesh.uv2 = ProcessUVSet(fbxUVSet, polygonVertexIndices, vertexCount); break; case 2: unityMesh.uv3 = ProcessUVSet(fbxUVSet, polygonVertexIndices, vertexCount); break; case 3: unityMesh.uv4 = ProcessUVSet(fbxUVSet, polygonVertexIndices, vertexCount); break; } uvsetIndex++; if (uvsetIndex == maxUVs) { break; } } // If we have received one UV set, check whether the same layer contains an emissive UV set // that is different from diffuse UV set. // 3dsmax FBX exporters doesn't export UV sets as different layers, instead for lightmapping usually // a material is set up to have lightmap (2nd UV set) as self-illumination slot, and main texture // (1st UV set) as diffuse slot. if (uvsetIndex == 1 && fbxFirstUVSet != null) { FbxLayerElementUV fbxSecondaryUVSet = null; // TODO: check if we've already passed eTextureEmissive layer for (int i = (int)FbxLayerElement.EType.eTextureEmissive; i < (int)FbxLayerElement.EType.eTypeCount; i++) { fbxSecondaryUVSet = fbxFirstUVLayer.GetUVs((FbxLayerElement.EType)i); if (fbxSecondaryUVSet != null) { break; } if (fbxSecondaryUVSet != null) { unityMesh.uv2 = ProcessUVSet(fbxSecondaryUVSet, polygonVertexIndices, vertexCount); uvsetIndex++; } } } }