//Convert the model to use just one mesh, one material. This will alter the original imported FBX, so make sure you have a backup void ConvertModel(GameObject sourceModel) { //GameObject clone = (GameObject)Object.Instantiate(sourceModel); //clone.name = sourceModel.name; AvatarTextureCreatorData avData = sourceModel.GetComponentInChildren <AvatarTextureCreatorData>(); if (avData == null) { Debug.LogError(string.Format("Missing AvatarTextureCreatorData from {0}, can't continue...", sourceModel.name)); return; } //Vector3 pos = sourceModel.transform.position; //pos.x += 5f; //clone.transform.position = pos; #region Unused at this time ////////////////////////////////////SkinnedMeshRenderer origSkinnedMesh = sourceModel.GetComponentInChildren<SkinnedMeshRenderer>(); ////////////////////////////////////SkinnedMeshRenderer newSkinnedMesh = clone.GetComponentInChildren<SkinnedMeshRenderer>(); //////////////////////////////////////Debug.Log("1"); ////////////////////////////////////if(origSkinnedMesh == null) ////////////////////////////////////{ //////////////////////////////////// Debug.LogError(string.Format("Missing SkinnedMeshRenderer on original {0}, can't continue...", sourceModel.name)); //////////////////////////////////// return; ////////////////////////////////////} ////////////////////////////////////if(newSkinnedMesh == null) ////////////////////////////////////{ //////////////////////////////////// Debug.LogError(string.Format("Missing SkinnedMeshRenderer on clone {0}, can't continue...", clone.name)); //////////////////////////////////// return; ////////////////////////////////////} ////////////////////////////////////Debug.Log(string.Format("Orig: {0} Triangle Count: {1} UV Count: {2} Vert Count: {3} Sub Mesh Count: {4} Bone Weight Count: {5} Bind Pose Count: {6} ", sourceModel.name, origSkinnedMesh.sharedMesh.triangles.Length, //////////////////////////////////// origSkinnedMesh.sharedMesh.uv.Length, origSkinnedMesh.sharedMesh.vertexCount, origSkinnedMesh.sharedMesh.subMeshCount, origSkinnedMesh.sharedMesh.boneWeights.Length, //////////////////////////////////// origSkinnedMesh.sharedMesh.bindposes.Length)); ////////////////////////////////////Vector2[] backup = (Vector2[])origSkinnedMesh.sharedMesh.uv.Clone(); //////////////////////////////////////for (int i = 0; i < origSkinnedMesh.sharedMesh.uv.Length; i++) //////////////////////////////////////{ ////////////////////////////////////// Debug.Log("orig # " + i + " uv " + origSkinnedMesh.sharedMesh.uv[i]); //////////////////////////////////////} ////////////////////////////////////Mesh newMesh = newSkinnedMesh.sharedMesh; ////////////////////////////////////Vector3[] newMeshVerts = new Vector3[origSkinnedMesh.sharedMesh.vertexCount]; ////////////////////////////////////Vector2[] newMeshUVs = new Vector2[newMeshVerts.Length]; ////////////////////////////////////int[] newMeshTriangles = (int[])origSkinnedMesh.sharedMesh.triangles.Clone(); ////////////////////////////////////Matrix4x4[] newBindPoses = (Matrix4x4[])origSkinnedMesh.sharedMesh.bindposes.Clone(); ////////////////////////////////////BoneWeight[] newBoneWeights = (BoneWeight[])origSkinnedMesh.sharedMesh.boneWeights.Clone(); ////////////////////////////////////Color[] newColors = new Color[newMeshVerts.Length]; ////////////////////////////////////Vector3[] newNormals = (Vector3[])origSkinnedMesh.sharedMesh.normals.Clone(); //////////////////////////////////////Debug.Log("2"); //////////////////////////////////////Test to print out submesh and verts //////////////////////////////////////for (int i = 0; i < origSkinnedMesh.sharedMesh.subMeshCount; i++) //////////////////////////////////////{ ////////////////////////////////////// int[] triangleList = origSkinnedMesh.sharedMesh.GetTriangles(i); ////////////////////////////////////// Debug.Log(string.Format("======== MESH {0} ========", i)); ////////////////////////////////////// for (int j = 0; j < triangleList.Length; j++) ////////////////////////////////////// { ////////////////////////////////////// Debug.Log(string.Format("#{0} vert: {1}", j, triangleList[j])); ////////////////////////////////////// } //////////////////////////////////////} //////////////////////////////////////First clone orig mesh ////////////////////////////////////newMeshVerts = (Vector3[])origSkinnedMesh.sharedMesh.vertices.Clone(); ////////////////////////////////////for (int i = 0; i < newColors.Length; i++) ////////////////////////////////////{ //////////////////////////////////// newColors[i] = Color.white; ////////////////////////////////////} //////////////////////////////////////Debug.Log("3"); //////////////////////////////////////Now update UVs ////////////////////////////////////for (int i = 0; i < origSkinnedMesh.sharedMesh.subMeshCount; i++) ////////////////////////////////////{ //////////////////////////////////// int[] triangleList = origSkinnedMesh.sharedMesh.GetTriangles(i); //////////////////////////////////// for (int j = 0; j < triangleList.Length; j++) //////////////////////////////////// { //////////////////////////////////// newMeshUVs[triangleList[j]] = ConvertUVToTextureAtlasUV(origSkinnedMesh.sharedMesh.uv[triangleList[j]], avData.SourceMaterialOrder[i], avData, triangleList[j]); //////////////////////////////////// } ////////////////////////////////////} //////////////////////////////////////Debug.Log("4"); ////////////////////////////////////newMesh.Clear(); ////////////////////////////////////newMesh.bindposes = newBindPoses; ////////////////////////////////////newMesh.name = "CombinedMesh"; ////////////////////////////////////newMesh.subMeshCount = 0; ////////////////////////////////////newMesh.vertices = newMeshVerts; ////////////////////////////////////newMesh.boneWeights = newBoneWeights; ////////////////////////////////////newMesh.uv = newMeshUVs; ////////////////////////////////////newMesh.uv1 = newMeshUVs; ////////////////////////////////////newMesh.uv2 = newMeshUVs; ////////////////////////////////////newMesh.colors = newColors; ////////////////////////////////////newMesh.triangles = newMeshTriangles; ////////////////////////////////////newMesh.normals = newNormals; //////////////////////////////////////newMesh.SetTriangles(newMeshTriangles, 0); ////////////////////////////////////newMesh.RecalculateBounds(); //////////////////////////////////////Debug.Log("5"); ////////////////////////////////////newSkinnedMesh.sharedMaterial = null; ////////////////////////////////////newSkinnedMesh.sharedMaterials = new Material[1]; ////////////////////////////////////newSkinnedMesh.sharedMesh = newMesh; ////////////////////////////////////newSkinnedMesh.sharedMesh.Optimize(); //////////////////////////////////////newSkinnedMesh.sharedMesh.subMeshCount = 1; //////////////////////////////////////newSkinnedMesh.sharedMesh.SetTriangles(newSkinnedMesh.sharedMesh.triangles, 1); #endregion //Now generate the singular default texture, and then material Texture combinedTex = GenerateCombinedTexture(sourceModel, g_combinedTextureDir); Material combinedMat = GenerateCombinedMaterial(sourceModel, g_combinedMaterialDir, combinedTex); #region Unused at this time //////////////////////////////newSkinnedMesh.material = combinedMat; //////////////////////////////MakePrefab(clone, g_prefabDir); ////////////////////////////////Debug.Log("6"); ////////////////////////////////for (int i = 0; i < copiedMesh.vertexCount; i++) ////////////////////////////////{ //////////////////////////////// Debug.Log(string.Format("# {0} uv: {1} submeshcount: {2} materials: {3}", i, copiedMesh.uv[i], copiedMesh.subMeshCount, skinnedMesh.sharedMaterials.Length)); ////////////////////////////////} //////////////////////////////Debug.Log(string.Format("Clone: {0} Triangle Count: {1} UV Count: {2} Vert Count: {3} Sub Mesh Count: {4} Bone Weight count: {5} Bind Pose Count: {6}", clone.name, newSkinnedMesh.sharedMesh.triangles.Length, ////////////////////////////// newSkinnedMesh.sharedMesh.uv.Length, newSkinnedMesh.sharedMesh.vertexCount, newSkinnedMesh.sharedMesh.subMeshCount, newSkinnedMesh.sharedMesh.boneWeights.Length, ////////////////////////////// newSkinnedMesh.sharedMesh.bindposes.Length)); ////////////////////////////////for (int i = 0; i < newSkinnedMesh.sharedMesh.uv.Length; i++) ////////////////////////////////{ //////////////////////////////// Debug.Log("# " + i + " new uv " + newSkinnedMesh.sharedMesh.uv[i] + " orig " + backup[i]); ////////////////////////////////} //////////////////////////////newMeshVerts = null; //////////////////////////////newMeshUVs = null; //////////////////////////////newMeshTriangles = null; //////////////////////////////newBindPoses = null; //////////////////////////////newBoneWeights = null; //////////////////////////////newColors = null; //////////////////////////////newNormals = null; #endregion }
void AttemptMatchTextureToSlot(GameObject m_modelPrefab, Object texturesDirObj, bool overWriteRects) { //First find the AvatarTextureCreatorData AvatarTextureCreatorData avatarData = m_modelPrefab.GetComponentInChildren <AvatarTextureCreatorData>(); //If we can't find the component if (avatarData == null) { //Look for the avatar component EQBrowser.Avatar avatar = m_modelPrefab.GetComponentInChildren <EQBrowser.Avatar>(); //If avatar isn't null, then use it's gameobject to add it, otherwise eject with an error if (avatar != null) { avatarData = avatar.gameObject.AddComponent <EQBrowser.AvatarTextureCreatorData>(); } else { Debug.LogError(string.Format("Can't find an AvatarTextureCreatorData OR Avatar, not finishing on {0}", m_modelPrefab.name)); return; } } string texAssetFolderPath = AssetDatabase.GetAssetPath(texturesDirObj); string dataPath = Application.dataPath; string texFolderPath = dataPath.Substring(0, dataPath.Length - 6) + texAssetFolderPath; //Create the source textures slots avatarData.SourceTextures = new Texture2D[14]; if (avatarData.SourceTextureRects.Length != avatarData.SourceTextures.Length) { avatarData.SourceTextureRects = new Rect[avatarData.SourceTextures.Length]; } //string[] allMatFilePaths = Directory.GetFiles(matFolderPath, "*.mat", SearchOption.AllDirectories); //Get all the different texture file types to make this more future proof string[][] allTexFilePathsROUGH = new string[4][]; allTexFilePathsROUGH[0] = Directory.GetFiles(texFolderPath, "*.bmp", SearchOption.AllDirectories); allTexFilePathsROUGH[1] = Directory.GetFiles(texFolderPath, "*.tga", SearchOption.AllDirectories); allTexFilePathsROUGH[2] = Directory.GetFiles(texFolderPath, "*.psd", SearchOption.AllDirectories); allTexFilePathsROUGH[3] = Directory.GetFiles(texFolderPath, "*.png", SearchOption.AllDirectories); //Combine them all into one nice list for easier processing string[] allTexFilePaths = new string[allTexFilePathsROUGH[0].Length + allTexFilePathsROUGH[1].Length + allTexFilePathsROUGH[2].Length + allTexFilePathsROUGH[3].Length]; int index = 0; for (int i = 0; i < allTexFilePathsROUGH.Length; i++) { for (int j = 0; j < allTexFilePathsROUGH[i].Length; j++) { allTexFilePaths[index] = allTexFilePathsROUGH[i][j]; index++; } } if (allTexFilePaths.Length < 1) { Debug.LogError(string.Format("Can't find any textures! Aborting on model: {0}", m_modelPrefab.name)); return; } //Now go through and look for matches int count = 0; bool foundItem = true; System.Collections.Generic.List <string> missingItems = new System.Collections.Generic.List <string>(); string searchName = ""; int texCount = 0; for (int i = 0; i < avatarData.SourceTextures.Length; i++) { if (foundItem == false) { missingItems.Add(searchName); } //Hopefully get the correct file name searchName = ConvertTextureIndexToSearchString(i, allTexFilePaths[0]); //Now search through all the textures for that file to assign it to the slot for (texCount = 0; texCount < allTexFilePaths.Length; texCount++) { //We're going to set up the default rects for the uvs here too if (avatarData.SourceTextureRects[i] != null && overWriteRects) { avatarData.SourceTextureRects[i] = new Rect(0, 0, 1f, 1f); } string texName = allTexFilePaths[texCount].Substring(dataPath.Length - 6); if (searchName.ToLower() == Path.GetFileNameWithoutExtension(texName).ToLower()) { //We should have the correct texture, so assign it avatarData.SourceTextures[i] = (Texture2D)AssetDatabase.LoadMainAssetAtPath(texName); count++; foundItem = true; break; } } if (texCount >= allTexFilePaths.Length) { foundItem = false; } } Debug.Log(string.Format("Finished with model: {0} and found {1} textures. Missing the following:", m_modelPrefab.name, count)); for (int i = 0; i < missingItems.Count; i++) { Debug.Log(missingItems[i]); } }
//Convert the original uv for multiple submeshes and materials into one uv set using the single texture atlas Vector2 ConvertUVToTextureAtlasUV(Vector2 origUV, AvatarTextureCreatorData.TextureSlot materialSlot, AvatarTextureCreatorData avData, int index) { Vector2 newUV = origUV; //Since some of the UVs are outside 0-1 range, we're going to trim them to make it easier to convert int integer = (int)(origUV.x - 0.001f); float remainder = origUV.x - integer; newUV.x = remainder; if (materialSlot == AvatarTextureCreatorData.TextureSlot.Chest01) { Debug.Log("index " + index + " x orig UV " + origUV + " int " + integer + " remainder " + remainder); } integer = 1;//(int)(origUV.y - 0.001f); remainder = origUV.y - integer; newUV.y = remainder; if (materialSlot == AvatarTextureCreatorData.TextureSlot.Chest01) { Debug.Log("index " + index + " y orig UV " + origUV + " int " + integer + " remainder " + remainder); Debug.Log("NEW UV " + newUV); } //Now we want to convert that to the pixel position (warning, we might lose some accuracy here) int pixelX = Mathf.RoundToInt(newUV.x * avData.SourceTextures[(int)materialSlot].width); int pixelY = Mathf.RoundToInt(newUV.y * avData.SourceTextures[(int)materialSlot].height); //Now we can offset the pixelX & Y by the new bottom left position in the larger atlas //These are hardcoded based on the template texture atlas //TODO make this completely hardcoded free (perhaps figure out original texture size and new texture atlas size?) switch (materialSlot) { case AvatarTextureCreatorData.TextureSlot.Chest01: //newUV += new Vector2(0 / 256f, 128f / 256f); Debug.Log("chest 01 index " + index + " " + pixelX + " , " + pixelY + " source width " + avData.SourceTextures[(int)materialSlot].width + " source height " + avData.SourceTextures[(int)materialSlot].height); pixelX += 0; pixelY += 128; break; case AvatarTextureCreatorData.TextureSlot.Chest02: //newUV += new Vector2(64f / 256f, 240f / 256f); pixelX += 64; pixelY += 240; break; case AvatarTextureCreatorData.TextureSlot.ForeArm01: //newUV += new Vector2(64f / 256f, 176f / 256f); pixelX += 64; pixelY += 176; break; case AvatarTextureCreatorData.TextureSlot.Foot01: //newUV += new Vector2(192f / 256f, 160f / 256f); pixelX += 192; pixelY += 160; break; case AvatarTextureCreatorData.TextureSlot.Foot02: //newUV += new Vector2(0f / 256f, 64f / 256f); pixelX += 0; pixelY += 64; break; case AvatarTextureCreatorData.TextureSlot.Head01: //newUV += new Vector2(0f / 256f, 0f / 256f); pixelX += 0; pixelY += 0; break; case AvatarTextureCreatorData.TextureSlot.Head02: //newUV += new Vector2(192f / 256f, 64f / 256f); pixelX += 192; pixelY += 64; break; case AvatarTextureCreatorData.TextureSlot.Head03: //newUV += new Vector2(128f / 256f, 0f / 256f); pixelX += 128; pixelY += 0; break; case AvatarTextureCreatorData.TextureSlot.Hand01: //newUV += new Vector2(64f / 256f, 144f / 256f); pixelX += 64; pixelY += 144; break; case AvatarTextureCreatorData.TextureSlot.Hand02: //newUV += new Vector2(64f / 256f, 112f / 256f); pixelX += 64; pixelY += 112; break; case AvatarTextureCreatorData.TextureSlot.Leg01: //newUV += new Vector2(128f / 256f, 192f / 256f); pixelX += 128; pixelY += 192; break; case AvatarTextureCreatorData.TextureSlot.Leg02: //newUV += new Vector2(128f / 256f, 128f / 256f); pixelX += 128; pixelY += 128; break; case AvatarTextureCreatorData.TextureSlot.Leg03: //newUV += new Vector2(128f / 256f, 96f / 256f); pixelX += 128; pixelY += 96; break; case AvatarTextureCreatorData.TextureSlot.UpperArm01: //newUV += new Vector2(196f / 256f, 192f / 256f); pixelX += 196; pixelY += 192; break; } //Remember: bottom left corner as 0,0, top right is 1,1 //Now we can convert back to relative UV float newUV.x = (float)pixelX / 256f; newUV.y = (float)pixelY / 256f; return(newUV); }