Notifications
Clear all

[Closed] Useful mxs sdk extension functions

i’m playing with your set of functions… they are really cool. but i can’t figure out why after i change a normal and apply edit_normal modifier to the poly, i see the normal non-set. as i expect the edit_normal modifier has to show how the spec normals currently set in the MNMesh

did you make them specified or explicit after you set it ? I only included functions to zap the lot as I tend to use them as set all at once but should be pretty easy to add the set an individual normal to specified or explicit.

1 Reply
(@denist)
Joined: 1 year ago

Posts: 0

setAllSpecNormalsExplicit to ON works…
but any way i have some strange result… check the sample:


  delete objects
  s = sphere()
  addmodifier s (edit_normals())
  converttopoly s
  b = box pos:[40,0,0]
  	
  deleteAllChangeHandlers id:#follow_tm
  when transform b change id:#follow_tm handleAt:#redrawViews obj do
  (
  	for n=1 to getNumSpecNormals s do setSpecNormal s n obj.transform[3]
  	setAllSpecNormalsExplicit s on
  	update s	
  )
  

edit:
it seams like it works as expected… good. i keep playing… thanks

you could use this sort of thing instead of adding the edit normals mod but after converting to editable poly…

if not hasSpecifiedNormals obj then 
     	buildSpecifiedNormals obj;	
   

let me know any strangeness, they seem to be a bit “odd” sometimes but using the reset seems correct any issues (not an issue for my purpose as I’m setting them procedurally most of the time).

my global idea is to write PolySkin… the max skin is not optimized at all. it looks like people who wrote it never expected that it would be used in real time. i’ve made some tests… my expectation – the skin can be at least five times faster…

1 Reply
(@denist)
Joined: 1 year ago

Posts: 0

i was right … i can calculate skin deformation ~15 times faster than the skin modifier does do… now i need to minimize the number of notifications to react to…

working with BitArray i found that shift bitArray function might be very useful in MXS:


    def_visible_primitive(shift,"shiftBitArray");
Value* shift_cf(Value **arg_list, int count)
{
	check_arg_count_with_keys(shift, 2, count);
	type_check(arg_list[1],Integer,"second argument");						//negative shifts left, positive shifts right 

	int start = key_arg_or_default(start,Integer::intern(1))->to_int() - 1;	//start index for shift. 1 based
	bool size = (key_arg_or_default(size,&false_value) == &true_value);		//do resize after shift. bool

	if (is_bitarray(arg_list[0]))
	{
		BitArray b = arg_list[0]->to_bitarray();
		int count = arg_list[1]->to_int();
		int dir = (count > 0) ? RIGHT_BITSHIFT : LEFT_BITSHIFT;
		count = abs(count);
		
		if (dir == LEFT_BITSHIFT) 
		{
			b.Shift(dir, count, start);
			if (size) b.SetSize(b.GetSize() - count, 1);
			else for (int i = 0, s = b.GetSize() - 1; i < count; i++) b.Set(s - i,false);
		}
		else
		{
			if (size) b.SetSize(b.GetSize() + count, 1);
			b.Shift(dir, count, start);
			for (int i = start, k = 0; i < b.GetSize(), k < count; i++, k++) b.Set(i,false);
		}
		return new BitArrayValue(b);
	}
	return &undefined;
}
    
it works different than the SDK method. after shift it clears left/right bits. which makes more sense for me.

these are pretty slick

had call to use the cubic ease In out for something I’m working on, 6-7 time faster than the mxs version. t is time or x, b = start value, c = end value – start value and d = duration.

Cubic Ease In Out
<float> easeInOutCubic <float t> <float b> <float c> <float d>

def_visible_primitive(easeInOutCubic, "easeInOutCubic");
 
 Value* easeInOutCubic_cf(Value **arg_list, int count)
 {
 	check_arg_count(easeInOutCubic, 4, count);
 	float t = arg_list[0]->to_float();
 	float b = arg_list[1]->to_float();
 	float c = arg_list[2]->to_float();
 	float d = arg_list[3]->to_float();
 
 	if((t/=d/2) < 1.0) return Float::intern(c/2*t*t*t + b);
 	return Float::intern(c/2*((t-=2)*t*t + 2) + b);	
 }

glad no-one spotted the deliberate mistake

something i needed to speed up custom mesh in scripted plugins, also tidies up the code a lot to, compress the get/set edge visibility for a face to a single call…

<bitarray> getEdgeVisFlags <editable_mesh> <face>

def_visible_primitive(getEdgeVisFlags,"getEdgeVisFlags");
  
  Value* getEdgeVisFlags_cf(Value **arg_list, int count)
  {
  	check_arg_count_with_keys(getEdgeVisFlags, 2, count);
  
  	Mesh* pmesh = get_meshForValue(arg_list[0], MESH_READ_ACCESS, NULL, getEdgeVisFlags);
  
  	int face = arg_list[1]->to_int() - 1; // mxs to sdk
  	range_check(face,0,(pmesh->numFaces - 1), GetString(IDS_FACE_INDEX_OUT_OF_RANGE));
  
  	BitArray viz(3);
  	viz.ClearAll();
  
  	for(int i = 0; i < 3; i++) 
  		if(pmesh->faces[face].getEdgeVis(i))
  			viz.Set(i);
  	
  	return new BitArrayValue(viz);
  }

setEdgeVisFlags <editable_mesh>

def_visible_primitive(setEdgeVisFlags,"setEdgeVisFlags");
  
  Value* setEdgeVisFlags_cf(Value **arg_list, int count)
  {
  	check_arg_count_with_keys(setEdgeVisFlags, 3, count);
  
  	Mesh* pmesh = get_meshForValue(arg_list[0], MESH_WRITE_ACCESS, NULL, setEdgeVisFlags);
  
  	int face = arg_list[1]->to_int() - 1; // mxs to sdk
  	range_check(face,0,(pmesh->numFaces - 1), GetString(IDS_FACE_INDEX_OUT_OF_RANGE));
  
  	BitArray viz = arg_list[2]->to_bitarray();
  	viz.SetSize(3,1); // set to correct size regardless
  
  
  	pmesh->faces[face].setEdgeVisFlags(viz[0],viz[1],viz[2]);
  	return &ok;
  }

is there a way of doing skinning on the gpu ? getting max to pass the bone transforms & vertex weighting to shaders would be nice.

these three functions draw dotted rectangle, circle, and polyline:


//--------------------------------------- Viewport Draw Dotted -------------------------------------//
#include "winutil.h"

def_visible_primitive(XORDottedRect, "drawDottedRect");
Value* XORDottedRect_cf(Value **arg_list, int count)
{
	check_arg_count_with_keys(XORDottedRect, 2, count);
	ViewExp* veiw = MAXScript_interface->GetActiveViewport();

	if (veiw && is_point2(arg_list[0]) && is_point2(arg_list[1]))
	{
		Point2 p0 = arg_list[0]->to_point2();
		Point2 p1 = arg_list[1]->to_point2();

		bool solid = key_arg_or_default(constant, Integer::intern(0))->to_int();
		bool erase = (key_arg_or_default(clear, &false_value) == &true_value);
		bool delayed = (key_arg_or_default(delayed, &false_value) == &true_value);

		XORDottedRect(veiw->GetHWnd(), IPoint2(p0.x, p0.y), IPoint2(p1.x, p1.y), solid, erase, delayed);
		return &true_value;
	}
	return &false_value;
}

def_visible_primitive(XORDottedCircle, "drawDottedCircle");
Value* XORDottedCircle_cf(Value **arg_list, int count)
{
	check_arg_count_with_keys(XORDottedCircle, 2, count);
	ViewExp* veiw = MAXScript_interface->GetActiveViewport();

	if (veiw && is_point2(arg_list[0]) && is_point2(arg_list[1]))
	{
		Point2 p0 = arg_list[0]->to_point2();
		Point2 p1 = arg_list[1]->to_point2();

		int solid = key_arg_or_default(constant, Integer::intern(0))->to_int();
		bool erase = (key_arg_or_default(clear, &false_value) == &true_value);
		bool delayed = (key_arg_or_default(delayed, &false_value) == &true_value);

		XORDottedCircle(veiw->GetHWnd(), IPoint2(p0.x, p0.y), IPoint2(p1.x, p1.y), solid, erase, delayed);
		return &true_value;
	}
	return &false_value;
}

def_visible_primitive(XORDottedPolyline, "drawDottedPolyline");
Value* XORDottedPolyline_cf(Value **arg_list, int count)
{
	check_arg_count_with_keys(XORDottedPolyline, 1, count);
	ViewExp* veiw = MAXScript_interface->GetActiveViewport();

	if (veiw && is_array(arg_list[0]))
	{
		bool closed = (key_arg_or_default(closed, &false_value) == &true_value);

		Array* points = (Array*)arg_list[0]; 
		if (closed) points->append(points->data[0]);

		int count = points->size;

		IPoint2* pp = new IPoint2[count];
		Point2 p;
		for (int i = 0; i < count; i++) 
		{
			p = points->data[i]->to_point2();
			pp[i] = IPoint2(p.x, p.y);
		}

		int solid = key_arg_or_default(constant, Integer::intern(0))->to_int();
		bool erase = (key_arg_or_default(clear, &false_value) == &true_value);
		bool delayed = (key_arg_or_default(delayed, &false_value) == &true_value);

		XORDottedPolyline(veiw->GetHWnd(), count, pp, solid, erase, delayed);
		return &true_value;
	}
	return &false_value;
}

they have optional parameters… you can find their meanings in SDK documentation.
#constant (or solid) option works only for rectangle. for circle and polyline it seams like broken. i’ve supported it just in case of max will fix it later.

Klunk,
what do you think might be the best way to update PolyObject after its MNMesh was modified?

Page 5 / 18