Notifications
Clear all

[Closed] Faster Export of Vertex and Triangle Data?

I want to write a file with all scene TriMesh faces and vertices in world space.
This is currently what I have made, it works but it is really slow for anything even remotely complex, does anyone have any suggestion for a faster way of doing it?

(Current speed for the test teapot scene: 11.7sec)

Thanks in advance.


global ExportScene
(
   if (ExportScene != undefined ) then(
         DestroyDialog ExportScene
   )


   struct sfn (
      fn fn_exportScene = (
         with undo off (
            local savePath = getSaveFileName  types:"SXP(*.sxp)|*.sxp|All|*.*|"
            if(savePath != undefined ) then (
               gc()
               local st=timestamp(); sh=heapfree
               local geoArr = (for o in geometry collect o)
               local vertCount = 0
               local faceCount = 0
               
               for o in geoArr do (
                  vertCount += o.mesh.numverts
                  faceCount += o.mesh.numfaces 
               )
               
               local f = fopen savePath "wb"         
               WriteLong f vertCount
               WriteLong f faceCount
               local v = [0,0,0]
               local vc = 0
               
               for o in geoArr do(
                  vc = o.mesh.numverts
                  for i = 1 to vc do 
                  (
                     v = ((getVert o.mesh i) * o.transform)
                     WriteFloat f v.x
                     WriteFloat f v.z
                     WriteFloat f v.y
                  )
               )
               
               local t = [0,0,0]
               local tc = 0
               local offset = -1
               for o in geoArr do (
                  tc = o.mesh.numfaces
                  for i = 1 to tc do 
                  (
                     t = (meshop.getFace o.mesh i) 
                     WriteLong f (t[1] + offset)
                     WriteLong f (t[2] + offset)
                     WriteLong f (t[3] + offset)
                  )
                  offset += o.mesh.numverts
               )
               
               fclose f
               local timeStr = ((timestamp()-st) as string)
               local heapStr = ((sh-heapfree) as string) 
               messageBox  ("Export Done. 
 time: " + timeStr + "ms 
 heap: " + heapStr ) title:"Export Done." beep:false
               print "Export Done"
               print timeStr
               print heapStr
            )
         )
      )
   )


   struct sUi (
      fn main = (
         rollout ExportScene "Export Scene"(
            button btn_ExportScene "Export Scene" align:#center
            button btn_AddTeapot "Add Teapots" align:#center
            
            on btn_ExportScene pressed do (
               sfn.fn_exportScene()
            )
            
            on btn_AddTeapot pressed do (
               delete $*
               teapot radius: 25 segs: 16 transform:(matrix3 [0.855922,-0.0198715,-0.516723] [0.335542,0.781667,0.525745] [0.393458,-0.623379,0.675714] [-114.069,-101.874,50])
               teapot radius: 50 segs: 16 transform:(matrix3 [0.800525,0.572427,-0.177447] [-0.598118,0.744542,-0.296498] [-0.0376068,0.343489,0.938403] [128.681,122.142,50])
            )
         )
         createdialog ExportScene height:64 width:154
      )
   )
   sUi.main()
   global ExportSceneUi = sUi()
)

6 Replies

Both of these should be around 60 times faster:


(
    
    fn ExportSceneGeometry filename: =
    (
        file = fopen filename "wb"
        
        meshes = for j in geometry collect snapshotasmesh j
        
        totalVerts = 0
        totalFaces = 0

        for j in meshes do
        (
            totalVerts += j.numverts
            totalVerts += j.numfaces
        )
        
        writelong file totalVerts
        writelong file totalFaces
        
        for j in meshes do
        (
            for i = 1 to j.numverts do
            (
                v = getvert j i
                writefloat file v[1]
                writefloat file v[3]
                writefloat file v[2]
            )
        )
        
        offset = -1
        for j in meshes do
        (
            for i = 1 to j.numfaces do
            (
                f = getface j i
                writelong file (f[1] + offset)
                writelong file (f[2] + offset)
                writelong file (f[3] + offset)
            )
            offset += j.numverts
        )
        
        fflush file
        fclose file
        
        for j in meshes do free j
        gc()
        
        return true
    )
    
    output = getsavefilename types:"SXP(*.sxp)|*.sxp|All|*.*|"
    
    if output != undefined do
    (
        st=timestamp(); sh=heapfree
        
        setwaitcursor()
        ExportSceneGeometry filename:output
        setarrowcursor()
        
        format "time:% heap:%
" (timestamp()-st) (sh-heapfree)
    )
    
)


(
    
    fn ExportSceneGeometry filename: =
    (
        file = fopen filename "wb"
        
        totalVerts = 0
        totalFaces = 0
        
        for j in geometry do
        (
            tmesh = snapshotasmesh j
            totalVerts += tmesh.numverts
            totalVerts += tmesh.numfaces
            free tmesh
        )
        
        writelong file totalVerts
        writelong file totalFaces
        
        for j in geometry do
        (
            tmesh = snapshotasmesh j
            for i = 1 to tmesh.numverts do
            (
                v = getvert tmesh i
                writefloat file v[1]
                writefloat file v[3]
                writefloat file v[2]
            )
            free tmesh
        )
        
        offset = -1
        for j in geometry do
        (
            tmesh = snapshotasmesh j
            for i = 1 to tmesh.numfaces do
            (
                f = getface tmesh i
                writelong file (f[1] + offset)
                writelong file (f[2] + offset)
                writelong file (f[3] + offset)
            )
            offset += tmesh.numverts
            free tmesh
        )
        
        fflush file
        fclose file
        gc()
        
        return true
    )
    
    output = getsavefilename types:"SXP(*.sxp)|*.sxp|All|*.*|"
    
    if output != undefined do
    (
        st=timestamp(); sh=heapfree
        
        setwaitcursor()
        ExportSceneGeometry filename:output
        setarrowcursor()
        
        format "time:% heap:%
" (timestamp()-st) (sh-heapfree)
    )
    
)

1 Reply
(@patan77)
Joined: 10 months ago

Posts: 0

Thanks, for me in (max 2018) its even 150x faster then the original code, so that’s really great.
However for more complex scene’s its still way to slow, I am trying to figure out how to do it with C# 3ds max .NET SDK, I feel like with that it should be able to be way faster, but even though I use C# a lot and the 3ds max SDK documentation is extensive I haven’t been able to figure out how to really use it yet, even for something as simple as just printing the scene nodes name. But when I figure it out I’ll post the full code here for future reference.

PS: saw that you accidentally made typo in your code, you wrote

 totalVerts +=

twice while one should be

totalFaces += 

Thanks for the suggestion, but the reason for me not doing it like that is, because eventually I plan to export a lot more information of the scene then just vertex and face data, and eventually after that I just plan to not export at all just load it in to memory and do some processing on it. But for now I am doing is a file export, and the 150x speed improvement of “PolyTools3D” is fine for now, I have a lot of other code I need to get done before I really need to worry of the “scene collect/export speed”, but eventually I will have to come up with a faster solution.

Why not using the default .obj exporter and then to use a script to read what you need from the exported obj file?

Googling is a key to success
C# export mesh

i don’t believe it can be done faster without multi-threading:


// OUT:
         std::ofstream fp(mesh_file, std::ios::out | std::ios::binary);
         fp.write(reinterpret_cast<const char*>(&mesh->numVerts), sizeof(DWORD));
         fp.write(reinterpret_cast<const char*>(&mesh->numFaces), sizeof(DWORD));

         fp.write(reinterpret_cast<const char*>(mesh->verts), mesh->numVerts * sizeof(Point3));
         Face* faces = mesh->faces;
         for (int k=0; k < mesh->numFaces; k++, faces++) fp.write(reinterpret_cast<const char*>(faces), 3 * sizeof(DWORD));
         fp.close();

// IN:
         std::ifstream fp(mesh_file, std::ios::in | std::ios::binary);

         int numv;
         int numf;

         fp.read(reinterpret_cast<char*>(&numv), sizeof(DWORD));
         fp.read(reinterpret_cast<char*>(&numf), sizeof(DWORD));
         
         Mesh* mesh = new Mesh();
         mesh->setNumVerts(numv);
         mesh->setNumFaces(numf);

         fp.read(reinterpret_cast<char*>(mesh->verts), numv * sizeof(Point3));
         Face* faces = mesh->faces;
         for (int k=0; k < numf; k++, faces++) fp.read(reinterpret_cast<char*>(faces), 3 * sizeof(DWORD));

         mesh->InvalidateGeomCache();
         mesh->InvalidateTopologyCache();
         mesh->buildNormals();



(
   m0 = snapshotasmesh $
   t = timestamp()
   h = heapfree

   writeallmesh @"d:	emp\mesh.bin" m0

   format "WRITE mesh(verts:% faces:%) >> time:% heap:%
" m0.numverts m0.numfaces (timestamp() - t) (h - heapfree)
)
-- WRITE mesh(verts:524290 faces:1047040) >> time:71 heap:184L


(
   t = timestamp()
   h = heapfree

   m1 = readallmesh @"d:	emp\mesh.bin"

   format "READ mesh(verts:% faces:%) >> time:% heap:%
" m1.numverts m1.numfaces (timestamp() - t) (h - heapfree)
)
-- READ mesh(verts:524290 faces:1047040) >> time:138 heap:336L


just the reading takes the same time as the writing. all other time is used by mesh invalidation