that’s insane :keenly:
just tested it with size=25
i+1
Time: 0.633sec. Mem: 1005180Li.x += 1
Time: 0.631sec. Mem: 1512L
Btw: I’m updating the first original post in the thread with the improved code.
A little more… pure C#:
(
fn compileCString =
(
SuperCubeClassName = "PathScripts.SuperCube"
classStr = (
"
using System;
using Autodesk.Max;
namespace PathScripts
{
class SuperCube
{
static public IGlobal global = GlobalInterface.Instance;
static public IInterface14 ip = global.COREInterface14;
static public void createSuperCube(int numCubes)
{
int[][] Faces = new int[12][] { new int[3] { 0, 2, 3 }, new int[3] { 3, 1, 0 }, new int[3] { 4, 5, 7 }, new int[3] { 7, 6, 4 },
new int[3] { 0, 1, 5 }, new int[3] { 5, 4, 0 }, new int[3] { 1, 3, 7 }, new int[3] { 7, 5, 1 },
new int[3] { 3, 2, 6 }, new int[3] { 6, 7, 3 }, new int[3] { 2, 0, 4 }, new int[3] { 4, 6, 2 }};
float[][] Verts = new float[8][] { new float[3] { -0.5f, -0.5f, -0.5f }, new float[3] { 0.5f, -0.5f, -0.5f }, new float[3] { -0.5f, 0.5f, -0.5f }, new float[3] { 0.5f, 0.5f, -0.5f },
new float[3] { -0.5f, -0.5f, 0.5f }, new float[3] { 0.5f, -0.5f, 0.5f }, new float[3] { -0.5f, 0.5f, 0.5f }, new float[3] { 0.5f, 0.5f, 0.5f }};
uint[] theElementSmooth = new uint[12] { 2, 2, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64 };
IClass_ID cid = global.Class_ID.Create((uint)BuiltInClassIDA.EDITTRIOBJ_CLASS_ID, 0);
// Create a new TriObject.
object objectEditMesh = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID);
// Create a new node to hold it in the scene.
IObject objBaseObject = (IObject)objectEditMesh;
IINode node = global.COREInterface.CreateObjectNode(objBaseObject);
// Name it unique.
string newName = \"SuperCube\";
//ip.MakeNameUnique(ref newName);
node.Name = newName;
// Cast to TriObject
ITriObject triNew = objBaseObject as ITriObject;
int nTotal = numCubes * numCubes * numCubes;
Random r = new Random();
int countVerts = 0;
int actualVert = 0;
int actualFace = 0;
double range = 1.0 - 0.1;
// Setup the new TriObject with number of faces and verts
triNew.Mesh.SetNumFaces(12 * nTotal, false, false);
triNew.Mesh.SetNumVerts(8 * nTotal, false, false);
for (int z = 0; z < numCubes; z++)
{
for (int y = 0; y < numCubes; y++)
{
for (int x = 0; x < numCubes; x++)
{
actualVert = (8 * countVerts);
actualFace = (12 * countVerts);
float rng = (float)(r.NextDouble() * range + 0.1);
for (int v = 0; v < 8; v++)
{
triNew.Mesh.SetVert(v + actualVert, Verts[v][0] * rng + x, Verts[v][1] * rng + y, Verts[v][2] * rng + z);
}
for (int f = 0; f < 12; f++)
{
triNew.Mesh.Faces[f + actualFace].SetVerts(Faces[f][0] + actualVert, Faces[f][1] + actualVert, Faces[f][2] + actualVert);
triNew.Mesh.Faces[f + actualFace].SetEdgeVisFlags(EdgeVisibility.Vis, EdgeVisibility.Vis, EdgeVisibility.Invis);
triNew.Mesh.Faces[f + actualFace].SmGroup = theElementSmooth[f];
}
countVerts++;
}
}
}
// Assign identity transform, position and center pivot.
//IMatrix3 tm = global.Matrix3.Create(); tm.IdentityMatrix();
//IPoint3 pt0 = global.Point3.Create(0, 0, 0);
//node.SetNodeTM(0, tm);
//node.ObjOffsetPos = pt0;
//node.CenterPivot(0, false);
// make it drawable.
triNew.Mesh.InvalidateGeomCache();
ip.RedrawViews(0, RedrawFlags.Normal, null);
}
}
}
"
)
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
dotnet.setlifetimecontrol compilerParams #dotnet
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.ReferencedAssemblies.Add(@"C:\Program Files\Autodesk\3ds Max Design 2014\Autodesk.Max.dll");
compilerParams.GenerateInMemory = on
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(classStr)
dotnet.setlifetimecontrol compilerResults #dotnet
---- CHECK COMPILE
if (compilerResults.Errors.Count > 0 ) then
(
local errs = stringstream ""
for i = 0 to (compilerResults.Errors.Count-1) do
(
local err = compilerResults.Errors.Item[i]
format "Error:% Line:% Column:% %
" err.ErrorNumber err.Line err.Column err.ErrorText to:errs
)
format "%
" errs
return undefined
)
----
else
(
compilerResults.CompiledAssembly
)
)
SuperCube = (compileCString()).CreateInstance "PathScripts.SuperCube"
)
And executing it:
hf = heapfree
t1=timestamp()
SuperCube.createSuperCube 15
t2 =timestamp()
format "Time: %sec. Mem: %
" ((t2-t1)/1000 as float) (hf-heapfree)
I get: Time: 0.025sec. Mem: 2576L (40.000%?? :))
Edit: and 0.085 for n=25
Tomorrow I’ll share a new version for any primitive…
I get 1,25 sec. for n=15 teapots.
Nice. 2-3 times faster on average.
I’ve just managed to create size=25 with instanced particle teapots under 2 seconds on my laptop.
compilerParams.ReferencedAssemblies.Add( getdir #maxroot + "Autodesk.Max.dll");
delete objects
(
gc()
with redraw off (
with undo off (
t1=timestamp()
hf = heapfree
size = 25
w = 10
h = 10
l = 10
m = mesh numverts:(size^3) numfaces:(size^3)
i = [0,0]
for x=0 to size-1 do for y=0 to size-1 do for z=0 to size-1 do (
setvert m (i.x += 1) [ x*w, y*l, z*h ]
)
p = PArray()
p.Emitter_Stop = 0f
p.quantityMethod = 1
p.Total_Number = size^3
p.formation = 2
p.Growth_Time = 0f
p.Fade_Time = 0f
p.size = w
p.standardParticle = 1
p.viewPercent = 100
p.viewType = 2
p.Size_Variation = 0.5
p.particleType = 3
p.materialSource = 2
t = Teapot radius:(w as float/ size)
p.instancingObject = t
p.emitter = m
mm = Mesher()
mm.pick = p
converttomesh mm
-- delete mm
delete p
delete m
format "Time: %sec. Mem: %
" ((timestamp()-t1)/1000 as float) (hf-heapfree)
)
))
You win! :bowdown:
I get about 6 sec for 25 teapots… and I haven’t included yet map coords. So I give up.
I wouldn’t give up, just yet.
I’ve tested both codes, PArray from post #37 and C# from post #35 in Max 2014 and the differences in performance are not small.
With a few minor improvements to the C# version from post #35 I get these results using boxes, but I don’t think things will change significantly with Teapots or other geometries:
10^3 (1000 Boxes)
time: 6 ms heap: 1744L | PArray
time: 3 ms heap: 552L | C#
20^3 (8000 Boxes)
time: 84 ms heap: 1744L | PArray
time: 6 ms heap: 552L | C#
30^3 (27000 Boxes)
time: 633 ms heap: 1744L | PArray
time: 14 ms heap: 560L | C#
40^3 (64000 Boxes)
time: 3131 ms heap: 1744L | PArray
time: 28 ms heap: 552L | C#
BTW, in the PArray version, as long as the mapcoords parameter is not explicitly turned on, the geometry uses default mapping. If you turn on mapping it is slower. So both versions I’ve compared are the same, unmapped boxes. The only missing thing in the C# version are Material IDs, but I don’t think that would cause a big impact.
Clue for minor C# improvements:
triNew.Mesh
triNew.Mesh.Faces
I am sure there are many other ways to improve the C# version, but with these simple changes it should be around 3 times faster.
This is very interesting.
Code:
: (n15 / 3375boxes) [Time: 0.033 sec] [Mem: 368L]
[View Code]( http://www.patan77.com/download/CODE_fn_createBoxSpeedTest_C01.txt)
– Serejah
compilerParams.ReferencedAssemblies.Add( getdir #maxroot + “Autodesk.Max.dll”);
we have to understand that c# way of adding boxes is very limited and can’t be used in general as a cloning element(s) approach.
only attach is really complete solution
You are computing redrawing time. Last line should be removed to be comparable with other codes.
// ip.RedrawViews(0, RedrawFlags.Normal, null)
That’s some crazy improvements you wouldn’t mind sharing a bit more exactly what those improvements are? (;
Sure. The code below is from Andrés (post #35). In bold are the minor changes. Original code gives me 80ms for 40^3 boxes, the improved code goes down to 28ms, almost 3X faster.
(
fn compileCString =
(
SuperCubeClassName = "PathScripts.SuperCube"
classStr = (
"
using System;
using Autodesk.Max;
using System.Collections.Generic;
namespace PathScripts
{
class SuperCube
{
static public IGlobal global = GlobalInterface.Instance;
static public IInterface14 ip = global.COREInterface14;
static public void createSuperCube(int numCubes)
{
int[][] Faces = new int[12][] { new int[3] { 0, 2, 3 }, new int[3] { 3, 1, 0 }, new int[3] { 4, 5, 7 }, new int[3] { 7, 6, 4 },
new int[3] { 0, 1, 5 }, new int[3] { 5, 4, 0 }, new int[3] { 1, 3, 7 }, new int[3] { 7, 5, 1 },
new int[3] { 3, 2, 6 }, new int[3] { 6, 7, 3 }, new int[3] { 2, 0, 4 }, new int[3] { 4, 6, 2 }};
float[][] Verts = new float[8][] { new float[3] { -0.5f, -0.5f, -0.5f }, new float[3] { 0.5f, -0.5f, -0.5f }, new float[3] { -0.5f, 0.5f, -0.5f }, new float[3] { 0.5f, 0.5f, -0.5f },
new float[3] { -0.5f, -0.5f, 0.5f }, new float[3] { 0.5f, -0.5f, 0.5f }, new float[3] { -0.5f, 0.5f, 0.5f }, new float[3] { 0.5f, 0.5f, 0.5f }};
uint[] theElementSmooth = new uint[12] { 2, 2, 4, 4, 8, 8, 16, 16, 32, 32, 64, 64 };
IClass_ID cid = global.Class_ID.Create( (uint) BuiltInClassIDA.EDITTRIOBJ_CLASS_ID , 0 );
// Create a new TriObject.
object objectEditMesh = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID);
// Create a new node to hold it in the scene.
IObject objBaseObject = (IObject)objectEditMesh;
IINode node = global.COREInterface.CreateObjectNode( objBaseObject );
// Name it unique.
string newName = \"SuperCube\";
//ip.MakeNameUnique(ref newName);
node.Name = newName;
// Cast to TriObject
ITriObject triNew = objBaseObject as ITriObject;
int nTotal = numCubes * numCubes * numCubes;
Random r = new Random();
int countVerts = 0;
int actualVert = 0;
int actualFace = 0;
double range = 1.0 - 0.1;
[B]IMesh TMESH = triNew.Mesh;[/B]
// Setup the new TriObject with number of faces and verts
TMESH.SetNumFaces(12 * nTotal, false, false);
TMESH.SetNumVerts(8 * nTotal, false, false);
[B]IList<IFace> MESH_FACES = TMESH.Faces;[/B]
for (int z = 0; z < numCubes; z++)
{
for (int y = 0; y < numCubes; y++)
{
for (int x = 0; x < numCubes; x++)
{
actualVert = (8 * countVerts);
actualFace = (12 * countVerts);
float rng = (float)(r.NextDouble() * range + 0.1);
for (int v = 0; v < 8; v++)
{
[B]TMESH[/B].SetVert(v + actualVert, Verts[v][0] * rng + x, Verts[v][1] * rng + y, Verts[v][2] * rng + z);
}
for (int f = 0; f < 12; f++)
{
[B]IFace FACE = MESH_FACES[f + actualFace];[/B]
[B]FACE[/B].SetVerts(Faces[f][0] + actualVert, Faces[f][1] + actualVert, Faces[f][2] + actualVert);
[B]FACE[/B].SetEdgeVisFlags(EdgeVisibility.Vis, EdgeVisibility.Vis, EdgeVisibility.Invis);
[B]FACE[/B].SmGroup = theElementSmooth[f];
}
countVerts++;
}
}
}
// Assign identity transform, position and center pivot.
//IMatrix3 tm = global.Matrix3.Create(); tm.IdentityMatrix();
//IPoint3 pt0 = global.Point3.Create(0, 0, 0);
//node.SetNodeTM(0, tm);
//node.ObjOffsetPos = pt0;
//node.CenterPivot(0, false);
// make it drawable.
TMESH.InvalidateGeomCache();
ip.RedrawViews(0, RedrawFlags.Normal, null);
}
}
}
"
)
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
dotnet.setlifetimecontrol compilerParams #dotnet
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.ReferencedAssemblies.Add( getdir #maxroot + "Autodesk.Max.dll");
compilerParams.GenerateInMemory = on
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(classStr)
dotnet.setlifetimecontrol compilerResults #dotnet
---- CHECK COMPILE
if (compilerResults.Errors.Count > 0 ) then
(
local errs = stringstream ""
for i = 0 to (compilerResults.Errors.Count-1) do
(
local err = compilerResults.Errors.Item[i]
format "Error:% Line:% Column:% %
" err.ErrorNumber err.Line err.Column err.ErrorText to:errs
)
format "%
" errs
return undefined
)
----
else
(
compilerResults.CompiledAssembly
)
)
global assembly = compileCString()
SuperCube = assembly.CreateInstance "PathScripts.SuperCube"
/* TEST */
delete objects
gc()
st=timestamp(); sh=heapfree
SuperCube.createSuperCube 40
format "time:% heap:%
" (timestamp()-st) (sh-heapfree)
)
There is more to do, I guess.
Thanks this is great, for me with 15^3 its 1583x ( 158333% ) the speed of the original problem, pretty crazy.