IBitArray EnumSet

 
 
 
Posted by:Vincentt
Data created:21 April 2015

Hello again,

This might be a bit of a long shot but I have noticed that in the Autodesk.max.dll from 2014+ the [] operator has gone missing from IBitArray. I know you no longer develop this so I’ve got a question about a workaround I was trying to use:

I wanted to use EnumSet(IBitArrayCallback cb) method. However I seem to be unable to create a valid class that inherits from IBitArrayCallback. I get a 'System.InvalidCastException' error: Unable to cast object of type 'TestNewAssembly.TestCallback' to type 'Autodesk.Max.IImplementable'.

IBitArray selVert = mesh.VertSel;
TestCallback test = new TestCallback();
selVert.EnumSet(test);

...

public class TestCallback : IBitArrayCallback
{
public void Proc(int n)
{}

public bool Equals(IBitArrayCallback other)
{return false;}

public void Dispose()
{}

public IntPtr NativePointer
{get { return IntPtr.Zero; }}
}

 

This is the same error as when I tried to inherit from IRestoreObj instead of Autodesk.Max.Plugins.RestoreObj. But I can’t find another (I)BitArrayCallback class to inherit from… I’ve checked the Max SDK samples and they only inherit Proc(int n) but I get Equals, Dispose and NativePointer too. So I have a feeling I’m inheriting from the wrong class again but don’t know which one is the correct one?

 

Also, do you know why the [] operator would’ve been removed from 2014+ .net SDK? The documentation does not mention this at all. Or am I missing something here?

 

Thanks again!

Vin

Hi Vin,

I did not realize that the operator was gone and I can see that it, indeed, is. I looked through the SDK and there has been a new __forceinline keyword added to operator[] in C++ which is what could be throwing the wrapper generator off. Do you think you could submit a defect to Autodesk regarding this?

I don't really know any other way of accessing the bits inside the bit array short of using unsafe code and looking through the pointer data yourself. IBitArrayCallback is not listed as a pluggable class so you cannot override it. However, please also submit a defect to Autodesk regarding this as it would relatively easy to make it pluggable.

Marsel Khadiyev (Software Developer, EPHERE Inc.)

Hey Marsel,

Thanks for the info, I was confused for a while when I couldn't find [], so I've submitted 2 defects to Autodesk: the missing [] operator and the IBitArrayCallback not being a pluggable class. Hopefully I'll hear back soon!

About the workaround, I know this has little to do with the wrapper itself, but would IBitArray.Handle be the starting point of the array in memory? I've tried casting it to an int* and then iterating over it like this:

 

int* start = (int*)bitArraySel.Handle.ToPointer();

for (int i = 0; i < bitArraySel.Size; i++)
{
     var test1 = bitArraySel[i];
     var test2 = start[i];
     //var test3 = *(start + i);
}

This isn't really working and I assume it's because I need to cast it to something else than int*. I assumed it stored ints as the [] returned ints and Marshal.SizeOf(bitArraySel[i]) returns 4, just like sizeof(int) does. 

You should try using bitArraySel.NativePointer instead of Handle, at least in newer versions of the wrapper.

Marsel Khadiyev (Software Developer, EPHERE Inc.)

Thanks! I've tried the NativePointer method too, unfortunately it's still not working. I was only using the .Handle because I was testing the SizeOf(bitArrayElement) using the [] that are now missing. 

I came up with this solution, got most of the code from the C++ SDK, if anyone is having similar problems... :) (Extension method: bitArray.GetBit(i))

public static class IBitArrayExtensions
{
private const int NSHIFT = 6;
private const int CHAR_BIT = 8;
private const int kMAX_LOCALBITS = CHAR_BIT*sizeof (ulong);

private const int BITS_PER_DWORD_PTR = (CHAR_BIT*sizeof (ulong));
private const int BITS_PER_DWORD_PTR_MASK = BITS_PER_DWORD_PTR - 1;

public static unsafe int GetBit(this IBitArray bitArray, int index)
{
//Number of bits in the bitArray
int numBits = bitArray.Size;

//Pointer to the start of either DWORD_PTR* bits or DWORD_PTR localBits
//DWORD_PTR is of type ulong
void* nativePtr = bitArray.NativePointer.ToPointer();

//Determines whether we use bits or localBits
bool useLocal = numBits <= kMAX_LOCALBITS;

if (useLocal)
{
ulong localBits = ((ulong*)nativePtr)[0];
ulong bitMask = (index < kMAX_LOCALBITS) ? (((ulong)1) << index) : 0;
return ((localBits & bitMask) != 0) ? 1 : 0;
}
else
{
ulong* bits = ((ulong**)nativePtr)[0];
int bitIndex = index >> NSHIFT;
var bitMaskParameter = index & BITS_PER_DWORD_PTR_MASK;
ulong bitMask = (bitMaskParameter < kMAX_LOCALBITS) ? (((ulong)1) << bitMaskParameter) : 0;
return ((bits[bitIndex] & bitMask) != 0) ? 1 : 0;
}
}
}

Wow, that is elaborate :) Congrats and sorry for not being able to help more

Marsel Khadiyev (Software Developer, EPHERE Inc.)

Hi Vincentt.

 

I have the same issue than you and need to solve it.

I've copied your code but I get this compilation error in this line "void* nativePtr = bitArray.NativePointer.ToPointer();": there's no NativePointer definition for IBitArray.

Do you know what I'm missing?

Thanks a lot for your help.

Hey aandres, I think in later versions of the wrapper the .Handle property has been changed to NativePointer. So maybe you could try and replace .NativePointer to .Handle

Correct, what is now NativePointer used to be Handle before. This was renamed to avoid name clashes.

Marsel Khadiyev (Software Developer, EPHERE Inc.)

Thanks a lot! It works fine with "handle". You save my code!

I'm using 3DSMax 2014. Is that IBitArray issue corrected in newer Max versions? It's really incredible a bug like that.

In 3dsmax 2013 and newer it should be NativePointer instead of Handle, with the Autodesk.Max.dll assembly which comes with 3dsmax

Marsel Khadiyev (Software Developer, EPHERE Inc.)

Not for me... Perhaps it's because it's the Design version (2014).

Here I am again with IBitArrays...

Is it just my 3DSMAX version or is it true that the OR operation doesn't exist for IBitArrays?? I just can find NOT, AND and XOR.

Anyone can help me to solve this new issue?

The only way I've found is: A OR B = B XOR (A XOR (A AND B))

Hmm, you're right, I can't seem to find or operator as well. All I see is:

//
// Summary:
// AND two BitArrays
IBitArray BitwiseAnd( IBitArray param0 );
//
// Summary:
// AND= this Autodesk.Max.IBitArray with the specified Autodesk.Max.IBitArray.
IBitArray BitwiseAndWith( IBitArray b );
//
// Summary:
// XOR two BitArrays
IBitArray BitwiseXor( IBitArray param0 );
//
// Summary:
// XOR= this Autodesk.Max.IBitArray with the specified Autodesk.Max.IBitArray.
IBitArray BitwiseXorWith( IBitArray b );

//
// Summary:
// Unary NOT function
IBitArray BitwiseNot { get; }

Marsel Khadiyev (Software Developer, EPHERE Inc.)

Yes.

What is worse is that the OR operation is the only way to add IBitArrays (thus add differents selections of faces o verts) as the '+' operator between IBitArrays is not implemented either. And it's not like in MaxScript, where you can use an array of integers to make a selection. The only way are IBitArrays.

If anyone has a better solution, then insert mine in Vincentt '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)));
}