- Home /
Import model at runtime
I want to be able to put a model into a folder and import it at runtime... is this possible?
I've found these posts:
http://answers.unity3d.com/questions/401675/import-a-3d-model-in-game-during-runtime-in-gui.html
http://answers.unity3d.com/questions/880404/import-fbx-model-at-runtime.html
The top one seems promising, but then the second says that the importers are only available in the editor.
The posts are a bit dated, and I'm hoping that this is something that's possible in Unity 5.
Is it? And how would I go about doing this?
Answer by saschandroid · Oct 05, 2015 at 12:40 PM
You can import models at runtime if you write your own importer. So check the wiki link in the first post on how to import .obj files if you don't want to write the importer completely on your own (so you still have to write the code for importing the material file ".mtl").
@saschandroid Great, thanks. I also noticed there is a different version of ObjImporter called FastObjImporter, but it says it is designed specifically for Blender OBJs. Do you think this would work for other OBJ files as well?
I think that it will work like the other importer on .obj files. The 'problem' with both of these importers is that they can import only 'simple' .obj-files. That means .obj file which only contain v (vertices), vn (normals), vt (texture coordinates) and f (face indices) and material files (.mtl). They can't read meshes made out of curves, lines, nurbs etc. (google for wavefront obj specs for more on obj-files).
So ... as long as you import only 'simple' meshes there should be no problem, no matter if they come from blender or any other 3d program. For more complicated meshes you have to code the importer yourself or find another script.
Right, i once began writing a nurbs converter for simple 16 float nurbs-patches to render an utah $$anonymous$$pot at arbitrary resolution using the patches you can find here. It actually works pretty well, but the greatest problem i had was to have a smooth transition between the patches (calculating the normals right). I put that project on hold for a long time ^^.
I haven't looked deep into the obj format for nurbs / freeform surfaces, however it looks like it goes way beyond simple bezier patches ^^. It would take quite some time to write a full OBJ importer.
If you plan to write or extend an OBJ loader this should be all you need. Have fun reading ^^.
(i thought i had a webplayer build with the $$anonymous$$pot but can't find it in my dropbox ^^)
edit
I've found my old project and made a webplayer example. $$anonymous$$aybe the NURBS class might help someone outthere ^^. I've linked the source as always at the bottom of the webplayer page.
Answer by Azbo · Nov 02, 2015 at 02:44 PM
I do not know if this will work for your needs but you could import the object but set the object to false so that it won't show and then just right some code saying at run time switch the active to true. You can easily find the code to do that.
I hope this has helped.
Answer by mathew_wwx · Nov 02, 2015 at 03:51 PM
I tried the solution referenced in wiki, and acknowledged it is different to import model operation in editor, for the coordinate of the mesh has a little deviation from the imported in editor one. and this code works on the large size obj file dramatically slow, I optimised the loading alogirthm, however, the deviation exists still. here is my code:
#define Debug // Debugging on
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System;
public class ObjLoader
{
List<Vector3> m_bufV = new List<Vector3>();
List<Vector2> m_bufVt = new List<Vector2>();
List<Vector3> m_bufVn = new List<Vector3>();
struct FaceV
{
public int iv;
public int ivt;
public int ivn;
public string key;
};
List<List<FaceV>> m_bufF = new List<List<FaceV>>();
struct GLVertex
{
public Vector3 vert;
public Vector2 uv;
public Vector3 norm;
};
List<GLVertex> m_bufGLV = new List<GLVertex>();
IDictionary<string, int> m_dictGLVertex = new Dictionary<string, int>();
List<int> m_idxTri = new List<int>();
GLVertex GenerateGLV(FaceV fv)
{
#if Debug
if (!(fv.iv < m_bufV.Count
&& fv.ivt < m_bufVt.Count
&& fv.ivn < m_bufVn.Count
&& fv.iv > -1
&& fv.ivt > -1
&& fv.ivn > -1))
Debug.LogError("out of range");
#endif
GLVertex glv = new GLVertex();
if (fv.iv < m_bufV.Count)
glv.vert = m_bufV[fv.iv];
if (fv.ivt < m_bufVt.Count)
glv.uv = m_bufVt[fv.ivt];
if (fv.ivn < m_bufVn.Count)
glv.norm = m_bufVn[fv.ivn];
return glv;
}
bool Parse4V3(string strLn, ref Vector3 vec)
{
string[] subField = strLn.Split();
int iE = 0;
int iF = 1;
for (; iF < subField.Length && iE < 3; iF ++)
{
try
{
vec[iE] = System.Convert.ToSingle(subField[iF]);
iE ++;
}
catch (FormatException)
{
}
}
if (vec[0] < 0)
Debug.Log("x is minus");
return (iE == 3
&& iF == subField.Length);
}
bool Parse4V2(string strLn, ref Vector2 vec)
{
string[] subField = strLn.Split();
int iE = 0;
int iF = 1;
for (; iF < subField.Length && iE < 2; iF ++)
{
try
{
vec[iE] = System.Convert.ToSingle(subField[iF]);
iE ++;
}
catch (FormatException)
{
}
}
return (iE == 2
&& iF == subField.Length);
}
bool Parse4F(string strLn, ref List<FaceV> face)
{
string[] subField = strLn.Split();
int iF = 1;
int cnt = 0;
for (; iF < subField.Length; iF++)
{
FaceV fv = new FaceV();
fv.key = subField[iF];
string[] ssf = subField[iF].Split('/');
try
{
fv.iv = System.Convert.ToInt32(ssf[0]) - 1;
fv.ivt = System.Convert.ToInt32(ssf[1]) - 1;
fv.ivn = System.Convert.ToInt32(ssf[2]) - 1;
}
catch (FormatException)
{
continue;
}
face.Add(fv);
cnt ++;
}
return cnt == 3
|| cnt == 4;
}
public Mesh ImportFile(string filePath)
{
StreamReader stream = File.OpenText(filePath);
string strLine = stream.ReadLine();
bool parseable = true;
while (null != strLine
&& parseable)
{
if (strLine.StartsWith("v "))
{
Vector3 vec3 = new Vector3();
parseable = Parse4V3(strLine,ref vec3);
if (parseable)
m_bufV.Add(vec3);
}
else if (strLine.StartsWith("vt"))
{
Vector2 vec2 = new Vector2();
parseable = Parse4V2(strLine, ref vec2);
if (parseable)
m_bufVt.Add(vec2);
}
else if (strLine.StartsWith("vn"))
{
Vector3 vec3 = new Vector3();
parseable = Parse4V3(strLine, ref vec3);
if (parseable)
m_bufVn.Add(vec3);
}
else if (strLine.StartsWith("f "))
{
List<FaceV> face = new List<FaceV>();
parseable = Parse4F(strLine, ref face);
if (parseable)
m_bufF.Add(face);
}
strLine = stream.ReadLine();
}
stream.Close();
List<int> faceprime = new List<int>();
for (int iF = 0; iF < m_bufF.Count; iF++)
{
List<FaceV> face = m_bufF[iF];
faceprime.Clear();
int iFV = 0;
for (; iFV < face.Count; iFV++)
{
FaceV fv = face[iFV];
int iV = -1;
if (!m_dictGLVertex.TryGetValue(fv.key, out iV))
{
iV = m_bufGLV.Count;
GLVertex glv = GenerateGLV(fv);
m_bufGLV.Add(glv);
m_dictGLVertex[fv.key] = iV;
}
faceprime.Add(iV);
}
Debug.Assert(faceprime.Count > 2);
int iPivot = faceprime[0];
int cntTri = faceprime.Count - 1;
for (iFV = 1; iFV < cntTri; iFV++)
{
m_idxTri.Add(iPivot);
m_idxTri.Add(faceprime[iFV]);
m_idxTri.Add(faceprime[iFV+1]);
}
}
//for (int iTri = 0; iTri < m_idxTri.Count; )
//{
// int i_v1 = m_idxTri[iTri++];
// int i_v2 = m_idxTri[iTri++];
// int i_v3 = m_idxTri[iTri++];
// string strLog = string.Format("Triangle:<{0}>,<{1}>,<{2}>", m_bufGLV[i_v1].vert.ToString(), m_bufGLV[i_v1].uv.ToString(), m_bufGLV[i_v1].norm.ToString());
// strLog += string.Format("\n\t<{0}>,<{1}>,<{2}>", m_bufGLV[i_v2].vert.ToString(), m_bufGLV[i_v2].uv.ToString(), m_bufGLV[i_v2].norm.ToString());
// strLog += string.Format("\n\t<{0}>,<{1}>,<{2}>", m_bufGLV[i_v3].vert.ToString(), m_bufGLV[i_v3].uv.ToString(), m_bufGLV[i_v3].norm.ToString());
// Debug.Log(strLog);
//}
Vector3[] Verts = new Vector3[m_bufGLV.Count];
Vector2[] Uvs = new Vector2[m_bufGLV.Count];
Vector3[] Norms = new Vector3[m_bufGLV.Count];
for (int iGLV = 0; iGLV < m_bufGLV.Count; iGLV ++)
{
Verts[iGLV] = m_bufGLV[iGLV].vert;
Uvs[iGLV] = m_bufGLV[iGLV].uv;
Norms[iGLV] = m_bufGLV[iGLV].norm;
}
int[] Tris = new int[m_idxTri.Count];
for (int iTri = 0; iTri < m_idxTri.Count; iTri++)
{
Tris[iTri] = m_idxTri[iTri];
}
Mesh mesh = new Mesh();
mesh.vertices = Verts;
mesh.uv = Uvs;
mesh.normals = Norms;
mesh.triangles = Tris;
mesh.RecalculateBounds();
mesh.Optimize();
return mesh;
}
}
I know what adjustment happens, it flips the x coordinates of the obj file, I can have the similar effect by setting -1 for scale x, but if I try to directly change the x value of the coordinate, then it doesn't work as expectation, and I completely confused what difference between this 2 approaches to flip x coordinate.
If you change the coordinate ( x -> -x) the normals you import are wrong and have to be recalculated. If you scale the model at the x-axis by -1 the normals will still be correct (at least that's what happened with my models).
Hey, great work you did here. Do you know how to import materials as well? In other works, importing all the stuff that may come with a model (mesh, materials etc).