[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()
)
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)
)
)
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?
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