Sdk basic modifier


#1

I’m trying to build a basic multi slice modifier.

I’m a bit confused about the trimesh and mnmesh classes. If I use a triobject the slice function works fine no issues but if I apply a poly mnmesh slice the plugin will crash if I interact with it. I use an if subojbectclass so it isn’t the obvious incompatible node type issue.

Do I need to convert to a trimesh then convert to mnmesh? These object types are a little confusing to the novice. Also how would one go about getting node bounding box size ie height on zaxis. Maxscript is a simple node.max, min.

Thanks guys.


#2

post the code and we can comment otherwize it’s a wild guess, I’m going with “it’s witches” :slight_smile:


#3

Sorry, forgot to add it.

//SLICE FUNCTION
void TimberBoarding_mod::SlicePolyObject(MNMesh & mesh, Point3 & N, float offset) {

mesh.SetFlag(MN_MESH_TEMP_1);
mesh.Slice(N, offset, 0, false, true);
mesh.ClearFlag(MN_MESH_TEMP_1);

}

/*************************************************************************************************
*
ModifyObject will do all the work in a full modifier
This includes casting objects to their correct form, doing modifications
changing their parameters, etc
*
************************************************************************************************/

//MAIN MOD ACTION ON MESH
void TimberBoarding_mod::ModifyObject(TimeValue t, ModContext& mc, ObjectState* os, INode* node)
{
//#pragma message(TODO(“Add the code for actually modifying the object”))
/**/
Interval valid = GetValidity(t);

//SEE CHANLED USED
valid &= os->obj->ChannelValidity(t, GEOM_CHAN_NUM);
valid &= os->obj->ChannelValidity(t, TOPO_CHAN_NUM);

//operations on mesh
float tention;
pblock->GetValue(pb_spin, t, tention, valid);


if (os->obj->CanConvertToType(polyObjectClassID))
{
	PolyObject *triObj = (PolyObject *)os->obj->ConvertToType(t, polyObjectClassID);
	MNMesh &mesh = triObj->GetMesh();

	//error on slice function
	SlicePolyObject(mesh, Point3(0.0f, 0.0f, 1.0f), tention);
}

//update velidity
NotifyDependents(Interval(t, t), PART_GEOM &PART_TOPO, REFMSG_CHANGE);
os->obj->UpdateValidity(PART_GEOM &PART_TOPO, valid);
//END

}


#4

this is how i would lay it out (it’s not tested)

void TimberBoarding_mod::ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node)
{
	Object *obj = os->obj;
	if(!obj) return;

	Interval iv = FOREVER;
	iv &= obj->ChannelValidity(t, GEOM_CHAN_NUM);
	iv &= obj->ChannelValidity(t, TOPO_CHAN_NUM);

	TimberBoarding_mod_params p;
	GetParams(p,t,iv);

	if(obj->IsSubClassOf(triObjectClassID))  // Handle any triobject types
	{
		ModifyTriObject((TriObject*)obj, p.normal, p.offset);
	}
	else if(obj->IsSubClassOf(polyObjectClassID)) // Handle any PolyObject types
	{
		ModifyPolyObject((PolyObject*)obj, p.normal, p.offset);
	}
	else if(obj->CanConvertToType(triObjectClassID)) // all others should try to convert to a triobject, 
	{
		TriObject  *triOb = (TriObject *)obj->ConvertToType(t, triObjectClassID);
		os->obj = triOb; // Now stuff this into the pipeline!
		ModifyTriObject(triOb, p.normal, p.offset);
	}
	obj->UpdateValidity(GEOM_CHAN_NUM, iv);
	obj->UpdateValidity(TOPO_CHAN_NUM, iv);
}

void TimberBoarding_mod::ModifyTriObject(TriObject* tobj, Point3 &normal, float offset)
{
	Mesh& mesh = tobj->GetMesh();
	SliceMesh(mesh, normal, offset); 
)  

void TimberBoarding_mod::ModifyPolyObject(PolyObject* pobj, Point3 &normal, float offset)
{
	MNMesh& pmesh = pobj->GetMesh();
	pmesh.Slice(normal, offset, 0.0, false, true);
}

#5

Perhaps this is not a matter of substance … but why do you write such a plugin on c++?


#6

if you want to use the “superior” poly cut for all object types then you handle it like this…

static PolyObject* GetPipeAsPoly(TimeValue t, Object *pipe)
{
	PolyObject *poly = NULL;
	if(pipe->IsSubClassOf (polyObjectClassID)) 
		poly = static_cast<PolyObject *>(pipe);
	else 
	{
		if (pipe->CanConvertToType(polyObjectClassID))
			poly = static_cast<PolyObject *>(pipe->ConvertToType(t, polyObjectClassID));
		else 
		{
			TriObject* tri = static_cast<TriObject *>(pipe->ConvertToType(t, triObjectClassID));
			poly = static_cast<PolyObject *>(tri->ConvertToType(t, polyObjectClassID));
			if (tri != pipe) 
				tri->DeleteThis();
		}
	}
	return poly;
}


void TimberBoarding_mod::ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node) 
{
	Object *obj = os->obj;
	if(!obj) return;

// get our params

	Interval iv = FOREVER;

	TimberBoarding_mod_params p;
	GetParams(p, t, iv);

// start the validity 

	iv &= obj->ChannelValidity(t, TOPO_CHAN_NUM);
	iv &= obj->ChannelValidity(t, GEOM_CHAN_NUM);

// we need the pipeline to be editable poly

	PolyObject *polyobj = GetPipeAsPoly(t, os->obj);
	if(polyobj != os->obj) // new object push it into the pipe
	{
		os->obj = polyobj;
		os->obj->UnlockObject();
	}

// get the mesh and slice

	MNMesh& pmesh = polyobj->GetMesh();
	pmesh.Slice(p.normal, p.offset, 0.0, false, true);

    obj->UpdateValidity(GEOM_CHAN_NUM, iv);
obj->UpdateValidity(TOPO_CHAN_NUM, iv);
}

#7

what is the reason for this?


#8

some types don’t convert directly to poly so you need an interim step i guess. It’s copied directly from tessmod.cpp


#9

Hi Klvnk, really appreciate all the help, fantastic!! I’ve tried the fist suggestion but still the same error crashing on mnmesh.Slice() function, bit stange.

I will try your second poly convert suggestion.

@denisT

This will eventually be a plugin that slices an object x number of times in z axis then will extrude and bevel faces. Its something that we already use as a script but as a learning experience I wanted to turn it into an interactive modifier.

so the goal is to basically make this maxscript code into an interactive modifier. Its not easy but I’m learning loads!!

SliceMin = $.min
SliceMax = $.max
Xmins = SliceMax.x-SliceMin.x
XEnteredNo = 10
XBoardNo = Xmins/XEnteredNo
xchops = XBoardNo -- number of slices

slicelen = slicemax-slicemin
slicelen = slicelen/xchops

--make slices
for i = 1 to (xchops-1) do
(
slicepoint = [(slicemin.x+(i*slicelen.x)), (slicemin.y+(i*slicelen.y)), (slicemin.z+(i*slicelen.z))]
$.slice [0,0,1] (slicepoint-$.pos)
)

--poly ops after slicing
max modify mode
subobjectLevel = 2


$.edgeExtrudeHeight = 4 *-1
$.edgeExtrudeWidth = 12

$.EditablePoly.buttonOp #Extrude
$.EditablePoly.SelectEdgeRing ()
max create mode

polyop.chamferEdges $ #selection 3

#10

make sure you have the following set

ChannelMask ChannelsUsed()		{ return GEOM_CHANNEL|TOPO_CHANNEL|TEXMAP_CHANNEL|SELECT_CHANNEL|SUBSEL_TYPE_CHANNEL|VERTCOLOR_CHANNEL; } 
ChannelMask ChannelsChanged()	{ return GEOM_CHANNEL|TOPO_CHANNEL|SELECT_CHANNEL|TEXMAP_CHANNEL|VERTCOLOR_CHANNEL; }

IIRC editpoly cutting routines will start to add mapping and vertex color verts and it causes an issue if the channel are not set… you should add them to the ChannelValidity and UpdateValidity calls


#11

i didn’t meet them… it would be interesting to know which one


#12

Great Klvnk, ChannelMask fixed it!!

I couldn’t get the GetPipeAsPoly function to work. I tested with a break, it executed into this part of the function.

	if (pipe->CanConvertToType(polyObjectClassID))
		poly = static_cast<PolyObject *>(pipe->ConvertToType(t, polyObjectClassID));
	else

so should have returned a PolyObject correct? Then when applying to editmesh in max nothing happens. Only if it is converted to a editpoly.

void TimberBoarding_mod::ModifyObject(TimeValue t, ModContext& mc, ObjectState* os, INode* node)
{
	Interval valid = GetValidity(t);
	Object *obj = os->obj;
	if (!obj) return;

	Interval iv = FOREVER;
	iv &= obj->ChannelValidity(t, GEOM_CHAN_NUM);
	iv &= obj->ChannelValidity(t, TOPO_CHAN_NUM);

	float tention;
	pblock->GetValue(pb_spin, t, tention, valid);
	Point3 axis = Point3(0.0f, 0.0f, 1.0f);

	PolyObject *pobj = GetPipeAsPoly(t, obj);

	ModifyPolyObject(pobj, axis, tention);

	obj->UpdateValidity(GEOM_CHAN_NUM, iv);
	obj->UpdateValidity(TOPO_CHAN_NUM, iv);

}

How in the heck would I get the object bounding box min & max, dose this have to be passed from maxscript?


#13

what happened to this bit ?

if(polyobj != os->obj) // new object push it into the pipe
	{
		os->obj = polyobj;
		os->obj->UnlockObject();
	}

what’s wrong with using

MNMesh::getBoundingBox 

?


#14

Sorry Klvnk, I forgot to copy that, my mistake. cut works great now!! Totally fixed!!

I’m a total newb sorry, but how the heck do I get the min max values like maxscript to use from getBoundingBox ()?

This has all helped me so much, thanks for taking the time!

 void TimberBoarding_mod::ModifyObject(TimeValue t, ModContext& mc, ObjectState* os, INode* 
  node)
 
  {
Interval valid = GetValidity(t);
Object *obj = os->obj;
if (!obj) return;

Interval iv = FOREVER;
iv &= obj->ChannelValidity(t, GEOM_CHAN_NUM);
iv &= obj->ChannelValidity(t, TOPO_CHAN_NUM);

float tention;
pblock->GetValue(pb_spin, t, tention, valid);
Point3 axis = Point3(0.0f, 0.0f, 1.0f);

PolyObject *polyobj = GetPipeAsPoly(t, obj);
if (polyobj != os->obj) // new object push it into the pipe
{
	os->obj = polyobj;
	os->obj->UnlockObject();
}

// get the mesh and slice
MNMesh& pmesh = polyobj->GetMesh();

//Trying to get min max values
Box3 bbox = pmesh.getBoundingBox();

int NumCuts = 10; //will be float spinner

//pmesh.min; cant use this

pmesh.Slice(axis, tention, 0.0, false, true);

obj->UpdateValidity(GEOM_CHAN_NUM, iv);
obj->UpdateValidity(TOPO_CHAN_NUM, iv);


  }

#15

where mxs gets it from…

   bbox.Min();
   bbox.Max();

I’m a total newb sorry

not to worry, the best advice I can give is use visual studios find in file functionality (link it to the maxsdk folder) to search for functions/methods for examples of usage. Also the mxsagni project is a treasure trove of howtos as is has the source for a lot of mxs in it.


#16

I use grepWin a lot for exact this purpose. It supports regex. Also there’s FileLocatorPro which is lightning fast


#17

Astro Grep is my favorite.


#18

Thanks guys, checking out those files! So close, just need to figure out the face extrudes and then add the spinners :slight_smile:


#19

I’m still fiddling with getting all faces to extrude. I’ve found some code you posed. But adapting to my needs I’m unsure. I haven’t found much in samples. There are some in maxscrip polyop.cpp but how to use them and get a facelist I’m lost.

This is what I’ve tried to do.

void TimberBoarding_mod::ModifyObject(TimeValue t, ModContext& mc, ObjectState* os, INode* node)
{
	Interval valid = GetValidity(t);
	Object *obj = os->obj;
	if (!obj) return;

	Interval iv = FOREVER;
	iv &= obj->ChannelValidity(t, GEOM_CHAN_NUM);
	iv &= obj->ChannelValidity(t, TOPO_CHAN_NUM);

	float tention;
	pblock->GetValue(pb_spin, t, tention, valid);
	Point3 axis = Point3(0.0f, 0.0f, 1.0f);

	PolyObject *polyobj = GetPipeAsPoly(t, obj);
	if (polyobj != os->obj) // new object push it into the pipe
	{
		os->obj = polyobj;
		os->obj->UnlockObject();
	}

	// get the mesh and slice
	MNMesh& pmesh = polyobj->GetMesh();

	//get bounding box
	Box3 bbox = pmesh.getBoundingBox();
	Point3 SliceMax = bbox.Max();
	Point3 SliceMin = bbox.Min();

	//vars as per maxscript
	int Xmins = SliceMax.x - SliceMin.x;
	float XEnteredNo = tention; //cutting distance
	int XBoardNo = Xmins / XEnteredNo;
	int xchops = XBoardNo; //number of slices

	Point3 slicelen1 = SliceMax - SliceMin;
	Point3 slicelen = slicelen1 / xchops;

	//slice loop
	for (int i = 0; i < xchops; ++i)
	{
		int slicepoint = (SliceMin.z + (i*slicelen.z));
		pmesh.Slice(axis, slicepoint, 0.0, false, false);
	}
	

	//extrude all faces
	pmesh->faceSel = faces;
	pmesh->ExtrudeFaces();
	int nVerts = pmesh->getNumVerts();
	if (nVerts != 0)
	{
		// collect the verts in the "extruded" faces
		BitArray verts(nVerts);
		for (int i = 0; i < pmesh->getNumFaces(); ++i)
		{
			if (!pmesh->faceSel[i]) continue;
			Face *f = &pmesh->faces[i];
			for (int j = 0; j < 3; j++) verts.Set(f->v[j]);
		}
		// do the shift
		for (int i = 0; i < pmesh->nVerts; ++i)
		{
			if (verts[i])
			{
				// compute the shift.... for each vert
			}
		}
	}
	//END

	obj->UpdateValidity(GEOM_CHAN_NUM, iv);
	obj->UpdateValidity(TOPO_CHAN_NUM, iv);


}

#20

I’ve managed to find some code to create the new extrude faces but I’m having trouble moving them.

float distance = 10;
bool ret = false, topoChange = false;

MNChamferData chamData;
MNMesh* mm = &pmesh;
MNTempData mtd(mm);
for (int i = 0; i < mm->numf; i++) {
	mm->f[i].SetFlag(MN_USER);
}
MNFaceClusters fClust(*mm, MN_USER);

topoChange = mm->ExtrudeFaces(MN_USER);

if (topoChange) {

	// Move vertices, not working!
	for (int i = 0; i < pmesh.numv; i++) {
		if (pmesh.v[i].GetFlag(MN_DEAD)) continue;
		pmesh.v[i].p += chamData.vdir[i] * distance;
	}

	mtd.Invalidate(TOPO_CHANNEL);
	mtd.freeBevelInfo();
	mtd.freeAll();
}
if (ret) mm->GetExtrudeDirection(mtd.ChamferData(), MN_USER);