Notifications
Clear all

[Closed] Useful mxs sdk extension functions

it’s an odd one for sure…

if you call the getNumSpecNormalFaces function in mxs directly after the build function it returns the correct number of faces and that is using the same code !

nspec->GetNumFaces(); 

I’ve tried this changing this

if(nspec->FAlloc(pmesh->numf))

to

if(nspec->SetNumNormals(pmesh->numf))

makes no difference though may well be the proper way to do it though, but who can tell when the reference is so vague, never mind though we’ll just plough on with python.

if you trust that max will always build the normals you can use

return Integer::intern(pmesh->numf);

it’s not pretty or elegant or might not even be correct but maybe better than zero

1 Reply
(@denist)
Joined: 1 year ago

Posts: 0

I’ve tried this changing this

if(nspec->FAlloc(pmesh->numf))

to

if(nspec->SetNumNormals(pmesh->numf))

makes no difference though may well be the proper way to do it though, but who can tell when the reference is so vague, never mind though we’ll just plough on with python.

i’ve tried to set number faces… it doesn’t work either

if you trust that max will always build the normals you can use

return Integer::intern(pmesh->numf);

it’s not pretty or elegant or might not even be correct but maybe better than zero

do i look like as a person who trusts max?

well… but i have an idea. there is a metod CheckNormals. it needs the Parent MNMesh to be set. so …

nspec->SetParent(pmesh); nspec->CheckNormals();

it has to do all for us… and probably doesn’t need any Allocation and Build. i don’t have max right now to try. if you can try it, please, let me know how it works

sorry late night brain scramble should be using SetNumFaces not SetNumNormals

anyway this works for me

Value* buildSpecifiedNormals_cf(Value **arg_list, int count)
  {
  	check_arg_count(buildSpecifiedNormals, 1, count);
  	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_WRITE_ACCESS, NULL, buildSpecifiedNormals);
  
  // clear any existing normals
  
  	if(pmesh->GetSpecifiedNormals())
  		pmesh->ClearSpecifiedNormals();
  
  // create an empty specified normals
  
  	pmesh->SpecifyNormals();
  	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
  	if(nspec)
  	{
  // initialize the normals
  
  		nspec->Initialize();
  
  // create the faces
  
  		if(nspec->SetNumFaces(pmesh->numf))	
  		{
  // build the normals
  
  			nspec->BuildNormals();
 		    return Integer::intern(nspec->GetNumFaces());
  		}
  	}
      return &undefined;
  }

i’ve tried it… yes. it sets the number of faces, but it still doesn’t update the nspec topology

getting the command panel rollup window hwnd, though possible via other means this is a more direct method

def_visible_primitive(getCommandPanelHWND, "getCommandPanelHWND");


Value* getCommandPanelHWND_cf(Value **arg_list, int count)
{
	check_arg_count(getCommandPanelHWND, 0, count);
	return IntegerPtr::intern((INT_PTR)MAXScript_interface->GetCommandPanelRollup()->GetHwnd());
}

by product of something else I was working on and I needed this info…

mxs usage

<array point3> getShapePolyLine <shape> <curve>

returns an array of points that make up the the drawn spline curve will take into account steps, optimize and adaptive.


def_visible_primitive(getShapePolyLine, "getShapePolyLine");

Value* getShapePolyLine_cf(Value **arg_list, int count)
{
	check_arg_count(getShapePolyLine, 2, count);

// handle the shape arg

	MAXNode* shape = (MAXNode*)arg_list[0];
	INode* node = get_valid_node(shape, getShapePolyLine);
	Object* obj = node->GetObjectRef();

	if (obj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
		obj = ((IDerivedObject*)obj)->FindBaseObject();

	if(obj->ClassID() != splineShapeClassID && obj->ClassID() != Class_ID(SPLINE3D_CLASS_ID,0))
		throw RuntimeError (GetString(IDS_SHAPE_OPERATION_ON_NONSPLINESHAPE), obj->GetObjectName());
	
// confident we have a shape object

	SplineShape* spline = dynamic_cast<SplineShape*>(obj);

// handle the curve index

	int index = arg_list[1]->to_int();
	if (index < 1 || index > spline->shape.splineCount)
		throw RuntimeError (GetString(IDS_SHAPE_SPLINE_INDEX_OUT_OF_RANGE), Integer::intern(index));

// grab the polyshape from our spline

	PolyShape pshape;
	spline->MakePolyShape(MAXScript_interface->GetTime(), pshape, PSHAPE_BUILTIN_STEPS, spline->shape.optimize);
	
// create an array from the curve we want

	PolyLine* pline  = &pshape.lines[index-1];
	int numPts = pline->numPts;
	one_typed_value_local(Array* result);

	vl.result = new Array(numPts);

	Point3 p;	
	for(int i = 0; i < numPts; i++)
	{
		p = (*pline)[i].p;
		shape->object_to_current_coordsys(p);
		vl.result->append(new Point3Value(p));
	}
	return_value(vl.result);
}
1 Reply
(@denist)
Joined: 1 year ago

Posts: 0

i’ve just recently used it… very, very nice and working great. it’s a very useful extension. thanks!
(one little change i did. i replaced second argument with an optional default key == 1)

two functions that appear in meshop interface but not the polyop. they are called from using something like

polyop.getMapVertsUsingMapFace $ 1 3
polyop.getMapFacesUsingMapVert $ 1 3

the ValueToBitArray to bit array function can be found in the common_functions.cpp of the mxsagni project in the samples.

void checkMapChannel(MNMesh* poly, int channel)
{
	range_check(channel, -NUM_HIDDENMAPS, MAX_MESHMAPS-1, GetString(IDS_MESH_MAP_CHANNEL_OUT_OF_RANGE));
	if (channel >= poly->numm)
		throw RuntimeError (GetString(IDS_MAP_SUPPORT_NOT_ENABLED_FOR_CHANNEL), Integer::intern(channel));
	if (poly->M(channel)->GetFlag(MN_DEAD))
		throw RuntimeError(GetString(IDS_MAP_SUPPORT_NOT_ENABLED_FOR_CHANNEL), Integer::intern(channel));
}

//********************************************************************************************

def_struct_primitive(polyop_getMapVertsUsingMapFace, polyop, "getMapVertsUsingMapFace");

// <bitarray> getMapVertsUsingMapFace   

Value* polyop_getMapVertsUsingMapFace_cf(Value** arg_list, int arg_count)
{
// the usual arg check

	check_arg_count(getMapVertsUsingMapFace, 3, arg_count);

// get the poly mesh

	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_READ_ACCESS, NULL, getMapVertsUsingMapFace);

// get map channel arg and check for support and in range

	int channel = arg_list[1]->to_int();
	checkMapChannel(pmesh,channel);

// ok to access the map

	MNMap* map = pmesh->M(channel);
	int nMapVerts = map->numv;
	int nMapFaces = map->numf;

// create a new bitarray from input which could be a bitarray, int or array of ints

	BitArray mapFaces(nMapFaces);
	ValueToBitArray(arg_list[2], mapFaces, nMapFaces, GetString(IDS_POLY_MAP_FACE_INDEX_OUT_OF_RANGE));

// create the return variable on the stack

	one_typed_value_local(BitArrayValue* mapVerts);
	vl.mapVerts = new BitArrayValue(nMapVerts);

	for(int f = 0; f < nMapFaces; f++) // for all map faces
	{
		if(mapFaces[f]) // if set
		{
			MNMapFace* face = map->F(f); // get our face and
			for(int v = 0; v < face->deg; v++) // for all verts in the map face
				vl.mapVerts->bits.Set(face->tv[v]); // set the corresponding bit
		}
	}
	return_value(vl.mapVerts);
}

//********************************************************************************************

def_struct_primitive(polyop_getMapFacesUsingMapVert, polyop, "getMapFacesUsingMapVert");

// <bitarray> getMapFacesUsingMapVert   

Value* polyop_getMapFacesUsingMapVert_cf(Value** arg_list, int arg_count)
{
// the usual arg check

	check_arg_count(getMapFacesUsingMapVert, 3, arg_count);

// get the poly mesh

	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_READ_ACCESS, NULL, getMapFacesUsingMapVert);

// get map channel arg and check for support and in range

	int channel = arg_list[1]->to_int();
	checkMapChannel(pmesh,channel);

// ok to access the map

	MNMap* map = pmesh->M(channel);
	int nMapVerts = map->numv;
	int nMapFaces = map->numf;

// create a new bitarray from input which could be a bitarray, int or array of ints

	BitArray mapVerts(nMapVerts);
	ValueToBitArray(arg_list[2], mapVerts, nMapVerts, GetString(IDS_POLY_MAP_VERTEX_INDEX_OUT_OF_RANGE));

// create the return variable on the stack

	one_typed_value_local(BitArrayValue* mapFaces);
	vl.mapFaces = new BitArrayValue (nMapFaces);

// iterate through the faces set the bit array if using the the verts
	
	for(int f = 0; f < nMapFaces; f++)
	{
		MNMapFace* face = map->F(f);
		for(int v = 0; v < face->deg; v++ ) // for all verts in the face
		{
			if(mapVerts[face->tv[v]]) // is this face using this vert ?
			{
				vl.mapFaces->bits.Set(f);
				break; // face is being used we don't need to know anymore
			}
		}
	}
	return_value(vl.mapFaces);
}

I’m sure there are ways to create meshes that defeat it both ways but it should work for most cases…

def_visible_primitive(isMeshClosed,"isMeshClosed");
 
 Value* isMeshClosed_cf(Value **arg_list, int count)
 {
 	check_arg_count(isMeshClosed, 1, count);
 	Mesh* pmesh = get_meshForValue(arg_list[0], MESH_READ_ACCESS, NULL, isMeshClosed);
 
 	int numEdges;
 	Edge* edges = pmesh->MakeEdgeList(&numEdges);
 	
 	BOOL openEdge = FALSE;
 	for(int i = 0; i < numEdges; ++i)
 	{
 		Edge& edge = edges[i];
 		if(edge.f[0] == -1 || edge.f[1] == -1)
 		{		
 			openEdge = TRUE;
 			break;
 		}
 	}
 	delete [] edges;
 	return bool_result(!openEdge);
 }

there is a conflict in the max… it allows to make T-edge (has more than 2 faces) but doesn’t consider it valid… how is about to double check it?

yep one day if I can be bothered

here is a pair for appendIfInique:


 def_visible_primitive(deleteIfContained, "deleteIfContained");
Value* deleteIfContained_cf(Value** arg_list, int count)
{
	check_arg_count(deleteIfContained, 2, count);
	Value* theArray = arg_list[0];
	Value* theVal = arg_list[1];
	Value* i;
	try 
	{ 
		i = theArray->findItem_vf(&theVal,1); 
		if (i->to_int() == 0) return &false_value;
	}
	catch (NoMethodError) 
	{
		return &false_value;
	}
	while (i->to_int() != 0)
	{
		theArray->deleteItem_vf(&i,1);
		i = theArray->findItem_vf(&theVal,1);
	}
	return &true_value;
}
 
Page 7 / 18