Notifications
Clear all

[Closed] Improve speed of "Create box()"

I ran all code at 50^3 and some interesting results, the original code expected time would be 351sec took more like 2h scales really really bad, and at n 50 pArray took 21.6sec while pflow only takes 2.47sec.

2 Replies
(@polytools3d)
Joined: 11 months ago

Posts: 0

The C# version takes 52ms (0.052 sec.) for 50^3 and 400ms (0.4 sec.) for 100^3 (12 million face).

(@patan77)
Joined: 11 months ago

Posts: 0

yeah the c# are for sure the fastest, that’s kind of expected, tho for me its 243ms 50^3, don’t know why its like 5x slower for me.

ok… finally i’ve found a time to look at this problem deeper…

first is a numbers i have for “true mesh clone” written on c++ SDK

50^3 boxes => verts:1000000 time:14 heap:272L
50^3 teapots => verts:66250000 time:1140 heap:272L

(50^3 == 125000)

but it seems like Jorge’s machine is much faster than mine

How would someone set uv coordinates with the C# version, example so every box have one unique UV / “solid color” ?

With pflow I just do:

pCont.setParticleMapping i [u,v,w] 

also another problem with pflow version other then its slower then C# is that mesher() only support ~500k boxes and I want to do > 1M, so pflow with mesher() doesn’t really even work for what I am trying to do.

PFlow version with UV:

fn fn_createBoxSpeedTest  = (
	gc()
	hf = heapfree
	local timeStart = timestamp()
	with redraw off(
		undo off(
			sliderTime = 0
			local n = 15
			local bScript = 
			("
				on ChannelsUsed pCont do
				(
					pCont.useScale = true
					pCont.usePosition = true
					pCont.useMapping  = true
				)
				on Proceed pCont do 
				(
					n = ((pCont.NumParticles())^(1/3.0))
					i = [0,0]
					rng
					rngUV
					for z = 0 to (n - 1) do (
						for y = 0 to (n - 1) do (
							for x = 0 to (n - 1) do (
								rng = (random 0.1 1)
								rngUV = (random 0.0 1.0)
								i.x += 1
								pCont.particleIndex = i.x
								pCont.particleScale = rng
								pCont.particlePosition = [x,y,z] 
								pCont.setParticleMapping i.x 1 [rngUV,rngUV,0]
							)
						)
					)
				)
			")
			local pmat = StandardMaterial name:"pMat" 
			local ramp = Gradient_Ramp()  --(bitmaptexture filename:imgToVoxelDB.importedColorMap)
			ramp.Gradient_Ramp.Flag__1.color = [255,0,0]
			ramp.Gradient_Ramp.Flag__2.color = [0,255,0]
			ramp.Gradient_Ramp.Flag__3.color = [0,0,255]
			pmat.diffuseMap = ramp
			showTextureMap pMat on
			local pf = PF_Source Quantity_Viewport:100 Particle_Amount_Limit:1000000
			ParticleFlow.BeginEdit()
			pf.AppendAction (RenderParticles())
			local e1 = Event name:"genBoxesEvent"
			e1.AppendAction (Birth Amount: (n^3) Emit_Start:0 Emit_Stop:0)
			e1.AppendAction (Script_Operator Proceed_Script: bScript)
			e1.AppendAction (ShapeLibrary '3D_Type':0 size:1)
			e1.AppendAction (Material_Static Assigned_Material:pMat)
			e1.AppendAction (DisplayParticles type:6)
			ParticleFlow.EndEdit()
			pf.appendInitialActionList e1
			local m = Mesher()
			m.pick = pf
			sm = (snapshot m)
			sm.material = pmat
			particleFlow.BeginEdit()
			particleFlow.delete $'genBoxesEvent'
			particleFlow.EndEdit()
			delete m 	
			delete pf
		)
	)
	local timeStop = timestamp()
	print ("Time: " + (((timeStop - timeStart)/1000.0)as string) + " sec Mem: " + ((hf-heapfree) as string))
)
fn_createBoxSpeedTest()

you don’t need mesher at all. you can snapshot ‘event node’ i guess

1 Reply
(@patan77)
Joined: 11 months ago

Posts: 0

Didn’t know that, it brings down the original n 15 time of 0.073sec to 0.045sec but looks like that also limited to ~500k boxes

(n 100)

I would think that this should work

(snapshotasmesh (pf.getParticleGroup 1))

but only works if I do this as print before

print ((getnodebyname(pf.name + "->" + e1.name))as string)

its a bit strange :S

delete objects
gc()

fn fn_createBoxSpeedTest  = (
	hf = heapfree
	local timeStart = timestamp()
	with redraw off(
		undo off(
			sliderTime = 0
			local n = 15
			local bScript = 
			("
				on ChannelsUsed pCont do
				(
					pCont.useScale = true
					pCont.usePosition = true
				)
				on Proceed pCont do 
				(
					n = ((pCont.NumParticles())^(1/3.0))
					i = [0,0]
					for z = 0 to (n - 1) do (
						for y = 0 to (n - 1) do (
							for x = 0 to (n - 1) do (
								rng = (random 0.1 1)
								rngUV = (random 0.0 1.0)
								i.x += 1
								pCont.particleIndex = i.x
								pCont.particleScale = rng
								pCont.particlePosition = [x,y,z] 
							)
						)
					)
				)
			")
			local pf = PF_Source Quantity_Viewport:100 Particle_Amount_Limit:1000000
			ParticleFlow.BeginEdit()
			pf.AppendAction (RenderParticles())
			local e1 = Event name:"testEvent"
			e1.AppendAction (Birth Amount: (n^3) Emit_Start:0 Emit_Stop:0)
			e1.AppendAction (Script_Operator Proceed_Script: bScript)
			e1.AppendAction (ShapeLibrary '3D_Type':0 size:1)
			pf.appendInitialActionList e1
			ParticleFlow.EndEdit()
			print ((getnodebyname(pf.name + "->" + e1.name))as string)
			mm = mesh name:"result" mesh:(snapshotasmesh (pf.getParticleGroup 1))
			particleFlow.BeginEdit()
			particleFlow.delete pf
			particleFlow.EndEdit() 	
		)
	)
	local timeStop = timestamp()
	print ("Time: " + (((timeStop - timeStart)/1000.0)as string) + " sec Mem: " + ((hf-heapfree) as string))
)
fn_createBoxSpeedTest()

this snippet shows that you don’t need a Mesher node at all, and also shows how to wipe (delete) the particle’s stuff after at the end


delete objects
gc()

fn fn_createBoxSpeedTest  = (
	hf = heapfree
	local timeStart = timestamp()
	with redraw off(
		undo off(
			sliderTime = 0
			local n = 15
			local bScript = 
			("
				on ChannelsUsed pCont do
				(
					pCont.useScale = true
					pCont.usePosition = true
					pCont.useMapping  = true
				)
				on Proceed pCont do 
				(
					n = ((pCont.NumParticles())^(1/3.0))
					i = [0,0]
					rng
					rngUV
					for z = 0 to (n - 1) do (
						for y = 0 to (n - 1) do (
							for x = 0 to (n - 1) do (
								rng = (random 0.1 1)
								rngUV = (random 0.0 1.0)
								i.x += 1
								pCont.particleIndex = i.x
								pCont.particleScale = rng
								pCont.particlePosition = [x,y,z] 
								pCont.setParticleMapping i.x 1 [rngUV,rngUV,0]
							)
						)
					)
				)
			")
			local pmat = StandardMaterial name:"pMat" 
			local ramp = Gradient_Ramp()  --(bitmaptexture filename:imgToVoxelDB.importedColorMap)
			ramp.Gradient_Ramp.Flag__1.color = [255,0,0]
			ramp.Gradient_Ramp.Flag__2.color = [0,255,0]
			ramp.Gradient_Ramp.Flag__3.color = [0,0,255]
			pmat.diffuseMap = ramp
			showTextureMap pMat on
			local pf = PF_Source Quantity_Viewport:100 Particle_Amount_Limit:1000000
			ParticleFlow.BeginEdit()
			pf.AppendAction (RenderParticles())
			local e1 = Event()
			e1.AppendAction (Birth Amount: (n^3) Emit_Start:0 Emit_Stop:0)
			e1.AppendAction (Script_Operator Proceed_Script: bScript)
			e1.AppendAction (ShapeLibrary '3D_Type':0 size:1)
			e1.AppendAction (Material_Static Assigned_Material:pMat)
			pf.appendInitialActionList e1
			ParticleFlow.EndEdit()
			mm = mesh name:"result" mesh:(snapshotasmesh (getnodebyname (pf.name + "->" + e1.name))) material:pmat
			particleFlow.BeginEdit()
			particleFlow.delete pf
			particleFlow.EndEdit() 	
		)
	)
	local timeStop = timestamp()
	print ("Time: " + (((timeStop - timeStart)/1000.0)as string) + " sec Mem: " + ((hf-heapfree) as string))
)
fn_createBoxSpeedTest()

getnodebyname (pf.name + "->" + e1.name)

is not a good way to find a ‘mesh container’.

to find a better one will be your homework

Maxscript :UV single mesh

delete objects
gc()
b = box width:10 height:10 lenght:10 mapcoords:true 
convertToMesh b
pmat = StandardMaterial name:"pMat" 
ramp = Gradient_Ramp() 
ramp.Gradient_Ramp.Flag__1.color = [255,0,0]
ramp.Gradient_Ramp.Flag__2.color = [0,255,0]
ramp.Gradient_Ramp.Flag__3.color = [0,0,255]
pmat.diffuseMap = ramp
showTextureMap pMat on
b.material = pMat
	
fn fn_uvSingleMesh inMesh = (
	local rng = (random 0.0 1.0)
	for i = 1 to (getNumTVerts inMesh) do (
		setTVert inMesh i [rng,rng,0]
	)
	update inMesh
)

fn_uvSingleMesh b

C# what am I doing wrong/missing here?

TMESH.SetNumTVerts(8 * nTotal, false);
TMESH.SetTVert(v + actualVert, rng, rng, rng);

C# version with UV (not working)

(
	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;

            IMesh TMESH = triNew.Mesh;

            // Setup the new TriObject with number of faces and verts
            TMESH.SetNumFaces(12 * nTotal, false, false);
            TMESH.SetNumVerts(8 * nTotal, false, false);
            TMESH.SetNumTVerts(8 * nTotal, false);

            IList<IFace> MESH_FACES = TMESH.Faces;

            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++)
                        {
                            TMESH.SetVert(v + actualVert, Verts[v][0] * rng + x, Verts[v][1] * rng + y, Verts[v][2] * rng + z);
                            TMESH.SetTVert(v + actualVert, rng, rng, 0);
                        }
                        for (int f = 0; f < 12; f++)
                        {
                            IFace FACE = MESH_FACES[f + actualFace];
                            FACE.SetVerts(Faces[f][0] + actualVert, Faces[f][1] + actualVert, Faces[f][2] + actualVert);
                            FACE.SetEdgeVisFlags(EdgeVisibility.Vis, EdgeVisibility.Vis, EdgeVisibility.Invis);
                            FACE.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
				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 15
		
	format "time:% heap:%
" (timestamp()-st) (sh-heapfree)
		
)

i guess it’s a limitation of pflow’s or particlesys’s number of particles per emitter

but anyway i don’t think that the using of a particle systems is the right way to clone meshes

maybe MCG can help somehow.

I’m actually very interested to see any example of do this kind of job with MCG help

more likely it’s only particle display limitation, but we are using in view render mesh to get the original mesh which is not right

Page 4 / 5