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
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)
)