Search Unity

Extract textures and materials from FBX at run-time

Discussion in 'Scripting' started by Salocin808, May 24, 2018.

  1. Salocin808

    Salocin808

    Joined:
    Mar 29, 2018
    Posts:
    13
    Hey,

    I am currently building a cloud application where users should be able to upload FBX files. I want to convert the FBX file as a Gameobject and export it as Assetbundle so I can save it in my DB (clients will update from the backend via Assetbundles). The whole process has to be automated and I will use Unity via batch on a windows server so I won't be able to use the editor.

    I am already able to extract materials from the FBX file during run-time but I am having problems extracting the textures.

    Does anybody have experience in that area or any idea if it's even possible?

    this is how material extraction already works during run-time:

    Code (CSharp):
    1. public class ExtractMaterials : MonoBehaviour {
    2.  
    3.     // Use this for initialization
    4.     void Start () {
    5.    
    6.         var materialsPath = "Assets/Materials";
    7.         Directory.CreateDirectory(materialsPath);
    8.      
    9.         var texturesPath = "Assets/Textures";
    10.         Directory.CreateDirectory(texturesPath);
    11.  
    12.         var assetsToReload = new HashSet<string>();
    13.         foreach (string assetPath in AssetDatabase.GetAllAssetPaths())
    14.         {
    15.             if (assetPath.ToLower().Contains(".fbx"))
    16.             {
    17.                 Debug.Log("Extracting for asset: " + assetPath);
    18.                 var importer = AssetImporter.GetAtPath(assetPath) as ModelImporter;
    19.  
    20.                 var materials = AssetDatabase.LoadAllAssetsAtPath(importer.assetPath).Where(x => x.GetType() == typeof(Material));
    21.                 foreach (var material in materials)
    22.                 {
    23.                     var newAssetPath = string.Join(Path.DirectorySeparatorChar.ToString(), new[] { materialsPath, material.name }) + ".mat";
    24.                     var error = AssetDatabase.ExtractAsset(material, newAssetPath);
    25.                     if (string.IsNullOrEmpty(error))
    26.                     {
    27.                         assetsToReload.Add(importer.assetPath);
    28.                     }
    29.                 }
    30.             }
    31.         }
    32.         foreach (var path in assetsToReload)
    33.         {
    34.             AssetDatabase.WriteImportSettingsIfDirty(path);
    35.             AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
    36.         }
    37.     }
    38.  
    39. }
    40.  
    Thanks for the help!
     
  2. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    Just a thought. As you already get the material, isn't the texture part of the material?
     
  3. Salocin808

    Salocin808

    Joined:
    Mar 29, 2018
    Posts:
    13
    Hey,

    no materials only store the reference to textures and not the textures per se. Thats why the model importer has both options (extract materials / extract textures).
     
  4. FMark92

    FMark92

    Joined:
    May 18, 2017
    Posts:
    1,243
  5. Salocin808

    Salocin808

    Joined:
    Mar 29, 2018
    Posts:
    13
  6. Deleted User

    Deleted User

    Guest

    Trying my luck, is there a solution to this yet? I am heading in the same direction and this would be great.
    Tnx
     
  7. Salocin808

    Salocin808

    Joined:
    Mar 29, 2018
    Posts:
    13
    Actually, when embedding media in export options (I used Maya) a .fbm folder is created with all the textures. I was simply able to move these textures from the .fbm folder to my Textures folder.
     
  8. hks_adohkd

    hks_adohkd

    Joined:
    Sep 7, 2018
    Posts:
    12
    Hi @Salocin808
    I have extracted the materials the same way you did.
    and also I have copied the textures to a Textures folder next to the materials folder.
    but the materials have no textures in them how I reassign to every material its collection of textures?
    is there an automated way?
     
  9. samuelmorais

    samuelmorais

    Joined:
    Aug 3, 2012
    Posts:
    62
    I know it is an old question, but for anyone searching for a solution, here is how I've solved it in order to add textures to the assetbundle.
    In summary, I have discovered that for the textures to be discovered during the importing and before building the assetbundle (which I do using the https://github.com/mitmadness/chuck / https://github.com/mitmadness/AssetBundleCompiler ) , the textures need to be extracted in OnPreprocessModel() method.


    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.IO;
    4. using System.Collections.Generic;
    5. using System.Linq;
    6. using UnityEditor.Animations;
    7. class MyAllPostprocessor : AssetPostprocessor
    8. {
    9.     void OnPreprocessModel()
    10.     {
    11.        
    12.         ModelImporter modelImporter = assetImporter as ModelImporter;
    13.         modelImporter.ExtractTextures("Assets/CopiedAssets/");
    14.         var materials = AssetDatabase.LoadAllAssetsAtPath(modelImporter.assetPath).ToList().Where(x => x.GetType() == typeof(Material));
    15.         foreach (var material in materials)
    16.         {
    17.             var newAssetPath = string.Join(Path.DirectorySeparatorChar.ToString(), new[] { "Assets/CopiedAssets/", material.name }) + ".mat";
    18.             var error = AssetDatabase.ExtractAsset(material, newAssetPath);
    19.         }
    20.     }
    21.  
    22.     static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    23.     {
    24.         var texturesPath = "Assets/CopiedAssets/";
    25.         Directory.CreateDirectory(texturesPath);
    26.         var materialsPath = "Assets/CopiedAssets/";
    27.         Directory.CreateDirectory(materialsPath);
    28.         var assetsToReload = new HashSet<string>();
    29.              
    30.         foreach (var assetPath in importedAssets)
    31.         {
    32.            
    33.             if (assetPath.ToLower().Contains(".fbx"))
    34.             {
    35.                
    36.                 var importer = AssetImporter.GetAtPath(assetPath) as ModelImporter;
    37.                 var materials = AssetDatabase.LoadAllAssetsAtPath(importer.assetPath).ToList().Where(x => x.GetType() == typeof(Material));
    38.                 foreach (var material in materials)
    39.                 {
    40.                     var newAssetPath = string.Join(Path.DirectorySeparatorChar.ToString(), new[] { materialsPath, material.name }) + ".mat";
    41.                     var error = AssetDatabase.ExtractAsset(material, newAssetPath);
    42.                    
    43.                     if (string.IsNullOrEmpty(error))
    44.                     {
    45.                         assetsToReload.Add(importer.assetPath);
    46.                     }              
    47.                 }
    48.                
    49.  
    50.             }
    51.         }
    52.     }
    53.  
    54.    
    55. }
     
  10. samuelmorais

    samuelmorais

    Joined:
    Aug 3, 2012
    Posts:
    62
    PS: although I copied the above example, I do not call the
    Code (CSharp):
    1. foreach (var path in assetsToReload) AssetDatabase.WriteImportSettingsIfDirty(path);
    part because it caused problems when running the script twice.