[.NET SDK 2024] IMesh & IMNMesh missing properties


In 3ds Max 2024, some properties, like Verts, Faces, etc. have been removed from IMesh and IMNMesh.

"The Mesh and MNMesh classes have been refactored to have internal data organized into channel objects. This provides improved stack performance, stability and a lot of code cleanup and modernization. The goal was no functional change to these meshes and minimal API changes to avoid ifdefs.

For example, you can access mesh->faces[f] and alter the faces but you cannot write to the faces member itself. If you want to do this, you still can but it’s not adviced."

The problem is that in this refactoring, those properties are completely gone in the NET SDK, so in order to make any old project work, it must be rewritten specifically for this latest version.

We are supposed to get the Verts, Faces, etc., from the ChannelDataContainer, so we can get the IMeshChannelContainer for IMesh, but that is the furthest I could get.

Do you know how to get any of the channels data (MeshTopoChannelData, MeshGeomChannelData, MeshVertexColorChannelData, etc.) from the ChannelDataContainer?



they’ve gone from the sdk c++ too

you have to use the getters and setters not direct access.

so to get the verts ptr you use in C++

Point3* verts = mesh.getVertPtr (0);


int nVerts=mesh->getNumVerts();

instead of

int nVerts=mesh->numVerts;

going by meshop.cpp in the mxsangi project there is still direct access to faces (there has never been a getter of the mesh faces ptr btw).

though going by meshop.cpp they are still using older code I don’t know what have going on for it to work and I’d rather not have to load the uber project nonsense they have going on with the sdk to find out.


Thank you @Klvnk for taking the time.

That’s the path I am going to follow, just getting the pointers. Hopefully, everything will remain in a contiguous memory block. However, I am not sure how future updates will affect this.

Still, I see not clear way to implement what the documentation suggests, and as you said, even none of the examples in the SDK uses this new approach.

I wonder, was it really necessary to implement it this way? Couldn’t the old properties and the new ChannelDataContainer coexist?


I don’t know whats going on

from… what’s new

The public mesh members of Mesh and MNMesh that are not owned by channel objects have been either replaced by getter/setter or wrapped with “BlockWrite” objects. The purpose of these is to block the ability to write to these members directly because it isn’t Mesh that owns the data, they are simply mirrors. This should not be too disruptive.

For example, you can access mesh->faces[f] and alter the faces but you cannot write to the faces member itself. If you want to do this, you still can but it’s not adviced. See documentation for Mesh::GetChannelDataContainer() and MNMesh::GetChannelDataContainer() in the API Reference.

leads to…

To modify a Mesh channel directly, instead of using the Mesh interface, you can fetch the MeshChannelContainer, then grab the appropriate channel object and make the changes directly. This is not recommended, mistakes here could cause crashes and data loss. Example to manually reallocate the Mesh faces:

so modifiy anything with either method at your own risk ???

so the only way to “access” mesh.faces ptr directly is via Mesh::GetChannelDataContainer() but doing that is not advised ?! :confused:

so I’m guessing something like…

Face* faces = mesh.faces;
for(int f = 0; f < mesh.getNumFaces  ( ); ++f, ++faces)
       DWORD * fverts = faces->v;

is “verboten”

where as

for(int f = 0; f < mesh.getNumFaces  ( ); ++f)
       DWORD * fverts = mesh.faces[f].v;

would be ok (as they’ve done some custom operator wrappage)

wonder what

Face* faces = &mesh.faces[0];

does ?

looking at the source you should be able to cast to type :confused:


are all shown as snippets in mesh.h

sorry PolyTools this doesn’t really help you that much :frowning:


Thank you for your input, I greatly appreciate it. Unfortunately, there seems to be no easy way out of this unnecessary mess.

These properties have been there for decades, and suddenly they become a threat? The very core of any 3d software, meshes vertices, faces and mapping. :astonished:

I wonder if there wasn’t there any better place to put the fingers on.

If things don’t look too good in C++, you don’t want to imagine how frustrating is to work with the .NET API, it’s like 80% just trying to decipher how things work, and once you got there, there is another 80% of chances that something will break along the way.

Moral: do you want to have a .NET API? code your own wrapper.:roll_eyes:

But hey, don’t complain, we’ve got a build in antivirus. :sunglasses:


have you looked at this…

Update sample to 2024. Includes fix to IMesh where underlying C++ SDK changed.


Nothing there but the obvious, GetFace(), GetVert(), etc., but no mention to the new Interface.

The ADN .NET examples are mostly things you can do in MXS in half the lines, and most probably with better performance. Besides that, they approach are very simple tasks, not the ones a developer would look for really.

I don’t know, perhaps the .NET API is slowly dying. :anguished:


that’s disappointing :frowning:

“off the cuff” what about asking chatgpt it might not get it right but it’s internet trolling powers might find something that can point you in the right direction.


And miss all the fun trying to “crack” this beast? No, that’s for the new generations.

I’ll stick to the hammer and chisel, what can go wrong?

Although, someone could ask it to write a few good examples using the .NET API. :thinking:


I wouldn’t understand what I was looking at :smile: I find c# odd and unnecessarily obtuse :wink: It’s c++ but not :slight_smile:


I’m on win7 so can’t even install 2024 to try, unfortunately.
Are you sure these props are gone or maybe they’re now just private?


they are not private, just changed

MaxSDK::BlockWrite_Ptr<Face, MaxSDK::MeshTopoChannelData> faces;

where BlockWrite_Ptr is defined as

template<typename T, typename ChannelType >
class BlockWrite_Ptr : public MaxHeapOperators
	friend ChannelType; /*Grant write access to the channel who owns the pointer value.*/
	BlockWrite_Ptr() : mPtr(nullptr) {}
	virtual ~BlockWrite_Ptr() = default;
	BlockWrite_Ptr(BlockWrite_Ptr&) = delete;
	BlockWrite_Ptr(BlockWrite_Ptr&&) = delete;
	BlockWrite_Ptr& operator=(const BlockWrite_Ptr&) = delete;
	BlockWrite_Ptr& operator=(BlockWrite_Ptr&&) = delete;
	BlockWrite_Ptr& operator+=(BlockWrite_Ptr&&) = delete;
	BlockWrite_Ptr& operator-=(BlockWrite_Ptr&&) = delete;
	BlockWrite_Ptr& operator++() = delete;
	BlockWrite_Ptr& operator--() = delete;
	operator T* () const { return mPtr; } //!< Auto converts to pointer type in most cases. May need to type cast.
	T* operator->() const { return mPtr; }
	T& operator*() const { return *mPtr; }
	T& operator[](size_t index) const { return mPtr[index]; }
	//! \brief Pointer to data that is owned elsewhere.
	T* mPtr;

operator T* () const { return mPtr; }

enable the casting to work, though should work without the cast :confused:


You can install VirtualBox(for example), then Win10 on it and max2024. This is how I tested it.


might be worth starting a thread on the Autodesk forum, you might get lucky and get someone from the dev team though it’s pretty rare.


Yes, they are not even present in the Wrapper. Here is a list of IMesh and IMNMesh missing Properties and Methods Max 2022 and Max 2024:

Present in 2022 - missing in 2024

int NumVerts_ { get; set; }
int NumFaces_ { get; set; }
IList<IFace> Faces { get; }
IList<IPoint3> Verts { get; }
int NumTVerts_ { get; set; }
IList<IPoint3> TVerts { get; }
IList<ITVFace> TvFace { get; }
int NumCVerts { get; set; }
IList<IPoint3> VertCol { get; }
IList<ITVFace> VcFace { get; }
int NumMaps_ { get; set; }
IList<IMeshMap> Maps { get; }
IBitArray VdSupport { get; set; }
IPerData VData { get; set; }
IBitArray VertSel_ { get; set; }
IBitArray FaceSel_ { get; set; }
IntPtr VFlags { get; set; }
void ZeroTopologyCache();

Present in 2024 - missing in 2022

int NumVDataSupported { get; }
IMeshChannelContainer ChannelDataContainer { get; }
int GetVFlags(int i);
IFace GetFace(int i);
IFace GetFacePtr(int i);
int GetNumMapFaces(int mp);
IPerData GetVD(int vdChannel);
void AcquireChannelLocks(uint channels);
void ReleaseChannelLocks(uint channels);

Present in 2022 - missing in 2024

IMNVert V_ { get; set; }
IMNEdge E_ { get; set; }
IMNFace F_ { get; set; }
int Numv { get; set; }
int Nume { get; set; }
int Numf { get; set; }
int Numm { get; set; }
IPerData Vd { get; set; }
IBitArray VdSupport { get; set; }
IPerData Ed { get; set; }
IBitArray EdSupport { get; set; }
ITab<int> Vedg { get; set; }
ITab<int> Vfac { get; set; }
void MAlloc(int num, bool keep);
void MShrink(int num);

Present in 2024 - missing in 2022 -*some Methods have overloads

ITab<int> VEdge { get; }
ITab<int> VFac { get; }
int VDNumSupported { get; }
int EDNumSupported { get; }
uint ExportFlags { get; }
IMNChannelContainer ChannelDataContainer { get; }
IPerData GetVD(int vdChannel);
IPerData GetED(int vdChannel);
void SetFlag(uint fl, bool val);
void ClearFlag(uint fl);
bool GetFlag(uint fl);
void ClearAllFlags();
void CopyFlags(uint fl);
void CopyFlags_(IMNMesh mesh);
void OrFlags(IMNMesh mesh);
void OrFlags_(IMNMesh mesh);
void AndFlags(IMNMesh mesh);
void AndFlags_(IMNMesh mesh);
bool FlagMatch(uint fmask, uint fl);
bool FlagMatch_(uint fmask, IMNMesh mesh);
void ImportFlags(uint fl);
IOResult WriteFlags(IISave isave, uint nb);
IOResult ReadFlags(IILoad iload, uint nb);
void AcquireChannelLocks(uint channels);
void ReleaseChannelLocks(uint channels);


For a good meal you will need:

  • A bit of pointers
  • A bit of custom marshallers
  • A bit of custom structures
  • A bit of reflection
  • Casting, casting, casting…
  • A lot of crossed fingers…

Things seems to be cooking pretty good so far.


Classic :slight_smile: