- Home /
The question is answered, right answer was accepted. Points awarded to @cjdev
Awarding 500 points if we manage to do it. How to import mesh over 400 000 verts into unity
Hello!
My game requires a single, imported mesh with more than 300 000 vertices.
You might say that's a bizarre number, I know, but it's justified. The project is using k-d trees which rely on the a single mesh being without seams (no overlapping vertices).
Moreover, the mesh is not going to be used in rendering, it's entirely for the kd datastructure. All it needs is non-overlapping vertices connected by the triangles
.
Please tell me the way to get that mesh over into unity, since the limit is 65 000 vertices, which causes mesh to be split into parts (with various verts).
Is it going to be possible to create procedural mesh which breaches that 65k vert limit? Maybe we could look at the spitted, imported mesh and reconstruct a non-seam procedural mesh without seams on top of it if that's allowed?
If so, what would be the snippet of code which modifies the topology when wielding?
True thank you!
If it's not to be used for rendering, does it need to live on the GPU? If no then can't you just create your own data structure to hold the data, which wouldn't be subject to the vertex limit? Otherwise you might be able to do something directly with OpenGL or a plugin or something.
Answer by cjdev · Aug 28, 2015 at 09:31 PM
Since you only need the data from the vertices and the triangles you might consider just parsing the data directly from an .obj file. Saving it out as a text file into your resources folder then loading it as a Text Asset would let you get the vertex and face info fairly easily since the obj format is so straightforward.
Vector3[] verts = new Vector3[400000]; //Assuming your vert and tri counts are known
int[][] tris = new int[500000][];
int vertIndex = 0;
int triIndex = 0;
TextAsset obj = Resources.Load("BigMesh") as TextAsset; //Your BigMesh.txt formerly .obj file
string[] objLines = Regex.Split(obj.text, "\n|\r|\r\n");
for (int i = 0 ; i < objLines.Length; i ++ )
{
string[] lineParts = objLines[i].Split(' ');
if (lineParts[0] == "v")
{
verts[vertIndex] = new Vector3(float.Parse(lineParts[1]), float.Parse(lineParts[2]), float.Parse(lineParts[3]));
vertIndex++;
}
else if (lineParts[0] == "f")
{
tris[triIndex] = new int[] { int.Parse(lineParts[1].Split('/')[0]), int.Parse(lineParts[2].Split('/')[0]), int.Parse(lineParts[3].Split('/')[0]) };
tris[triIndex + 1] = new int[] { int.Parse(lineParts[1].Split('/')[0]), int.Parse(lineParts[3].Split('/')[0]), int.Parse(lineParts[4].Split('/')[0]) };
triIndex += 2;
}
}
thank you, 500 points awarded.
I was already doing exactly the same thing, just got stuck at parsing for triangles.
if anyone is interested, this is what obj file looks like:
# 3ds $$anonymous$$ax Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# File Created: 28.08.2015 22:16:06
mtllib cube$$anonymous$$esh.mtl
#
# object Box001
#
v -19.6850 -22.6114 19.6850
v -19.6850 -22.6114 -19.6850
v 19.6850 -22.6114 -19.6850
v 19.6850 -22.6114 19.6850
v -19.6850 16.7587 19.6850
v 19.6850 16.7587 19.6850
v 19.6850 16.7587 -19.6850
v -19.6850 16.7587 -19.6850
# 8 vertices
vn 0.0000 -1.0000 -0.0000
vn 0.0000 1.0000 -0.0000
vn 0.0000 0.0000 1.0000
vn 1.0000 0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
vn -1.0000 0.0000 -0.0000
# 6 vertex normals
vt 1.0000 0.0000 0.0000
vt 1.0000 1.0000 0.0000
vt 0.0000 1.0000 0.0000
vt 0.0000 0.0000 0.0000
# 4 texture coords
g Box001
usemtl wire_085028177
s 2
f 1/1/1 2/2/1 3/3/1 4/4/1
s 4
f 5/4/2 6/1/2 7/2/2 8/3/2
s 8
f 1/4/3 4/1/3 6/2/3 5/3/3
s 16
f 4/4/4 3/1/4 7/2/4 6/3/4
s 32
f 3/4/5 2/1/5 8/2/5 7/3/5
s 64
f 2/4/6 1/1/6 5/2/6 8/3/6
# 6 polygons
.
# are comments,
s are smoothing groups (ignored in my case)
f are faces (triangles) (sometimes they are separated by /, most of the time by spaces)
to parse an obj file, one can use regular expressions (tutorial here)
Test your progress and try code here
an alternative to the answer, I've found an example, which almost worked . It parsed the file extracting integers and triangles.
Obj files which are only used to build datastructures can be saved in Application.persistentDatapath directory, without a need to be transfered into .txt files. That will help in avoiding unothorized access to folders outside of unity (That way unity won't "import" that .obj, splitting it, but you have access to the file )
There are several pitfalls to watch out for:
1) when getting triangle integer, you need to subtract 1, since obj format specifies them starting from 1, and not from 0.
2) Remeber that there is also "vn", so also check if (lineParts[0] == "f" && lineParts[0].Length == 1
since we want to do vertices without normals
3) we can use generic lists to add the data one by one.
Here is the complete code which works:
public static void GetData(){
List<Vector3> verts = new List<Vector3>(); //Assu$$anonymous$$g your vert and tri counts are known
List<int> tris = new List<int> ();
string obj = File.ReadAllText (Application.persistentDataPath + "/cube$$anonymous$$esh.obj");
string[] lines = Regex.Split(obj, @"\n|\r|\r\n");
for (int i = 0; i < lines.Length; i++ ){
string[] lineParts = Regex.Split(lines[i], @"\s+"); //split the line into chunks of strings
if (lineParts[0] == "v" && lineParts[0].Length == 1){ //for example, don't include vn or vt
verts.Add( new Vector3(float.Parse(lineParts[1]), float.Parse(lineParts[2]), float.Parse(lineParts[3])) );
}
else if (lineParts[0] == "f" && lineParts[0].Length == 1){
tris.AddRange( new int[] { int.Parse(Regex.Split(lineParts[1], @"/")[0]) - 1, int.Parse(Regex.Split(lineParts[2], @"/")[0])- 1, int.Parse(Regex.Split(lineParts[3], @"/")[0])- 1 }.ToList() );
tris.AddRange( new int[] { int.Parse(Regex.Split(lineParts[1], @"/")[0])- 1, int.Parse(Regex.Split(lineParts[3], @"/")[0])- 1, int.Parse(Regex.Split(lineParts[4], @"/")[0])- 1 }.ToList() );
}
}//end iterating through the lines
$$anonymous$$esh my_mesh = new $$anonymous$$esh ();
my_mesh.vertices = verts.ToArray();//new Vector3[]{new Vector3 (-2f, 2f, 0), new Vector3 (2f, 2f,0),new Vector3 (2f, -2f, 0), new Vector3(-2f,-2f,0)};
my_mesh.triangles = tris.ToArray(); // new int[]{0,1,2,0,2,3};
GameObject go = GameObject.CreatePrimitive (PrimitiveType.Cube);
//GameObject go2 = GameObject.CreatePrimitive (PrimitiveType.Cylinder);
go.GetComponent<$$anonymous$$eshFilter> ().shared$$anonymous$$esh = my_mesh;
}
it will create a cube object in hierarchy and will swap its mesh for the custom one. Turn on wireframe mode in scene view to trace the topology.
Thank you once again!
Answer by YoungDeveloper · Aug 28, 2015 at 01:11 PM
It's not possible by default import, unity will split it up. I have a theory about why it does so though. Some data is probably held as ushort which max value is 65k with something, that's probably the reason why the cap.
I see couple solutions.
If the bad looking split bothers you but nice looking split will do you can pre split the mesh in any 3d package.
Weld the mesh seams on the run via code, but this might be actually harder than 3rd solution.
Construct the whole mesh via code. There probably might be a slow down on the start, but instead of importing mesh import as fbx or obj import it as text file. The file is a simply read verts location file writing a mesh builder from it would be relatively easy.
According to the question, the mesh won't need to be rendered.
It is possible to write a custom importer that imports the mesh to a custom data structure. You won't be able to use Unity meshes because of the 65k hard limit of vertices, but if you're only using the mesh to store information, a simple custom data structure should be sufficient.
Hey @YoungDeveloper, @$$anonymous$$lausJoensuu !
Ok, re-constructing mesh via code might be possible. But I recently found this post: http://answers.unity3d.com/questions/506569/adding-vertices-to-procedural-mesh-generator.html#comment-1037198
In there, @Bunny83 answers that there is a limit on procedural meshes as well.
So, I might need to indeed, build a create another datastructure to hold vector3s and connections to other points which would be via triangles. Indeed, the mesh would not need to be rendered, but must be a whole, without seams. @_Gkxd @DaveCarlilie
Will be testing and will report progress
@_Gkxd At this point it doesn't really matter should it be rendered or not. The core idea is to work with the exported file directly. Which contains verts, uv and rest of the stuff. OP can pick out anything he needs or not.
Didn't you say that you didn't need to render the mesh?
$$anonymous$$oreover, the mesh is not going to be used in rendering, it's entirely for the kd datastructure.
If you need to render the mesh, you either need to use Unity meshes (which will involve splitting your mesh up at some point), or you can use some GL functions to draw triangles from your own data structure, which will likely be slower than using meshes.
Do you really need a mesh or would a data structure with verts, tris, and normals work?
Answer by KlausJoensuu · Aug 28, 2015 at 12:09 PM
Honestly i don't think there is a way. :( I would have had the same problem but i found a short cut by building a procedural mesh. http://imgur.com/qIZ8F2u . But there is a way to combine them after they are cut up and imported in unity3D. You can fool unity3D by thinking its one big mesh by code.
tried creating a mesh
verts = new Vector3[400000];
tris = new int[1200000];
proced_mesh = new $$anonymous$$esh();
proced_mesh.vertices = verts;
proced_mesh.triangles = tris;
but got an error that it breaches a limit :(
@IgorAherne I think you understanded me wrong. You cannot have that big mesh but you can trick unity3D to read it as one big mesh. So basiclly the mesh is cut into many parts but you need to fool unity3d that it reads the meshes into one.