this one is a little daft but need a way of computing a curvecontrol value without a rollup being present this will do the job.
def_visible_primitive(computeCurveValue, "computeCurveValue");
Value* computeCurveValue_cf(Value **arg_list, int count)
{
check_arg_count(computeCurveValue, 2, count);
float fx = arg_list[0]->to_float();
Array* thePoints = (Array*)arg_list[1];
type_check(thePoints, Array, "computeCurveValue");
if(thePoints->size == 0) return Float::intern(0.0);
int npnts = thePoints->size;
ICurveCtl* curvecontrol = (ICurveCtl *)CreateInstance(REF_MAKER_CLASS_ID,CURVE_CONTROL_CLASS_ID);
if(!curvecontrol)
return &undefined;
curvecontrol->SetNumCurves(1);
ICurve* curve = curvecontrol->GetControlCurve(0);
curve->SetNumPts(npnts);
for(int i = 0; i < npnts; i++)
{
Array* point = (Array*)thePoints->data[i];
type_check(point, Array, "computeCurveValue");
CurvePoint cp;
cp.p = point->data[0]->to_point2();
cp.in = point->data[1]->to_point2();
cp.out = point->data[2]->to_point2();
cp.flags = point->data[3]->to_int();
curve->SetPoint(0,i,&cp,TRUE,FALSE);
}
float result = curve->GetValue(0,fx);
curvecontrol->DeleteThis();
return Float::intern(result);
}
the function takes the fx value as the first argument and an array of arrays with the format
#(<point2> pos, <point2> in, <point2>out, <integer> flags)
eg...
curveData = #(#([0,1], [0,0], [0,0], 259), #([0.225072,1], [-0.0869597,0], [0.400793,0], 3), #([1,0], [-0.226655,0.519355], [0,0], 275));
computeCurveValue 0.5 curveData
the flags follow the sdk definition and can be decoded in mxs as follows:
CURVEP_BEZIER = 1;
CURVEP_CORNER = 2;
CURVEP_LOCKED_Y = 3;
CURVEP_LOCKED_X = 4;
CURVEP_SELECTED = 5;
CURVEP_ENDPOINT = 9;
CURVEP_NO_X_CONSTRAINT = 10;
fn GetCurvePntFlags cp =
(
flags = 0;
flags = bit.set flags CURVEP_BEZIER cp.bezier;
flags = bit.set flags CURVEP_CORNER cp.corner;
flags = bit.set flags CURVEP_LOCKED_Y cp.lock_x;
flags = bit.set flags CURVEP_LOCKED_X cp.lock_y ;
flags = bit.set flags CURVEP_SELECTED cp.selected;
flags = bit.set flags CURVEP_ENDPOINT cp.end;
flags = bit.set flags CURVEP_NO_X_CONSTRAINT cp.noXConstraint;
flags;
)
fn SetCurvePntFlags cp flags =
(
cp.bezier = bit.get flags CURVEP_BEZIER;
cp.corner = bit.get flags CURVEP_CORNER;
cp.lock_x = bit.get flags CURVEP_LOCKED_Y;
cp.lock_y = bit.get flags CURVEP_LOCKED_X;
cp.selected = bit.get flags CURVEP_SELECTED;
cp.noXConstraint = bit.get flags CURVEP_NO_X_CONSTRAINT;
)
though they make no difference to the result so passing 0 is also an option. An useful extension to this function is to handle an array of fx params and return an array of results.
a cleaner version using max autoptr to make sure memory is returned on any thrown error.
Value* computeCurveValue_cf(Value **arg_list, int count)
{
check_arg_count(computeCurveValue, 2, count);
float fx = arg_list[0]->to_float();
Array* thePoints = (Array*)arg_list[1];
type_check(thePoints, Array, "computeCurveValue");
if(thePoints->size == 0) return Float::intern(0.0);
int npnts = thePoints->size;
MaxSDK::DeleteThisAutoPtr<ICurveCtl> curvecontrol((ICurveCtl *)CreateInstance(REF_MAKER_CLASS_ID,CURVE_CONTROL_CLASS_ID));
if(!curvecontrol.Get())
return &undefined;
curvecontrol->SetNumCurves(1);
ICurve* curve = curvecontrol->GetControlCurve(0);
curve->SetNumPts(npnts);
for(int i = 0; i < npnts; i++)
{
Array* point = (Array*)thePoints->data[i];
type_check(point, Array, "computeCurveValue");
if(point->size != 4)
return &undefined;
CurvePoint cp;
cp.p = point->data[0]->to_point2();
cp.in = point->data[1]->to_point2();
cp.out = point->data[2]->to_point2();
cp.flags = point->data[3]->to_int();
curve->SetPoint(0,i,&cp,TRUE,FALSE);
}
float result = curve->GetValue(0,fx);
return Float::intern(result);
}
there is quite a few times in mxs where you need to use the construct on a bit array
index = (ba as array)[1]
to retrieve the equivalent array index of the first set bit in a bit array. So it got me thinking whether a sdk function would return this faster than the conversion from bitarray to array. Well it turns out yes anywhere from 12 to 600+ times quicker depending on the size of the bit array.
so anyway heres the function
def_visible_primitive(firstIndex, "firstIndex");
Value* firstIndex_cf(Value **arg_list, int count)
{
check_arg_count(firstIndex, 1, count);
BitArray& ba = arg_list[0]->to_bitarray();
if(ba.IsEmpty())
return &undefined;
int nbits = ba.GetSize();
for(int i = 0; i < nbits; ++i)
if(ba[i])
return Integer::intern(i+1);
return &undefined;
}
i found a huge difference of sdk VS mxs methods especially for memory use. so i’ve added this pair to my extension as well:
def_visible_primitive(firstBit, "firstBit");
Value* firstBit_cf(Value** arg_list, int count)
{
check_arg_count(firstBit, 1, count);
if (is_bitarray(arg_list[0]))
{
BitArray bits = arg_list[0]->to_bitarray();
if (bits.AnyBitSet()) for (int k=0; k < bits.GetSize(); k++) if (bits[k]) return Integer::intern(k+1);
}
return &undefined;
}
def_visible_primitive(lastBit, "lastBit");
Value* lastBit_cf(Value** arg_list, int count)
{
check_arg_count(lastBit, 1, count);
if (is_bitarray(arg_list[0]))
{
BitArray bits = arg_list[0]->to_bitarray();
if (bits.AnyBitSet()) for (int k=bits.GetSize()-1; k >= 0; k--) if (bits[k]) return Integer::intern(k+1);
}
return &undefined;
}
i’ve found a problem… it doesn’t work for Edit Spline modifier for other then first spline.
try to make two lines. add modifier to one of them and attach the second. the function doesn’t see second spline in this case. any thoughts?
ok. the problem fixed.
replace the way how you get the Object:
TimeValue t = MAXScript_time();
Object *obj = node->EvalWorldState(t).obj;
//Object* obj = node->GetObjectRef();
//if (obj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
//obj = ((IDerivedObject*)obj)->FindBaseObject();
no idea, the spline accessing code is boiler plate stuff ripped straight from avg_DLX.cpp from the mxsagni project in the samples.
here is a version that support any shape class objects (line, rectangle, circle, donut, etc), and any spline modifiers on top:
def_visible_primitive(getSplineShapeSteps, "getSplineShapeSteps");
Value* getSplineShapeSteps_cf(Value **arg_list, int count)
{
check_arg_count_with_keys(getSplineShapeSteps, 1, count);
INode* node = arg_list[0]->to_node();
TimeValue t = MAXScript_time();
Object *obj = node->EvalWorldState(t).obj;
if (obj->CanConvertToType(Class_ID(SPLINESHAPE_CLASS_ID, 0)))
{
MAXNode* shape = (MAXNode*)arg_list[0];
SplineShape* spline = (SplineShape*)obj->ConvertToType(t, Class_ID(SPLINESHAPE_CLASS_ID,0));
int index = key_arg_or_default(index, Integer::intern(1))->to_int();
if (index < 1 || index > spline->shape.splineCount) return new Array(0);
//throw RuntimeError (GetString(IDS_SHAPE_SPLINE_INDEX_OUT_OF_RANGE), Integer::intern(index));
// grab the polyshape from our spline
PolyShape pshape;
spline->MakePolyShape(t, 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;
Array* steps = new Array(numPts);
Point3 p;
for(int i = 0; i < numPts; i++)
{
p = (*pline)[i].p;
shape->object_to_current_coordsys(p);
steps->append(new Point3Value(p));
}
return steps;
}
return &undefined;
}
please let me know if you will find any problems. Klunk, thanks again.
this is a little more correct version (returns points always in world space)
def_visible_primitive(getShapeSteps, "getShapeSteps");
Value* getShapeSteps_cf(Value **arg_list, int count)
{
check_arg_count_with_keys(getShapeSteps, 1, count);
INode* node = arg_list[0]->to_node();
TimeValue t = MAXScript_time();
Object *obj = node->EvalWorldState(t).obj;
if (obj->CanConvertToType(Class_ID(SPLINESHAPE_CLASS_ID, 0)))
{
SplineShape* spline = (SplineShape*)obj->ConvertToType(t, Class_ID(SPLINESHAPE_CLASS_ID,0));
int index = key_arg_or_default(index, Integer::intern(1))->to_int();
if (index < 1 || index > spline->shape.splineCount) return new Array(0);
//throw RuntimeError (GetString(IDS_SHAPE_SPLINE_INDEX_OUT_OF_RANGE), Integer::intern(index));
PolyShape pshape;
spline->MakePolyShape(t, pshape, PSHAPE_BUILTIN_STEPS, spline->shape.optimize);
PolyLine* pline = &pshape.lines[index-1];
int numPts = pline->numPts;
Array* steps = new Array(numPts);
Point3 p;
Matrix3 tm = node->GetObjectTM(t);
for(int i = 0; i < numPts; i++)
{
p = (*pline)[i].p * tm;
steps->append(new Point3Value(p));
}
return steps;
}
return &undefined;
}
edited… the bug fixed. we have to use GetObjectTM instead of GetNodeTM
the function is super useful. that’s the great support for making your own ‘lofter kind’ scripted plugins
following this thread http://forums.cgsociety.org/showthread.php?f=98&t=1156054 i’ve added two mxs extension functions:
they work by analogy with GetWeatherFileSpecificTimeAndDate and SetWeatherFileSpecificTimeAndDate…
so the mxs using is:[i]
[/i]
getDaylightTimeAndDate <&point3>timeOfDay <&point3>date[i]
[/i]setDaylightTimeAndDate <&point3>timeOfDay <&point3>date[i]
[/i][i]
[/i]
#include <IDaylightSystem.h>
IDaylightSystem* getDaylightSystem(Value *node)
{
IDaylightSystem* ds = NULL;
if is_node(node)
{
Object *obj = node->to_node()->GetObjectRef();
BaseInterface* bi = obj->GetInterface(DAYLIGHT_SYSTEM_INTERFACE);
if (bi) ds = dynamic_cast<IDaylightSystem*>(bi);
}
return ds;
}
def_visible_primitive(getDaylightTimeAndDate, "getDaylightTimeAndDate");
Value* getDaylightTimeAndDate_cf(Value **arg_list, int count)
{
check_arg_count(getDaylightTimeAndDate, 3, count);
IDaylightSystem* ds = getDaylightSystem(arg_list[0]);
if ((ds != NULL) && is_thunk(arg_list[1]) && is_thunk(arg_list[2]))
{
Thunk* time = arg_list[1]->to_thunk();
Thunk* date = arg_list[2]->to_thunk();
Value* timeValue = new Point3Value(ds->GetTimeOfDay());
Value* dateValue = new Point3Value(ds->GetDate());
time->assign(timeValue);
date->assign(dateValue);
return &ok;
}
return &undefined;
}
def_visible_primitive(setDaylightTimeAndDate, "setDaylightTimeAndDate");
Value* setDaylightTimeAndDate_cf(Value **arg_list, int count)
{
check_arg_count(setDaylightTimeAndDate, 3, count);
IDaylightSystem* ds = getDaylightSystem(arg_list[0]);
if ((ds != NULL) && is_thunk(arg_list[1]) && is_thunk(arg_list[2]))
{
Thunk* time = arg_list[1]->to_thunk();
Thunk* date = arg_list[2]->to_thunk();
ds->SetTimeOfDay(time->eval()->to_point3());
ds->SetDate(date->eval()->to_point3());
return &ok;
}
return &undefined;
}
(please let me know if you will see any problems)
thanks, Klunk for this set of very useful functions… the only problem I have is to get MNMesh updated. as you see i’ve modifies the function above to return the number of nspec faces. the function returns 0 all the time, but the right number was allocated and built! what do we have to update the return the right number?
thanks
finally found the correct method to build the MNNormalSpec from scratch. Create a new MNNormalSpec and set the parent to the mesh and call [i]CheckNormals()[/i] then copy to poly mesh.