Notifications
Clear all

[Closed] [maxscript] KDTree. Can we make it faster?

After a few restarts I see no slowdowns on the first start and it is strange. I remember it was and issue long ago and is probably related to JIT

Can you give an example of GeomObject? I’ll try to find a way to convert it
Ok, nevermind.

g.animatable.getanimbyhandle (asuptr (gethandlebyanim (createInstance teapot)))
–> dotNetObject:Autodesk.Max.Wrappers.GeomObject

I just thought, if you write c# assembly anyway, wouldn’t it be easier to write everything from mxs mesh to vertexoctree?

Well, yes, it makes more sense to implement this whole struct as a c# class and compile it on demand. This way we won’t need to use reflection at all and it should make things work a little bit faster.
I already did this kind of thing for geometry3sharp library. It has a lot more useful things inside.

fn make_mxs_wrappers_assembly = 
(	
	source = @"
		using System;
		using System.Collections.Generic;
		using System.Runtime.InteropServices;
		using Autodesk.Geometry3D;
		using Microsoft.Xna.Framework;
		using Autodesk.Max;
		using Autodesk.Max.Wrappers;
		using Autodesk.Sequences;

		namespace MxsWrappers
		{
			public class Methods
			{
				private static readonly IGlobal Global = Autodesk.Max.GlobalInterface.Instance;

				public static object GetIMesh(IntPtr mesh_pointer) 
				{ 
					var mesh = (Mesh)CustomMarshalerMesh.GetInstance(string.Empty).MarshalNativeToManaged(mesh_pointer);
					return mesh;
				}
				public static object GetIPoint3(IntPtr point3_pointer) 
				{ 
					var p = (Point3)CustomMarshalerPoint3.GetInstance(string.Empty).MarshalNativeToManaged(point3_pointer);
					return p;
				}
				public static object GetVector3(IntPtr point3_pointer) 
				{ 
					var p = (Point3)CustomMarshalerPoint3.GetInstance(string.Empty).MarshalNativeToManaged(point3_pointer);
					return new Vector3(p.X, p.Y, p.Z);
				}
				public static Octree BuildOctree(Mesh mesh)
				{
					var vecs = new List<Vector3>();
					foreach (var p in mesh.Verts) vecs.Add(new Vector3(p.X, p.Y, p.Z));

					var data = ImmutableArray.ToIArray(vecs);
					return new VertexOctree(data);
				}
				public static Octree BuildOctree(IntPtr mesh_pointer)
				{
					var mesh = (Mesh)CustomMarshalerMesh.GetInstance(null).MarshalNativeToManaged(mesh_pointer);
					return BuildOctree(mesh);
				}
			}
		}"
		
	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
	
	compilerParams.ReferencedAssemblies.AddRange \
		#(
			"System.dll",
			getdir #maxroot 	+ @"\Autodesk.Max.dll", 
			getdir #assemblies 	+ @"\Autodesk.Max.Wrappers.dll",
			getdir #maxroot 	+ @"\Geometry3D.dll",
			getdir #maxroot 	+ @"\ImmutableArray.dll",
			getdir #maxroot 	+ @"\Monogame.Framework.dll"			
		)
		
	compilerParams.GenerateInMemory = on
	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
	
	dotnetclass (compilerResults.CompiledAssembly.GetType "MxsWrappers.Methods")
)

c = make_mxs_wrappers_assembly()
oc = c.BuildOctree $.mesh

/*
c.GetIMesh $.mesh
c.GetIPoint3 [1,1,1]
c.GetVector3 [1,1,1]

pt = oc.FindClosestPoint (c.GetVector3 (getvert $.mesh 100))
pts = oc.FindNClosestPoints (c.GetVector3 (getvert $.mesh 100)) 20
*/

Wow, now it is so mush less code
I see that you found that post of Swordslayer about passing mxs values by their names. Do you know in which max version it was first added? Code works in 2016 max, but I don’t have 2015 to test

It isn’t very convenient to pass several args of the same type but it is doable.

public static object[] GetVector3Array(IntPtr[] point3_pointer) 
{ 	
	object[] vectors = new object[ point3_pointer.Length ];
	for(int i=0; i < point3_pointer.Length; i++ )
	{
		var p = (Point3)CustomMarshalerPoint3.GetInstance(string.Empty).MarshalNativeToManaged(point3_pointer[i]);
		vectors[i] = new Vector3(p.X, p.Y, p.Z);
	}
	return vectors;	
}

upd.
No matter what I do it leaks memory. Passing large arrays of mxs values to c# is probably not an option

Mem leaks
fn make_mxs_wrappers_assembly = 
(	
	source = @"
		using System;
		using System.Collections.Generic;
		using System.Runtime.InteropServices;
		using Autodesk.Geometry3D;
		using Microsoft.Xna.Framework;
		using Autodesk.Max;
		using Autodesk.Max.Wrappers;
		using Autodesk.Sequences;

		namespace MxsWrappers
		{
			public class Methods
			{
				private static readonly IGlobal Global = Autodesk.Max.GlobalInterface.Instance;
				private static readonly ICustomMarshaler Point3Marshaler = CustomMarshalerPoint3.GetInstance(string.Empty);
	
				public static object GetIMesh(IntPtr mesh_pointer) 
				{ 
					var mesh = (Mesh)CustomMarshalerMesh.GetInstance(string.Empty).MarshalNativeToManaged(mesh_pointer);
					return mesh;
				}
				public static object GetIPoint3(IntPtr point3_pointer) 
				{ 
					var p = (Point3)CustomMarshalerPoint3.GetInstance(string.Empty).MarshalNativeToManaged(point3_pointer);
					return p;
				}
				public static object GetVector3(IntPtr point3_pointer) 
				{ 
					using ( Point3 p = (Point3)Point3Marshaler.MarshalNativeToManaged(point3_pointer) )
					{
						return new Vector3(p.X, p.Y, p.Z);
					}
				}
				public static void GetVector3Array(IntPtr[] point3_pointer, ref Vector3[] vectors) 
				{ 	
					// nothing happens here, but leak still exists
					point3_pointer = null;
					vectors = null;
				}
				public static void SetNull( object val ) 
				{ 	
					val = null;
				}
				public static Octree BuildOctree(Mesh mesh)
				{
					var vecs = new List<Vector3>();
					foreach (var p in mesh.Verts) vecs.Add(new Vector3(p.X, p.Y, p.Z));

					var data = ImmutableArray.ToIArray(vecs);
					return new VertexOctree(data);
				}
				public static Octree BuildOctree(IntPtr mesh_pointer)
				{
					var mesh = (Mesh)CustomMarshalerMesh.GetInstance(null).MarshalNativeToManaged(mesh_pointer);
					return BuildOctree(mesh);
				}
			}
		}"
		
	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
	
	compilerParams.ReferencedAssemblies.AddRange \
		#(
			"System.dll",
			getdir #maxroot 	+ @"\Autodesk.Max.dll", 
			getdir #assemblies 	+ @"\Autodesk.Max.Wrappers.dll",
			getdir #maxroot 	+ @"\Geometry3D.dll",
			getdir #maxroot 	+ @"\ImmutableArray.dll",
			getdir #maxroot 	+ @"\Monogame.Framework.dll"			
		)
		
	compilerParams.GenerateInMemory = on
	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
	
	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:% %\n" err.ErrorNumber err.Line err.Column err.ErrorText to:errs
		)
		format "%\n" errs
		
		return undefined
	)
				
	dotnetclass (compilerResults.CompiledAssembly.GetType "MxsWrappers.Methods")
)

if ::c == undefined do
(
	c = make_mxs_wrappers_assembly()
-- 	oc = c.BuildOctree $.mesh
	s = GeoSphere segments:100
	tri = snapshotAsMesh s
	::pts = for i=1 to tri.numverts collect getvert tri i
)
(
	t1=timestamp();hf = heapfree
	
	format "Passing % points to c#\n" pts.count
	vecs = dotNetObject "microsoft.xna.framework.vector3[]" pts.count
	c.GetVector3Array pts vecs -- comment this line to eliminate mem leak. Each run it eats about 7Mb ram
	c.SetNull vecs	
	(dotnetclass "system.gc").collect()
	vecs = undefined
	gc()
	
	format "Time: %sec. Mem: %\n" ((timestamp()-t1)/1000 as float) (hf-heapfree)
)

look what I found


delete objects
gc()

tea = Teapot()
s   = sphere radius:2 wirecolor:red pos:(2 * tea.radius * normalize [random 0.0 1.0,random 0.0 1.0,random 0.0 1.0])
rotate tea (EulerAngles (random 0 360) (random 0 360) (random 0 360))
tri = tea.mesh
wrapper = (dotNetClass "Autodesk.Max.MaxPlus.Mesh")._createwrapper tri
p3_wrapper = (dotNetClass "Autodesk.Max.MaxPlus.Point3")._createwrapper (s.pos * inverse tea.objecttransform)
Geometry3D_TriMesh = (dotNetClass "Viper3dsMaxBridge.Converters").ConvertToViperValue wrapper
Geometry3D_Vector3 = (dotNetClass "Viper3dsMaxBridge.Converters").ConvertToViperValue p3_wrapper

octree_ops = dotNetClass "Autodesk.ViperGeometry3D.ViperGeometryOps+OctreeOps"

closest_pt = octree_ops.ClosestPointOnSurface Geometry3D_TriMesh Geometry3D_Vector3
MaxPlus_Point3 = (dotNetClass "Viper3dsMaxBridge.Converters").VectorToPoint closest_pt


pt = [MaxPlus_Point3.x,MaxPlus_Point3.y,MaxPlus_Point3.z]
pt *= tea.objecttransform 
point pos:pt centermarker:on cross:off wirecolor:yellow

what is Viper?

it is the MCG engine

In fact, MCG has a lot of useful things … unlike MCG itself. I have long thought to steal them from there.

Time has come, but AD said that they will abandon MaxPlus, but I hope they won’t remove these mxs to mcg converters

Little embree offtop

1.000 mesh ray intersections time:
Time: 0.052sec. Mem: 345048L – embree
Time: 13.88sec. Mem: 107884L – intersectRay

(
delete objects
gc()

tea = Teapot segments:10
rotate tea (EulerAngles (random 0 33) (random 0 33) (random 0 360))	
tea_itm = inverse tea.objecttransform
tea_tm  = tea.objecttransform
tri = tea.mesh

wrapper = (dotNetClass "Autodesk.Max.MaxPlus.Mesh")._createwrapper tri
Geometry3D_TriMesh = (dotNetClass "Viper3dsMaxBridge.Converters").ConvertToViperValue wrapper

ray_ops = dotNetObject "Autodesk.ViperGeometry3D.ViperGeometryOps+FaceRayIntersectionOps"
ray_scene = ray_ops.RayTraceScene false	
tuple = ray_ops.RayTraceAddGeometry ray_scene Geometry3D_TriMesh


ConvertToViperValue = (dotNetClass "Viper3dsMaxBridge.Converters").ConvertToViperValue
GetRayWrapper = (dotNetClass "Autodesk.Max.MaxPlus.Ray")._createwrapper
	
fn MakeRay pos dir tm =
(
	ConvertToViperValue (GetRayWrapper (ray (pos * tm) (dir * tm)))
)	

t1=timestamp();hf = heapfree

ray_opsRayTraceFaceIntersection = ray_ops.RayTraceFaceIntersection

seed 321321
pts = for i = 1 to 1000 collect
(
	ray_pos = (random tea.min tea.max) * 0.75
	ray_pos.z = 150

	intersection = ray_opsRayTraceFaceIntersection ray_scene (MakeRay ray_pos -z_axis tea_itm)	
	
-- 	if intersection.IsValidHit then [ intersection.HitCoordinates.x, intersection.HitCoordinates.y, intersection.HitCoordinates.z ] * tea_tm else dontCollect
	
)

format "Time: %sec. Mem: %\n" ((timestamp()-t1)/1000 as float) (hf-heapfree)

t1=timestamp();hf = heapfree
seed 321321
pts = for i = 1 to 1000 collect
(
	ray_pos = (random tea.min tea.max) * 0.75
	ray_pos.z = 150

	intersection = intersectRay tea (ray ray_pos -z_axis)
	
-- 	if intersection.IsValidHit then [ intersection.HitCoordinates.x, intersection.HitCoordinates.y, intersection.HitCoordinates.z ] * tea_tm else dontCollect
	
)
format "Time: %sec. Mem: %\n" ((timestamp()-t1)/1000 as float) (hf-heapfree)

)
1 Reply
(@denist)
Joined: 10 months ago

Posts: 0

MaxPlus Python is deprecated… C# stays as I know

Page 5 / 6