Notifications
Clear all

[Closed] 3ds max sdk .NET

Need help from experts…

After several days working with sdk.NET for manipulating meshes, I have found that I can’t index IBitArrays!!! It seems it’s a new bug appeared in 2013-2014 version.
If I can’t get what vertex or faces are selected, it’s the end. I can’t go on.

I’ve found a trick in this forum from Vincentt user: https://www.ephere.com/autodesk/max/forums/general/thread_2527.html
where he creates an extension ‘GetBit’ for IBitArrays.

But sadlely, I get a compiling error in line: “void* nativePtr = bitArray.NativePointer.ToPointer();” : there’s no ‘NativePointer’ definition for IBitArray

Anyone knows how to solve this or do I have to lose all these days of coding?
I can’t find any workaround to extract information from an IBitArray.

string s = ManagedServices.MaxscriptSDK.ExecuteStringMaxscriptQuery(“(polyop.getvertselection selection[1] as array)as string”);

Not sure if that fits your needs.
Maybe there’s a way to convert IBitarray to array or smth like it, i couldnt find.

Thanks Sergey.
Sadlely, the mesh is something manipulated inside the C# code. And it doesn’t belong to any node or editable_mesh or poly, so it hasn’t a ‘handleByAnim’ value neither to use it with a MaxScript call.
All I think about will lose all performance. It’s really a shame.

I am not sure, but it could be:

bitArray.[b]Handle[/b].ToPointer()

I think Handle was then renamed to NativePointer in newer Max versions.
Anyway, it is a mess to work with Bitarray in C#.

1 Reply
(@aaandres)
Joined: 11 months ago

Posts: 0

Hey Jorge! Thanks a lot! It works fine.
The problem is that, as far as I know, working with IBitArrays is a must. You can’t work with arrays as in Maxscript for selections.
This extension is a mystery for me… but it works!

Once again, problems with IBitArray in SDK.NET

There’s no the OR operation between IBitArrays (or at least, I can’t find it). There’s just NOT, AND and XOR, and some reverse, rotation and shift operations.
How can I do an OR operation with these other ones??!! Is it possible?

Thanks in advance.

The way I’ve found, but I’m sure there’s a better one is:

A OR B = B XOR (A XOR (A AND B))

If anyone has a better solution, then my IBitArray extension is:

    public static class IBitArrayExtensions
    {
        public static IBitArray BitwiseOR(this IBitArray A, IBitArray B)
        {
            int sizeA = A.Size;
            int sizeB = B.Size;

            if (sizeA > sizeB)
            {
                B.SetSize(sizeA, 1);
            }
            else
            {
                A.SetSize(sizeB, 1);
            }

            return B.BitwiseXor(A.BitwiseXor(A.BitwiseAnd(B)));
        }
    }

Use it like: A.BitwiseOR(B) to add B IBitArray to A IBitArray.

P.S.: to much code to get an OR operation!!!

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

public Object ORBits(IBitArray a, IBitArray b)
{
        return a.BitwiseNot.BitwiseAnd(b.BitwiseNot).BitwiseNot;
}

in theory it’s only one boolean operation. BitwiseNot is hopefully a cached object

Have anyone asked Autodesk where they have lost BitwiseOr?

Perhaps it can help someone:
Function to retrieve values from a generic local/global MaxScript bidimensional arrray.
For example: myVar = #(#(“hello”, 1, true),#(“goodbye”, 2, false), #(“regards”, 5, true))


using System;
using Autodesk.Max;

namespace Proin3D
{
    class SDKNETUtilities
    {

        /// <summary>
        /// Getting local MXC variables: generic bidimensional array
        /// [Ex.: myVar = #(#("hello", 1, true),#("goodbye", 2, false), #("regards", 5, true)) ]
        /// </summary>
        /// <typeparam name="T"></typeparam>    type of the value to retrieve (string, int, float, bool...)
        /// <param name="localVarName"></param> the name of the local variable in MXS
        /// <param name="elementIndex"></param> first index of the bidimensional array (zero-based) [element]
        /// <param name="valueIndex"></param>   second index of the bidimensional array (zero-based) [value]
        /// <returns></returns>                 returned value will be type found (string, int, float, bool...)
        ///                                     converted to type 'T' if possible. ERROR IF CONVERSION IS NOT POSSIBLE
        ///                                     OR defaullt 'T' VALUE if index out of range.
        /// By Andrés Fernández - Proin3D - April 2016     
        ///                                
        static public T GetMxsBiDimensionalLocalVariable<T>(string localVarName, int elementIndex, int valueIndex)
        {
            IFPValue IFPvar1 = GlobalInterface.Instance.FPValue.Create();
            GlobalInterface.Instance.ExecuteMAXScriptScript(localVarName, false, IFPvar1);

            if (IFPvar1.Type != ParamType2.FpvalueTabBv) return default(T);

            ITab<IFPValue> IFPvarMain = IFPvar1.FpvTab;
            if (elementIndex > IFPvarMain.Count-1 || elementIndex < 0) return default(T);

            IFPValue IFPvar2 = IFPvarMain[(IntPtr)elementIndex];
            if (IFPvar2.Type != ParamType2.FpvalueTabBv) return default(T);
                
            ITab<IFPValue> IFPvarElement = IFPvar2.FpvTab;
            if (valueIndex > IFPvarElement.Count - 1 || valueIndex < 0) return default(T);

            IFPValue IFPvar3 = IFPvarElement[(IntPtr)valueIndex];
            ParamType2 ptype = IFPvar3.Type;

            switch (ptype)
            {
                case ParamType2.Bool:
                case ParamType2.Bool2:
                    return (T)System.Convert.ChangeType(IFPvar3.B, Type.GetTypeCode(typeof(T)));

                case ParamType2.PcntFrac:
                case ParamType2.Float:
                    return (T)System.Convert.ChangeType(IFPvar3.F, Type.GetTypeCode(typeof(T)));

                case ParamType2.Double:
                    return (T)System.Convert.ChangeType(IFPvar3.Dbl, Type.GetTypeCode(typeof(T)));

                case ParamType2.Int:
                    return (T)System.Convert.ChangeType(IFPvar3.I, Type.GetTypeCode(typeof(T)));

                case ParamType2.Int64:
                case ParamType2.Intptr:
                    return (T)System.Convert.ChangeType(IFPvar3.Intptr, Type.GetTypeCode(typeof(T)));

                case ParamType2.String:
                    return (T)System.Convert.ChangeType(IFPvar3.S, Type.GetTypeCode(typeof(T)));

                default:
                    return default(T);
            }
        }
    }
}

Call it inside your C# code:
bool theValue = GetMxsBiDimensionalLocalVariable<bool>(“myVar”, 1, 2);
OR
int theValue = GetMxsBiDimensionalLocalVariable<bool>(“myVar”, 0, 1);
As allways, “use it at your own risk”.

P.S.: It would be fine if someone implements a throwException when conversion is not possible (I’m still haven’t searched for this in C#)

So grateful for this thread, maybe we can make some sense of the magical and whimsical C# in Max. aaandres and Vincentt already enlightened us with some basic (and yet still poorly implemented in native Max libraries) BitArray methods, and since I’m learning C# along with the SDK for my job I wanted to expand on it a bit.

What I’m trying to do is to understand how to use some Mesh or MNMesh (poly) methods in C#. The library I’m writing is dealing with some basic mesh operations like info gathering, comparison, smart welding etc. For that I’m trying to use some mesh methods, like ElementFromFace etc. Let’s focus on that for a bit (you’ll see the pun come into play in a little while ;)). Note that I’m an absolute C# beginner, but I’m trying my damnest to learn this stuff, and jumping from Python and MSX I’m terrorized by the unfriendly docs and lack of examples.

Autodesk.Max.IMNMesh.ElementFromFace(int, Autodesk.Max.IBitArray) definition:

Sets bits for all faces in the same “element”, or connected component, with face ff. Faces already selected in fset will be considered “walls” for this processing and will not be evaluated. That is, if ff is not selected, but there’s a ring of faces around it that is, the algorithm will stop at that ring. int ff The zero based index of the face. & fset The bits of the faces in the element are selected in this array

Okay, so let’s say I have a face index and a BitArray with all faces, cleared:

int faceIndex = 0;
IBitArray eleBits = global.BitArray.Create(mesh.Numf);

and we want to select those element faces (just to test the function out!) in that mesh, probably using:

mesh.FaceSelect(eleBits);

which means we need to set the proper bits in the eleBits BitArray, so:

mesh.ElementFromFace(faceIndex, eleBits);

but there’s a catch – ElementFromFace returns void and doesn’t seem to modify eleBits BitArray in no manner. That’s a bummer, cause I try to use a method with no result. I thought I could then iterate through all faces and get the flags/bits, something like:

for (int fID = 0; fID < mesh.Numf; fID++)
{
    IBitArray getBits = global.BitArray.Create(mesh.Numf);
    IMNFace f = mesh.F(fID);
    if (f.GetFlag(Convert.ToUInt32(allPolys[0])))
    {
        getBits.Set(fID);
    }
}
mesh.FaceSelect(getBits);

but it also returns a cleared (all bool False) BitArray, thus selecting nothing. I suppose there are plenty more methods like this one, and I’m really scratching my head as how to properly use them. Sorry for the lengthy message, hope you didn’t mind it. Any ideas?

For this specific task, among others, it might be easier to use IObjectWrapper.

<IObjectWrapper>.FaceSel

It provides a unified interface to work with both Mesh and MNMesh.

Also, some methods return a referenced IBitArray, while other returns a new IBitArray. So sometimes you have to assign the modified IBitArray back where you need it, and other times modifying it will have immediate effect from where you got it.

BTW, working with BitArrays is not the only problem of the C# wrapper. It is supposed that it should be version independent, but it is not. For many properties, methods, interfaces, etc. you will have to compile a different assembly for each Max version. Sad.

Page 2 / 4