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.
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…
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.
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?