Export from Unity

Mayalicious
polycounter lvl 6
Offline / Send Message
Mayalicious polycounter lvl 6
I am in the process of learning level design in Unity. I would like to know if i can export models from unity as .obj, or fbx files outside? I want to know this because if this is possible i can export the pre-made project models (angrybots for example) from unity to maya. Thanks.

Replies

  • Lamont
    Offline / Send Message
    Lamont polycounter lvl 9
    If the model data is in FBX format, then look in the Assets folder for the data.
  • SanderDL
    Offline / Send Message
    SanderDL polycounter lvl 7
  • renderhjs
    Offline / Send Message
    renderhjs polycounter
    SanderDL wrote: »

    I have been using this script in the past and its really awesome. All it ads is a export option under your [File] menu
    12828256125_750f1c1b9d_o.jpg

    For example in a recent project I create a road network inside unity with sub mesh tiles. A simple export allowed me to edit the whole road set as a single OBJ model in photoshop

    12828670204_8c613d62dd_o.jpg
    ( Prototyping: using automated UV's, I would never unwrap like that :D )
  • SanderDL
    Offline / Send Message
    SanderDL polycounter lvl 7
    Hey if Render says it's awesome, then it has got to be so. :)
  • smurfted
    Sorry. Bit of a noob to unity. I couldn't get the script to work?!

    I made a script in unity and copied the second script (from the wiki) into it. I saved the script into a folder in the assets folder called "Editor".

    Any ideas what i am doing wrong?
  • jStins
    Offline / Send Message
    jStins polycounter lvl 5
    That script is a bit old, but should still work. Hard to say what's wrong if it's already in Assets/Editor/

    I suggest you use this if FBX is an option:

    https://assetstore.unity.com/packages/essentials/fbx-exporter-101408
  • danr
    Offline / Send Message
    danr greentooth
    i've been using a (modified) version of that script for years, it still works fine in 2018

    its a c# script so make sure you save it as so  - a .cs filename, not unity/javascript which is .js .Also make sure the script is called the same as that defined in public class "EditorObjExporter"

    any specific compile errors will come up in your console, copy and paste those if you need help.

    here is the modified code. It copes properly with hierarchies of gameobjects, which is super useful - ie you can select the top a hierarchy and it'll export all the mesh filters below rather than tripping up on empty gameobject
    using UnityEngine;
    using UnityEditor.SceneManagement;
    using UnityEditor;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System;

    struct ObjMaterial
    {
    public string name;
    public string textureName;
    }

    public class EditorObjExporter : ScriptableObject
    {
    private static int vertexOffset = 0;
    private static int normalOffset = 0;
    private static int uvOffset = 0;


    //User should probably be able to change this. It is currently left as an excercise for
    //the reader.
    private static string targetFolder = "ExportedObj";


    private static string MeshToString(MeshFilter mf, Dictionary<string, ObjMaterial> materialList)
    {
    Mesh m = mf.sharedMesh;
    Material[] mats = mf.GetComponent<Renderer>().sharedMaterials;

    StringBuilder sb = new StringBuilder();

    sb.Append("g ").Append(mf.name).Append("\n");
    foreach(Vector3 lv in m.vertices)
    {
    Vector3 wv = mf.transform.TransformPoint(lv);

    //This is sort of ugly - inverting x-component since we're in
    //a different coordinate system than "everyone" is "used to".
    sb.Append(string.Format("v {0} {1} {2}\n",-wv.x,wv.y,wv.z));
    }
    sb.Append("\n");

    foreach(Vector3 lv in m.normals)
    {
    Vector3 wv = mf.transform.TransformDirection(lv);

    sb.Append(string.Format("vn {0} {1} {2}\n",-wv.x,wv.y,wv.z));
    }
    sb.Append("\n");

    foreach(Vector3 v in m.uv)
    {
    sb.Append(string.Format("vt {0} {1}\n",v.x,v.y));
    }

    for (int material=0; material < m.subMeshCount; material ++) {
    sb.Append("\n");
    sb.Append("usemtl ").Append(mats[material].name).Append("\n");
    sb.Append("usemap ").Append(mats[material].name).Append("\n");

    //See if this material is already in the materiallist.
    try
    {
    ObjMaterial objMaterial = new ObjMaterial();

    objMaterial.name = mats[material].name;

    if (mats[material].mainTexture)
    objMaterial.textureName = AssetDatabase.GetAssetPath(mats[material].mainTexture);
    else
    objMaterial.textureName = null;

    materialList.Add(objMaterial.name, objMaterial);
    }
    catch (ArgumentException)
    {
    //Already in the dictionary
    }


    int[] triangles = m.GetTriangles(material);
    for (int i=0;i<triangles.Length;i+=3)
    {
    //Because we inverted the x-component, we also needed to alter the triangle winding.
    sb.Append(string.Format("f {1}/{1}/{1} {0}/{0}/{0} {2}/{2}/{2}\n",
    triangles[i]+1 + vertexOffset, triangles[i+1]+1 + normalOffset, triangles[i+2]+1 + uvOffset));
    }
    }

    vertexOffset += m.vertices.Length;
    normalOffset += m.normals.Length;
    uvOffset += m.uv.Length;

    return sb.ToString();
    }

    private static void Clear()
    {
    vertexOffset = 0;
    normalOffset = 0;
    uvOffset = 0;
    }

    private static Dictionary<string, ObjMaterial> PrepareFileWrite()
    {
    Clear();

    return new Dictionary<string, ObjMaterial>();
    }

    private static void MaterialsToFile(Dictionary<string, ObjMaterial> materialList, string folder, string filename)
    {
    using (StreamWriter sw = new StreamWriter(folder + "/" + filename + ".mtl"))
    {
    foreach( KeyValuePair<string, ObjMaterial> kvp in materialList )
    {
    sw.Write("\n");
    sw.Write("newmtl {0}\n", kvp.Key);
    sw.Write("Ka 0.6 0.6 0.6\n");
    sw.Write("Kd 0.6 0.6 0.6\n");
    sw.Write("Ks 0.9 0.9 0.9\n");
    sw.Write("d 1.0\n");
    sw.Write("Ns 0.0\n");
    sw.Write("illum 2\n");

    if (kvp.Value.textureName != null)
    {
    string destinationFile = kvp.Value.textureName;


    int stripIndex = destinationFile.LastIndexOf('/');//FIXME: Should be Path.PathSeparator;

    if (stripIndex >= 0)
    destinationFile = destinationFile.Substring(stripIndex + 1).Trim();


    string relativeFile = destinationFile;

    destinationFile = folder + "/" + destinationFile;

    Debug.Log("Copying texture from " + kvp.Value.textureName + " to " + destinationFile);

    try
    {
    //Copy the source file
    File.Copy(kvp.Value.textureName, destinationFile);
    }
    catch
    {

    }


    sw.Write("map_Kd {0}", relativeFile);
    }

    sw.Write("\n\n\n");
    }
    }
    }

    private static void MeshToFile(MeshFilter mf, string folder, string filename)
    {
    Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();

    using (StreamWriter sw = new StreamWriter(folder +"/" + filename + ".obj"))
    {
    sw.Write("mtllib ./" + filename + ".mtl\n");

    sw.Write(MeshToString(mf, materialList));
    }

    MaterialsToFile(materialList, folder, filename);
    }

    private static void MeshesToFile(MeshFilter[] mf, string folder, string filename)
    {
    Dictionary<string, ObjMaterial> materialList = PrepareFileWrite();

    using (StreamWriter sw = new StreamWriter(folder +"/" + filename + ".obj"))
    {
    sw.Write("mtllib ./" + filename + ".mtl\n");

    for (int i = 0; i < mf.Length; i++)
    {
    sw.Write(MeshToString(mf[i], materialList));
    }
    }

    MaterialsToFile(materialList, folder, filename);
    }

    private static bool CreateTargetFolder()
    {
    try
    {
    System.IO.Directory.CreateDirectory(targetFolder);
    }
    catch
    {
    EditorUtility.DisplayDialog("Error!", "Failed to create target folder!", "");
    return false;
    }

    return true;
    }

    [MenuItem ("Custom/Export/Export all MeshFilters in selection to separate OBJs")]
    static void ExportSelectionToSeparate()
    {
    if (!CreateTargetFolder())
    return;

    Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);

    if (selection.Length == 0)
    {
    EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
    return;
    }

    int exportedObjects = 0;

    for (int i = 0; i < selection.Length; i++)
    {
    Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));

    for (int m = 0; m < meshfilter.Length; m++)
    {
    exportedObjects++;
    MeshToFile((MeshFilter)meshfilter[m], targetFolder, selection[i].name + "_" + i + "_" + m);
    }
    }

    if (exportedObjects > 0)
    EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
    else
    EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
    }

    [MenuItem ("Custom/Export/Export whole selection to single OBJ")]
    static void ExportWholeSelectionToSingle()
    {
    if (!CreateTargetFolder())
    return;


    Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);

    if (selection.Length == 0)
    {
    EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
    return;
    }

    int exportedObjects = 0;

    ArrayList mfList = new ArrayList();

    for (int i = 0; i < selection.Length; i++)
    {
    Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));

    for (int m = 0; m < meshfilter.Length; m++)
    {
    exportedObjects++;
    mfList.Add(meshfilter[m]);
    }
    }

    if (exportedObjects > 0)
    {
    MeshFilter[] mf = new MeshFilter[mfList.Count];

    for (int i = 0; i < mfList.Count; i++)
    {
    mf[i] = (MeshFilter)mfList[i];
    }

    string filename = EditorSceneManager.GetActiveScene().name + "_" + exportedObjects;

    int stripIndex = filename.LastIndexOf('/');//FIXME: Should be Path.PathSeparator

    if (stripIndex >= 0)
    filename = filename.Substring(stripIndex + 1).Trim();

    MeshesToFile(mf, targetFolder, filename);


    EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects to " + filename, "");
    }
    else
    EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
    }



    [MenuItem ("Custom/Export/Export each selected to single OBJ")]
    static void ExportEachSelectionToSingle()
    {
    if (!CreateTargetFolder())
    return;

    Transform[] selection = Selection.GetTransforms(SelectionMode.Editable | SelectionMode.ExcludePrefab);

    if (selection.Length == 0)
    {
    EditorUtility.DisplayDialog("No source object selected!", "Please select one or more target objects", "");
    return;
    }

    int exportedObjects = 0;


    for (int i = 0; i < selection.Length; i++)
    {
    Component[] meshfilter = selection[i].GetComponentsInChildren(typeof(MeshFilter));

    MeshFilter[] mf = new MeshFilter[meshfilter.Length];

    for (int m = 0; m < meshfilter.Length; m++)
    {
    exportedObjects++;
    mf[m] = (MeshFilter)meshfilter[m];
    }

    MeshesToFile(mf, targetFolder, selection[i].name + "_" + i);
    }

    if (exportedObjects > 0)
    {
    EditorUtility.DisplayDialog("Objects exported", "Exported " + exportedObjects + " objects", "");
    }
    else
    EditorUtility.DisplayDialog("Objects not exported", "Make sure at least some of your selected objects have mesh filters!", "");
    }

    as @jStins says above, the new fbx exporter is more of a "complete" workflow but i still use the above script all the time for sheer speed

Sign In or Register to comment.