Notifications
Clear all

[Closed] Useful mxs sdk extension functions

this one covers some old ground Denis

return the triangles that make up a poly face… if corner_index is set to false then it returns the vert index else it returns the face corner index

//********************************************************************************************
 // getPolyTriangles 
 // mxs exposure
 
 def_visible_primitive(getPolyTriangles,"getPolyTriangles");
 
 // usage: <bool> getPolyTriangles   <edit_poly node> <int face> <bool corner_index>
 
 Value* getPolyTriangles_cf(Value **arg_list, int count)
 {
 	check_arg_count(getPolyTriangles, 3, count);
 	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_READ_ACCESS, NULL, getPolyTriangles);
 	int face = arg_list[1]->to_int() - 1;
 	range_check(face,0,(pmesh->numf - 1), GetString(IDS_FACE_INDEX_OUT_OF_RANGE));
 	int corner_index = arg_list[2]->to_bool();
 
 	Tab<int> tri;
 	pmesh->f[face].GetTriangles(tri);
 
 // create the return array
 
 	int tri_count = tri.Count();
 	one_typed_value_local(Array* result); 
 	vl.result = new Array(tri_count);
 
 // copy data
 
 	for(int i=0; i < tri_count; i++)
 		corner_index ? vl.result->append(Integer::intern(tri[i] + 1)) :  
 				vl.result->append(Integer::intern(pmesh->f[face].vtx[tri[i]] + 1)) ;
 	return_value(vl.result);
 }

the range_check & get_polyForValue macros can be found in maxsdk\samples\maxscript\mxsagni\MXSAgni.h and the function get_polyForValue calls (_get_polyForValue) can be found in maxsdk\samples\maxscript\mxsagni\common_funcs.cpp

“hidden” interior edges can then be found with

fn GetInteriorEdges tris = (for v = 1 to (tris.count - 3) by 3 collect #(tris[v],tris[v+2]);)

where tris is the output from getPolyTriangles

this turns out to be a real time saver.

def_visible_primitive(getEdgeLength,"getEdgeLength");

// <float> getEdgeLength  <Poly poly> <int edge>

Value* getEdgeLength_cf(Value** arg_list, int count)
{
	check_arg_count(getEdgeLength, 2, count);

// get the mesh

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

// get the edge

	int edge = arg_list[1]->to_int() - 1; // mxs to sdk
	range_check(edge,0,(pmesh->nume - 1), GetString(IDS_EDGE_INDEX_OUT_OF_RANGE));

// return the length

	return Float::intern(Distance(pmesh->v[pmesh->E(edge)->v1].p,pmesh->v[pmesh->E(edge)->v2].p));
}

distance would be your preferred function and the poly mesh functions and range check macro are as before.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

it’s the real saver…get-angle-between-edge-faces function is needed to keep the company

comes in two flavours simple and proper, though proper is twice as slow as simple and simple is about 8x (on 2 quad object) faster than mxs. On a 3200 poly object its 128x faster

// a simplistic face edge angle
     
     def_visible_primitive(getEdgeFaceAngle,"getEdgeFaceAngle");
     
     // <float> getEdgeFaceAngle   <Poly poly> <int edge>
     
     Value* getEdgeFaceAngle_cf(Value** arg_list, int count)
     {
     	check_arg_count(getEdgeFaceAngle, 2, count);
     
     // get the mesh
     
     	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_READ_ACCESS, NULL, getEdgeFaceAngle);
     
     // get the edge
     
     	int edge = arg_list[1]->to_int() - 1; // mxs to sdk
     	range_check(edge,0,(pmesh->nume - 1), GetString(IDS_EDGE_INDEX_OUT_OF_RANGE));
     
     // He is dead, Jim
     
     	if(pmesh->E(edge)->GetFlag(MN_DEAD))
     		return &undefined;
     
     // we have an open edge
     
     	if(pmesh->E(edge)->f2 == -1) 
     		return Float::intern(1.0f);
     
     // return the dot
     
     	return Float::intern( pmesh->GetFaceNormal(pmesh->E(edge)->f1,true) % pmesh->GetFaceNormal(pmesh->E(edge)->f2,true) );
     }
// the proper method face edge angle
     
     def_visible_primitive(getEdgeFaceAngleReflex,"getEdgeFaceAngleReflex");
     
     // <float> getEdgeFaceAngleReflex   <Poly poly> <int edge> <int& reflex>
     
     Value* getEdgeFaceAngleReflex_cf(Value** arg_list, int count)
     {
     	check_arg_count(getEdgeFaceAngleReflex, 3, count);
     
     // get the mesh
     
     	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_READ_ACCESS, NULL, getEdgeFaceAngleReflex);
     
     // get the edge
     
     	int edge = arg_list[1]->to_int() - 1; // mxs to sdk
     	range_check(edge,0,(pmesh->nume - 1), GetString(IDS_EDGE_INDEX_OUT_OF_RANGE));
     
     	MNEdge* pedge = pmesh->E(edge);
     
     // set the reflex thunk
     
     	Thunk* reflexThunk = arg_list[2]->to_thunk();
     	
     // He is dead, Jim
     
     	if(pedge->GetFlag(MN_DEAD))
     		return &undefined;
     
     // we have an open edge 
     
     	if(pedge->f2 == -1) 
     	{
     		reflexThunk->assign(Integer::intern(1));
     		return Float::intern(1.0f);
     	}
     
     // get spare f1 spare vert
     
     	MNFace* f1 = pmesh->F(pedge->f1);
     	Point3& c1 = pmesh->v[f1->vtx[f1->FindTriPoint(f1->EdgeIndex(edge))]].p;
     
     // compute normal
     
     	Point3& a = pmesh->v[pedge->v1].p;
     	Point3& b = pmesh->v[pedge->v2].p;
     	Point3 ab = b - a;
     	Point3 ac = c1 - a;
     	Point3 n1 = (ab ^ ac).Normalize();
     
     // compute matrix
     
     	Matrix3 mat;
     	MatrixFromNormal(n1, mat);
     	mat.SetTranslate(a);
     
     // get spare f2 spare vert
     
     	MNFace* f2 = pmesh->F(pedge->f2);
     	Point3& c2 = pmesh->v[f2->vtx[f2->FindTriPoint(f2->EdgeIndex(edge))]].p;
     
     // compute normal
     
     	Point3 bc = c2 - b;
     	Point3 n2 = (bc ^ ab).Normalize();
     
     // transform spare vert into f1 coordsys
     
     	Point3 c2_in_f1 = c2 * Inverse(mat);
     
     // if c2_in_f1.z neg reflex = true  
     
     	reflexThunk->assign(Integer::intern(c2_in_f1.z < 0.0f ? 1 : 0));
     
     // return the dot
     
     	return Float::intern(n1 % n2);
     }
     
     

very very nice. thank you. i will definitely add it to the extension that i use

a cleaner version (though no faster, think the bottle neck is in findtripoint & EdgeIndex) using plane equation to see if the angle is reflex. Also removed the thunk and poke the reflex param directly so no need for the “&”…

edit: put the thunk back as the direct access was having strange effect on the indexing conversion between mxs & sdk 😮


      def_visible_primitive(getEdgeFaceAngleReflex,"getEdgeFaceAngleReflex");
   
   // <float> getEdgeFaceAngleReflex <Poly poly> <int edge> <int& reflex>
   
   Value* getEdgeFaceAngleReflex_cf(Value** arg_list, int count)
   {
   	check_arg_count(getEdgeFaceAngleReflex, 3, count);
   
   // get the mesh
   
   	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_READ_ACCESS, NULL, getEdgeFaceAngleReflex);
   
   // get the edge
   
   	int edge = arg_list[1]->to_int() - 1; // mxs to sdk
   	range_check(edge,0,(pmesh->nume - 1), GetString(IDS_EDGE_INDEX_OUT_OF_RANGE));
   	MNEdge* pedge = pmesh->E(edge);
   
   // set the reflex thunk
   
   	Thunk* reflexThunk = arg_list[2]->to_thunk();
   	
   // He is dead, Jim
   
   	if(pedge->GetFlag(MN_DEAD))
   		return &undefined;
   
   // we have an open edge 
   
   	if(pedge->f2 == -1) 
   	{
   		reflexThunk->assign(Integer::intern(1));
   		return Float::intern(1.0f);
   	}
   
   // get spare f1 spare vert
   
   	MNFace* face = pmesh->F(pedge->f1);
   	Point3& c1 = pmesh->v[face->vtx[face->FindTriPoint(face->EdgeIndex(edge))]].p;
   
   // compute f1 normal
   
   	Point3& a = pmesh->v[pedge->v1].p;
   	Point3& b = pmesh->v[pedge->v2].p;
   
   	Point3 ab = b - a;
   	Point3 ac = c1 - a;
   	Point3 n1 = (ab ^ ac).FNormalize();
   
   // define the plane
   
   	float d = n1 % c1;
   
   // get spare f2 spare vert
   
   	face = pmesh->F(pedge->f2);
   	Point3& c2 = pmesh->v[face->vtx[face->FindTriPoint(face->EdgeIndex(edge))]].p;
   
   // compute f2 normal
   
   	Point3 bc = c2 - b;
   	Point3 n2 = (bc ^ ab).FNormalize();
   
   // is c2 below the plane defined by n1 & d  
   
   	reflexThunk->assign(Integer::intern((n1 % c2) - d < 0.0f ? 1 : 0));
   
   // return the dot
   
   	return Float::intern(n1 % n2);
   }
      

edit: have found more efficient way of doing this in mxs and the performance is on average only 5/6 times better.

following this thread http://forums.cgsociety.org/showthread.php?f=98&t=1090733
i wrote this function…


def_visible_primitive(patchAttach, "patchAttach");
Value* patchAttach_cf(Value **arg_list, int count)
{
	check_arg_count(patchAttach, 2, count);

	INode *patch = arg_list[0]->to_node();
	INode *node = arg_list[1]->to_node();

	if (patch == node) return &false_value;
	
	Object* obj = Get_Object_Or_XRef_BaseObject(patch->GetObjectRef());
	if (obj->ClassID() == patchObjectClassID) 
	{
		PatchObject *ep = dynamic_cast<PatchObject *>(obj);

		GeomObject *object = (GeomObject*)node->GetObjectRef()->Eval(MAXScript_interface->GetTime()).obj;
   
		if(object->CanConvertToType(patchObjectClassID)) {
			PatchObject *attPatch = (PatchObject*)object->ConvertToType(MAXScript_interface->GetTime(),patchObjectClassID);
			if(attPatch) {
				PatchMesh source = attPatch->patch;
				bool canUndo = true;
				ep->DoAttach(node, &source, canUndo);

				if(attPatch != (PatchObject*)object) delete attPatch;
				return &true_value;
			}
		}
	}
	return &false_value;
}

it uses DoAttach which very old, and as many other methods of that time needs to be executed when patch is selected and it is in modifier panel…
it’s possible to use a lower level (PatchMesh) Attach but it needs more work.

the code is primitive the function might be useful:


 def_visible_primitive(abortRender, "abortRender");
 Value* abortRender_cf(Value **arg_list, int count)
 {
 	check_arg_count(patchAttach, 0, count);
 	MAXScript_interface->AbortRender();
 	return MAXScript_interface->CheckForRenderAbort() ? &true_value : &false_value;
 }
 

mxs use:


 fn preRenderAbort event: = 
 (
 	format "% >> % %
" event (callbacks.notificationparam()) (abortRender())
 )
 callbacks.removescripts id:#render_test
 callbacks.addscript #preRender "preRenderAbort event:#preRender" id:#render_test 
 callbacks.addscript #preRenderEval "preRenderAbort event:#preRenderEval" id:#render_test 
 

I was hoping this would be more than a 2 man show Denis, if you don’t already have these I think you will like them…

exposes editable poly specified normals without using the edit normal modifier and having the target node selected and the modify panel open for it to work. It would be easy to expand on these if the functionality you need is not here, anyway enjoy....


hasSpecifiedNormals, checks for the MNNormalSpec is present, though this maybe empty
[b]usage: &lt;bool&gt; hasSpecifiedNormals &lt;edit_poly_node&gt;[/b]

    def_visible_primitive(hasSpecifiedNormals,"hasSpecifiedNormals");
    
    Value* hasSpecifiedNormals_cf(Value **arg_list, int count)
    {
    	check_arg_count(hasSpecifiedNormals, 1, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, hasSpecifiedNormals);
    	return bool_result(pmesh->GetSpecifiedNormals());
    }
clearSpecifiedNormals, clear specified normals from an editable poly
[b]usage: <bool> clearSpecifiedNormals <edit_poly_node>[/b]
def_visible_primitive(clearSpecifiedNormals,"clearSpecifiedNormals");
    
    Value* clearSpecifiedNormals_cf(Value **arg_list, int count)
    {
    	check_arg_count(clearSpecifiedNormals, 1, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, clearSpecifiedNormals);
    
    	if(pmesh->GetSpecifiedNormals())
    	{
    		pmesh->ClearSpecifiedNormals();
    		return &true_value;
    	}
    	else
    		return &false_value;
    }
    
buildSpecifiedNormals, builds specified normals for an editable poly caution this clears any current specified normals before rebuilding the normals
[b]usage: <bool> buildSpecifiedNormals <edit_poly_node>[/b]
def_visible_primitive(buildSpecifiedNormals,"buildSpecifiedNormals");
    
    Value* buildSpecifiedNormals_cf(Value **arg_list, int count)
    {
    	check_arg_count(buildSpecifiedNormals, 1, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, 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->FAlloc(pmesh->numf))
    		{
    // build the normals
    
    			nspec->BuildNormals();
    			return &true_value;
    		}
    	}
    	return &false_value;
    }
getNumSpecNormalFaces, returns the number of specified normal faces
[b]usage: <int> getNumSpecNormalFaces <edit_poly_node>[/b]
def_visible_primitive(getNumSpecNormalFaces,"getNumSpecNormalFaces");
    
    Value* getNumSpecNormalFaces_cf(Value **arg_list, int count)
    {
    	check_arg_count(getNumSpecNormalFaces, 1, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, getNumSpecNormalFaces);
    
    	int numfaces = 0;
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    		numfaces = nspec->GetNumFaces();
    	return Integer::intern(numfaces);
    }
    
getNumSpecNormals, returns the number of specified normals
[b]usage: <int> getNumSpecNormals <edit_poly_node>[/b]
def_visible_primitive(getNumSpecNormals,"getNumSpecNormals");
    
    Value* getNumSpecNormals_cf(Value **arg_list, int count)
    {
    	check_arg_count(getNumSpecNormals, 1, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, getNumSpecNormalFaces);
    
    	int numNormals = 0;
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    		numNormals = nspec->GetNumNormals();
    	return Integer::intern(numNormals);
    }
setAllSpecNormalsExplicit , sets all normals explicit or not
[b]usage: <bool> setAllSpecNormalsExplicit <edit_poly_node> <bool flag>[/b]
def_visible_primitive(setAllSpecNormalsExplicit,"setAllSpecNormalsExplicit");
    
    Value* setAllSpecNormalsExplicit_cf(Value **arg_list, int count)
    {
    	check_arg_count(setAllSpecNormalsExplicit, 2, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, setAllSpecNormalsExplicit);
    
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    	{
    		nspec->MakeNormalsExplicit(false, NULL ,arg_list[1]->to_bool());
    		return &true_value;
    	}
    	return &false_value;
    }
setAllSpecNormalsSpecified , sets all normals explicit or not
[b]usage: <bool> setAllSpecNormalsSpecified <edit_poly_node> <bool flag>[/b]
def_visible_primitive(setAllSpecNormalsSpecified,"setAllSpecNormalsSpecified");
    
    Value* setAllSpecNormalsSpecified_cf(Value **arg_list, int count)
    {
    	check_arg_count(setAllSpecNormalsSpecified, 2, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, setAllSpecNormalsSpecified);
    
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    	{
    		nspec->SpecifyNormals(false, NULL);
    		return &true_value;
    	}
    	return &false_value;
    }
resetAllSpecNormals , resets all normals which are then recalculated
[b]usage: <bool> resetAllSpecNormals <edit_poly_node>[/b]
def_visible_primitive(resetAllSpecNormals,"resetAllSpecNormals");
    
    Value* resetAllSpecNormals_cf(Value **arg_list, int count)
    {
    	check_arg_count(resetAllSpecNormals, 1, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, resetAllSpecNormals);
    
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    	{
    		nspec->ResetNormals(false,NULL);
    		return &true_value;
    	}
    	return &false_value;
    }
setSpecNormal , sets the normal value
[b]usage: <bool> setSpecNormal <edit_poly_node> <int normal_id> <point3 normal>[/b]
def_visible_primitive(setSpecNormal,"setSpecNormal");
    
    Value* setSpecNormal_cf(Value **arg_list, int count)
    {
    	check_arg_count(setSpecNormal, 3, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, setSpecNormal);
    
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    	{
    		int normalID = arg_list[1]->to_int() - 1;
    		range_check(normalID,0,(nspec->GetNumNormals() - 1), GetString(IDS_NORMAL_INDEX_OUT_OF_RANGE));
    
    		nspec->Normal(normalID) = arg_list[2]->to_point3();
    		return &true_value;
    	}
    	return &false_value;
    }
getSpecNormal , gets the normal value
[b]usage: <point3> getSpecNormal <edit_poly_node> <int normal_id>
[/b]
def_visible_primitive(getSpecNormal,"getSpecNormal");
    
    Value* getSpecNormal_cf(Value **arg_list, int count)
    {
    	check_arg_count(getSpecNormal, 2, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, getSpecNormal);
    
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    	{
    		int normalID = arg_list[1]->to_int() - 1;
    		range_check(normalID,0 ,(nspec->GetNumNormals() - 1), GetString(IDS_NORMAL_INDEX_OUT_OF_RANGE));
    
    		return new Point3Value(nspec->Normal(normalID));
    	}
    	return &undefined;
    }
getSpecNormalIndex , gets the normal index from the face and corner (1..to facedeg)
[b]usage: <int> getSpecNormalIndex <edit_poly_node> <int face> <int corner>[/b]

    def_visible_primitive(getSpecNormalIndex,"getSpecNormalIndex");
    
    Value* getSpecNormalIndex_cf(Value **arg_list, int count)
    {
    	check_arg_count(getSpecNormalIndex, 3, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, getSpecNormalIndex);
    
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    	{
    		int face = arg_list[1]->to_int() - 1;
    		int corner = arg_list[2]->to_int() - 1;
    		
    		range_check(face,0,(nspec->GetNumFaces() - 1), GetString(IDS_FACE_INDEX_OUT_OF_RANGE));
    		range_check(corner,0,(nspec->Face(face).GetDegree() - 1), GetString(IDS_FACE_CORNER_INDEX_OUT_OF_RANGE));
    
    		return Integer::intern(nspec->GetNormalIndex(face,corner) + 1);
    	}
    	return &undefined;
    }
getVertIndex , gets the vert index from the face and corner (1..to facedeg) this is to mimic the editnormals GetVertexID function
[b]usage: <int> getVertIndex <edit_poly_node> <int face> <int corner>[/b]
def_visible_primitive(getVertIndex,"getVertIndex");
    
    Value* getVertIndex_cf(Value **arg_list, int count)
    {
    	check_arg_count(getVertIndex, 3, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, getVertIndex);
    
    	int face = arg_list[1]->to_int() - 1;
    	int corner = arg_list[2]->to_int() - 1;
    
    	range_check(face,0,(pmesh->numf - 1), GetString(IDS_FACE_INDEX_OUT_OF_RANGE));
    	range_check(corner,0,(pmesh->f[face].deg - 1), GetString(IDS_FACE_CORNER_INDEX_OUT_OF_RANGE));
    
    	return Integer::intern(pmesh->f[face].vtx[corner] + 1);
    }
    
VertexToSpecNormals , returns a bitarray of normal ids from a vertex id
[b]usage: <bitarray> VertexToSpecNormals <edit_poly_node> <int vertex>
[/b]
def_visible_primitive(VertexToSpecNormals,"VertexToSpecNormals");
    
    Value* VertexToSpecNormals_cf(Value **arg_list, int count)
    {
    	check_arg_count(VertexToSpecNormals, 2, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, VertexToSpecNormals);
    
    // range check
    
    	int vert = arg_list[1]->to_int() - 1;
    	range_check(vert,0,(pmesh->numv - 1), GetString(IDS_VERTEX_INDEX_OUT_OF_RANGE));
    
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    	{
    // new bitarray to size required
    
    		BitArray normals(nspec->GetNumNormals());
    		normals.ClearAll();
    
    		int numfaces = nspec->GetNumFaces();
    
    // make sure we use the max number of faces
    
    		if(pmesh->numf < numfaces) 
    			numfaces = pmesh->numf;
    
    // exit if zero
    
    		if(!numfaces) 
    			return &undefined;
    		
    		for (int i=0; i < numfaces; i++) 
    		{
    // skip over dead faces
    
    			if (pmesh->f[i].GetFlag(MN_DEAD)) 
    				continue;
    
    			MNNormalFace& face = nspec->Face(i);
    			int facedeg = face.GetDegree();
    
    // use the max face degree
    
    			if(pmesh->f[i].deg < facedeg) 
    				facedeg = pmesh->f[i].deg;
    
    			for(int j=0; j < facedeg; j++) 
    			{
    // next vert if not ours
    
    				if(vert != pmesh->f[i].vtx[j]) 
    					continue;
    
    // grab the normal index
    
    				int normalID = face.GetNormalID(j);
    
    // check we're in range if not continue
    
    				if(normalID < 0 || normalID >= normals.GetSize()) 
    					continue;
    
    // save the normal id
    
    				normals.Set(normalID);
    			}
    		}
    		return new BitArrayValue(normals);
    	}
    	return &undefined;
    }
getSpecNormalFaceDeg , gets the normal face degree
[b]usage: <int> getSpecNormalFaceDeg <edit_poly_node> <int face>
[/b]
def_visible_primitive(getSpecNormalFaceDeg,"getSpecNormalFaceDeg");
    
    Value* getSpecNormalFaceDeg_cf(Value **arg_list, int count)
    {
    	check_arg_count(getSpecNormalFaceDeg, 2, count);
    	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_BASE_OBJ, NULL, getSpecNormalFaceDeg);
    
    	MNNormalSpec* nspec = pmesh->GetSpecifiedNormals();
    	if(nspec)
    	{
    		int face = arg_list[1]->to_int() - 1;
    		range_check(face,0,(nspec->GetNumFaces() - 1),GetString(IDS_FACE_INDEX_OUT_OF_RANGE));
    
    		return Integer::intern(nspec->Face(face).GetDegree());
    	}
    	return &undefined;
    }

edit: i’ve just noticed i’ve used MESH_BASE_OBJ instead of MESH_READ_ACCESS in functions where they don’t change the mesh. Just means some functionality (working on objects with modifier present) will be lost but it keeps it consistent I suppose.

VERY COOL! i like it! thanks

there are two functions about poly edge loop/ring shift selection… poly has these methods but the difference is mine don’t really need a selection


 def_visible_primitive(setSelectionShift, "setSelectionShift");
 Value* setSelectionShift_cf(Value** arg_list, int count)
 {
 	check_arg_count(setSelectionShift, 5, count);
 	INode		*node	= arg_list[0]->to_node();
 	int			shift	= arg_list[1]->to_int();
 	bool		move	= (arg_list[2] == &true_value);
 	bool		add		= (arg_list[3] == &true_value);
 
 	Object* obj = Get_Object_Or_XRef_BaseObject(node->GetObjectRef());
 	if (obj->ClassID() == EPOLYOBJ_CLASS_ID) 
 	{
 		BaseInterface* i = ((Animatable*)obj)->GetInterface(EPOLY_INTERFACE);
 		if (i)
 		{
 			EPoly* ip = (EPoly*)i;
 			if (arg_list[4] == &true_value)
 				ip->EpfnSetRingShift(shift, move, add);
 			else
 				ip->EpfnSetLoopShift(shift, move, add);
 			node->NotifyDependents(FOREVER, PART_GEOM | PART_TOPO | PART_SELECT, REFMSG_CHANGE);
 			node->NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
 			ip->RefreshScreen(); 
 			return &true_value;
 		}
 		return &false_value;
 	}
 	return &undefined;
 }
 
 def_visible_primitive(getEdgeSelectionShift, "getEdgeSelectionShift");
 Value* getEdgeSelectionShift_cf(Value** arg_list, int count)
 {
 	check_arg_count_with_keys(getEdgeSelectionShift, 2, count);
 	INode		*node	= arg_list[0]->to_node();
 	int			value	= arg_list[1]->to_int();
 	
 	Value*		tmp;
 	BOOL		loop	= bool_key_arg(loop, tmp, TRUE);
 
 	Object* obj = Get_Object_Or_XRef_BaseObject(node->GetObjectRef());
 	if (obj->ClassID() == EPOLYOBJ_CLASS_ID) 
 	{
 		BitArray newSel;
 		
 		PolyObject *poly = dynamic_cast<PolyObject *>(obj);
 		if(poly && poly->GetMesh().nume > 0)
 		{
 			MNMesh &mesh = poly->GetMesh();
 			newSel.SetSize(mesh.nume);
 			newSel.ClearAll();
 			IMNMeshUtilities8* meshToShift = static_cast<IMNMeshUtilities8*>(mesh.GetInterface( IMNMESHUTILITIES8_INTERFACE_ID )); 
 
 			if (loop) 
 			{
 				meshToShift->SelectEdgeLoopShift(value,newSel); 
 				//mprintf("loop: %f
", dir);
 			}
 			else meshToShift->SelectEdgeRingShift(value,newSel);
 		}
 		return new BitArrayValue(newSel);
 	}
 	return &undefined;
 }
 

and it works for Editable poly base object. so i use it for example for skin step loop/ring

Page 3 / 18