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
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);
}
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?
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;
}