Meshop Face Methods
The following methods exposed by the
meshOp Struct provide advanced access to the mesh faces.
Face Number
<integer>meshop.getNumFaces <Mesh mesh>
Returns the number of faces in the mesh.
meshop.setNumFaces <Mesh mesh> <int>
Sets the number of faces in the mesh.
Get Face
<point3> meshop.getFace <Mesh mesh> <int faceIndex>
NEW in 3ds Max 2018.2: Returns the face vertex indices for the indexed face as a point3. Each component
of the point3 value contains a vertex index.
<point3_array> meshop.getFaces <Mesh mesh> <facelist>
NEW in 3ds Max 2018.2: Returns the face vertex indices for the indexed faces as a point3 array. Each component
of each point3 value contains a vertex index.
Get Face Info
<float>meshop.getFaceArea <Mesh mesh> <facelist>
Returns the area of the specified faces as a float.
FOR EXAMPLE
|
--Select all faces with area smaller than 20.0
obj = sphere radius:20 --create a Sphere
convertToMesh obj --collapse to EditableMesh
faceSel= #() --init. an array
--Loop through all faces in the mesh,
--compare the face area to the threshold and
--append the face to the array to be selected
for f = 1 to obj.numfaces do
if meshop.getFaceArea obj #{f} < 20.0 then
append faceSel f
setFaceSelection obj faceSel --select all collected faces
update obj --update the mesh
max modify mode --switch to modify mode
select obj --select the mesh
subObjectLevel = 3 --switch to face sub-object level
|
|
<point3>meshop.getBaryCoords <Mesh mesh> <int faceIndex> <point3 pos> node:<node=unsupplied>
The
barycentric coordinates of the specified point in the plane of the specified face, relative to the face.
If
<mesh> is a node, or if
<mesh> is an Editable Mesh or a Mesh value and
node: is specified, the position is in the current coordinate system context.
If
<mesh> is an Editable Mesh or a Mesh value and
node: is not specified, the position is in the mesh's local coordinate system.
<point3>meshop.getFaceCenter <Mesh mesh> <int faceIndex> node:<node=unsupplied>
Returns the center of the specified face. The face center is the average of the face's
three vertex coordinates.
If
<mesh> is a node, or if
<mesh> is an Editable Mesh or a Mesh value and
node: is specified, the position is in the current coordinate system context.
If
<mesh> is an Editable Mesh or a Mesh value and
node: is not specified, the position is in the mesh's local coordinate system.
meshop.getFaceRNormals <Mesh mesh> <int faceIndex> node:<node=unsupplied>
Returns a three element array of the render normals for the face's three vertices.
If
<mesh> is a node, or if
<mesh> is an Editable Mesh or a Mesh value and
node: is specified, the position is in the current coordinate system context. If
<mesh> is an Editable Mesh or a Mesh value and
node: is not specified, the position is in the mesh's local coordinate system.
Delete Faces
meshop.deleteFaces <Mesh mesh> <facelist> delIsoVerts:<boolean=true>
Deletes the specified faces. If
delIsoVerts: is
true , any isolated vertices are deleted.
EXAMPLE
|
obj = geosphere() --create a GeoSphere
convertToMesh obj --collapse to EditableMesh
--Delete all faces in the mesh but leave the vertices:
meshop.deleteFaces obj #{1..obj.numfaces} delIsoVerts:false
update obj --update the mesh
max modify mode --switch to modify mode
select obj --select the mesh
SubObjectLevel = 1 --switch to vertex level to see the vertices
|
|
meshop.removeDegenerateFaces <Mesh mesh>
Deletes any degenerate faces in the mesh. A degenerate face has two or more equal
vertex indices.
meshop.removeIllegalFaces <Mesh mesh>
Deletes any illegal faces in the mesh. An illegal face has one or more vertex indices
that are out of range.
Hidden Faces
meshop.setHiddenFaces <Mesh mesh> <facelist>
Sets the specified faces as hidden, and non-specified faces as visible.
meshop.getHiddenFaces <Mesh mesh>
Returns a bitarray of size=(#faces in
mesh) with bits set for all faces that are hidden.
Bevel, Extrude, Clone, Collapse and Detach Faces
meshop.bevelFaces <Mesh mesh> <facelist> <float height> <float outline> dir:<{<point3 dir> | #independent | #common}=#independent> node:<node=unsupplied>
Moves the specified faces by
<height> and outlines them by
<outline> .
NOTE:This method does not correspond to the Bevel functionality in the EditableMesh UI.
It moves and outlines faces without creating new faces. See
meshop.ExtrudeFaces further on this page for the corresponding method that creates new beveled faces.
The direction the faces are moved is determined by
<dir> .
If
dir is a point3 value, the faces will be moved in that direction.
(If
<mesh> is a node, or if
<mesh> is an Editable Mesh or a Mesh value and
node: is specified, the direction is in the current coordinate system context. If
<mesh> is an Editable Mesh or a Mesh value and
node: is not specified, the direction is in the mesh's local coordinate system.)
If
dir:#independent is specified, the faces are moved based on the individual face normals.
If
dir:#common is specified, the faces are moved based on the face cluster normals.
meshop.extrudeFaces <Mesh mesh> <facelist> <float height> <float outline> dir:<{<point3 dir> | #independent | #common}=#independent> node:<node=unsupplied>
Creates new faces corresponding to the specified faces, and then moves the new faces
by
<height> and outlines them by
<outline> .
The direction the faces are moved is determined by
dir .
If
dir: is a point3 value, the faces will be moved in that direction.
(If
<mesh> is a node, or if
<mesh> is an Editable Mesh or a Mesh value and
node: is specified, the direction is in the current coordinate system context. If
<mesh> is an Editable Mesh or a Mesh value and
node: is not specified, the direction is in the mesh's local coordinate system.)
If
dir:#independent is specified, the faces are moved based on the individual face normals.
If
dir:#common is specified, the faces are moved based on the face cluster normals.
EXAMPLE
|
obj = geosphere() --create a GeoSphere
convertToMesh obj --collapse to Editable_Mesh
--Loop from half the face count back to 1.
--Since ExtrudeFaces creates new faces, counting backwards
--ensures the loop will go only through the original faces:
for f = obj.numfaces/2 to 1 by -1 do
meshop.extrudeFaces obj #{f} 5.0 -1.0 --extrude the single face
update obj --update the mesh
|
|
meshop.cloneFaces <Mesh mesh> <facelist>
Clones the specified faces.
EXAMPLE
|
obj = geosphere() --create a GeoSphere
convertToMesh obj --collapse to EditableMesh
--Clone half of the faces - they get selected automatically:
meshop.cloneFaces obj #{1..obj.numfaces/2}
--Offset the selected Faces at 20 units along their local normals
meshop.bevelFaces obj (getFaceSelection obj) 20 0 dir:#independent
update obj--update the mesh
|
|
meshop.collapseFaces <Mesh mesh> <facelist>
Collapses the specified faces.
EXAMPLE
|
obj = geosphere() --create a GeoSphere
convertToMesh obj --collapse to EditableMesh
--Collapse half the faces:
meshop.collapseFaces obj #{1..obj.numfaces/2}
update obj --update the mesh
|
|
meshop.detachFaces <Mesh mesh> <facelist> delete:<boolean=true> asMesh:<boolean=false>
Detaches the specified faces.
If
delete: is
true , the faces are deleted after being detached. If
delete: is
false , the faces are not deleted.
If
asMesh: is
true , the faces are detached and returned as a Mesh value. If
asMesh: is
false , the faces are detached as an element in the mesh and a value of OK is returned.
EXAMPLE
|
--Let's detach faces as element inside the original mesh:
obj = geosphere pos:[100,0,0] --create a GeoSphere
convertToMesh obj --collapse to EditableMesh
--Detach half of the faces as element, delete the originalfaces
meshop.detachFaces obj #{1..obj.numfaces/2} delete:true
update obj --update the mesh
--In this example, the same mesh will be detached as a separate object
obj = geosphere pos:[0,0,0] --create another GeoSphere
convertToMesh obj --collapse to EditableMesh
-- Detach the faces as Mesh and
--get the resulting TriMesh in a variable:
newMesh = meshop.detachFaces obj #{1..obj.numfaces/2} delete:true asMesh:true
update obj --update the mesh
emesh = Editable_mesh() --create an empty Editable_mesh
emesh.mesh = newMesh --assign the detached faces to the new mesh
update emesh --update the mesh
|
|
Divide Faces
meshop.divideFace <Mesh mesh> <int faceIndex> baryCoord:<point3=unsupplied>
Divides the specified face.
If
baryCoord: is specified, the new vertex will be created at the corresponding position.
If
baryCoord: is not specified, the new vertex will be created at the center of the face.
See also
BarycentricCoordinates
EXAMPLE
|
obj = plane() --create a plane
convertToMesh obj --collapse to Editable_Mesh
--Loop backwards through all original faces.
--This will ignore any new faces added to the face array
for f = obj.numfaces to 1 by -1 do
meshop.divideFace obj f barycoord:[0.1,0.1,0.5] --divide the face
update obj --update the mesh
--Note that if the sum of the barycentric coordinates' components is not
--equal to 1.0, the values will be scaled proportionally. Thus the above
--coordinates are equivalent to [0.142857,0.142857,0.714285].
|
|
meshop.divideFaceByEdges <Mesh mesh> <int faceIndex> <int edge1Index> <float edge1f> <int edge2Index> <float edge2f> fixNeighbors:<boolean=true> split:<boolean=false>
Divides the specifed face. New vertices are created on the specified edges at the
specified fractional distance along their length.
If
fixNeighbors: is
true , the face on the other side of the edges, that is, the "reverse faces", should be
divided as well to prevent the introduction of a seam.
If
split: is
true , separate vertices for the two halves of the edges will be created, splitting the
mesh open along the diagonal(s).
EXAMPLE
|
obj = plane lengthsegs:1 widthsegs:1 --create a simple plane of 2 faces
convertToMesh obj --collapse to Editable_mesh
obj.allEdges = true --show all edges
--Divide face 1 connecting edges 2 and 3 at 0.25 resp. 0.75 along
--their length. This gives a new edge parallel to edge 1
meshop.divideFaceByEdges obj 1 2 0.25 3 0.75
update obj --update the mesh
|
|
meshop.divideFaces <Mesh mesh> <facelist>
Divides the specified faces, with new vertices created at the center of the faces.
EXAMPLE
|
--The following code does the same as Tessellate in Face-Center mode
obj = plane() --create a plane
convertToMesh obj --collapse to Editable_Mesh
--Divide all faces by placing a new vertex at the face center:
meshop.divideFaces obj #{1..obj.numfaces}
update obj --update the mesh
|
|
Explode Faces
meshop.explodeAllFaces <Mesh mesh> <float threshold>
"Explodes" the mesh into separate elements.
<threshold> specifies the angle between faces that indicates whether they should be in the same
or different element.
WARNING!
|
The float threshold parameter is currently expected in
radians!
|
EXAMPLE
|
obj = geosphere() --create a GeoSphere
convertToMesh obj --collapse to EditableMesh
--Explode all faces in the mesh to elements, disregarding the angle:
meshop.explodeAllFaces obj 0
update obj --update the mesh
|
|
meshop.explodeFaces <Mesh mesh> <facelist> <float threshold>
"Explodes" the specified faces into separate elements.
<threshold> specifies the angle between faces that indicates whether they should be in the same
or different element.
EXAMPLE
|
obj = geosphere() --create a GeoSphere
convertToMesh obj --collapse to EditableMesh
--Explode half of the faces regardless the angle
meshop.explodeFaces obj #{1..obj.numfaces/2} 0
--Move the exploded faces (selected by the explodeFaces method)
--along their normals at 10 units without outline:
meshop.bevelFaces obj (getFaceSelection obj) 10 0 dir:#independent
update obj --update the mesh
|
|
Get A Using B
meshop.getVertsUsingFace <Mesh mesh> <facelist>
Returns a bitarray of size=(#vertices inmesh) with bits set for all vertices that
are used by the specified faces.
SCRIPT
|
macroScript Face2VertSel category:"MXS Help"
(
--make sure a single EMesh object is selected
on isEnabled return
selection.count == 1 and classof selection[1] == Editable_Mesh
on execute do
(
obj = selection[1]--get selected object
faceSel = getFaceSelection obj--get selected Faces
--get Verts of selected Faces:
vertsSel = meshop.getVertsUsedFace obj faceSel
setVertSelection obj vertsSel --select the Verts
update obj --update the mesh
max modify mode--switch to Modify panel
subObjectLevel = 1--set Vertex SO level
)
) --end macro
|
NOTE:
A common mistake would be to pass an array with a single face to
meshop.getVertsUsingFace() and to assume that the result converted to an array is the definition of the face
(all its vertices).
This would be the case only in the special case where the face definition happens
to have the vertex indices in ascending order. This is because the return value is
a bitarray where the indices are always sorted in ascending order.
For example, it a face definition is [4,6,9], the assumption would be correct. But if the face
definition was [6,9,4], the result of
meshop.getVertsUsingFace() converted to array would be #(4,6,9) and would not match. Any operations requiring
the correct vertex order, for example those involving calculations with barycentric
coordinates would turn out incorrect.
The
correct approach for getting the three vertices of a face in the correct order is by calling
getFace() and accessing the .x, .y and .z components of the returned Point3 value.
meshop.getEdgesUsingFace <Mesh mesh> <facelist>
Returns a bitarray of size=(#edges in
mesh ) with bits set for all edges that are used by the specified faces.
SCRIPT
|
macroScript Face2EdgeSel category:"MXS Help"
(
--make sure a single EMesh object is selected
on isEnabled return
selection.count == 1 and classof selection[1] == Editable_Mesh
on execute do
(
obj = selection[1] --get selected object
faceSel = getFaceSelection obj --get selected Faces
--get Edges of selected Faces:
edgeSel = meshop.getEdgesUsingFace obj faceSel
setEdgeSelection obj edgeSel --select the edges
update obj --update the mesh
max modify mode --switch to Modify panel
subObjectLevel = 2 --set Edge SO level
)
) --end macro
|
meshop.getPolysUsingFace <Mesh mesh> <facelist> ignoreVisEdges:<boolean=false> threshhold:<float=45.>
Returns a bitarray of size=(#faces inmesh) with bits set for all faces that are in
'polygons' containing the specified faces. The definition of a polygon is all faces
sharing invisible edges with edge angles below the threshhold angle. The default threshhold
angle is 45 degrees. If
ignoreVisEdges: is set to
true , the edge visibility is ignored but the threshhold is still relevant.
meshop.getFacesUsingVert <Mesh mesh> <vertlist>
Returns a bitarray of size=(#faces in mesh) with bits set for all faces that use the
specified vertices.
EXAMPLE
|
obj =Sphere() --create a Sphere
convertToMesh obj --collapse to EditableMesh
--Get the faces used by the first vertex (north pole)
verts= meshop.getFacesUsingVert obj #{1}
--Add the faces used by the last vertex (south pole)
verts+= meshop.getFacesUsingVert obj #{obj.numverts}
--Select the faces used by both pole vertices
setFaceSelectionobjverts
update obj --update the mesh
max modify mode --switch to modify mode
select obj --select the mesh
subObjectLevel = 3 --switch to face sub-object level
|
|
SCRIPT
|
macroScript Vert2FaceSel category:"MXS Help"
(
--make sure a single EMesh object is selected
on isEnabled return
selection.count == 1 and classof selection[1] == Editable_Mesh
on execute do
(
obj = selection[1] --get selected object
vertSel = getVertSelection obj --get selected Vertices
--get Faces of selected Verts:
faceSel = meshop.getFacesUsingVert obj vertSel
setFaceSelection obj vertsSel --select the Faces
update obj --update the mesh
max modify mode --switch to Modify panel
subObjectLevel =3 --set Face SO level
)
) --end macro
|
meshop.getElementsUsingFace <Mesh mesh> <facelist> fence:<facelist=unsupplied>
Returns a bitarray of size=(#faces inmesh) with bits set for all faces in elements
where at least one face in the element is specified in
<facelist>. If
fence: is specified, its faces are considered as barriers to the element and are not processed.
meshop.getVertsUsedOnlyByFaces <Mesh mesh> <facelist>
Returns a bitarray of size=(#vertices inmesh) with bits set for all vertices used
by the specified faces.
Flip and Unify Normals
meshop.unifyNormals <Mesh mesh> <facelist>
Unifies the normals of the specified faces.
meshop.flipNormals <Mesh mesh> <facelist>
Flips the normal of the specified faces.
FOR EXAMPLE
|
obj =sphere() --create a Sphere
convertToMesh obj --collapse to EditableMesh
--Flip the normals of the top half of the faces:
meshop.flipNormals obj #{1..obj.numfaces/2}
update obj --update the mesh
|
|
Make Planar
meshop.makeFacesPlanar <Mesh mesh> <facelist>
Moves the specified faces so that they are planar.
EXAMPLE
|
obj = sphere()
convertToMesh obj
--make the faces of the top half of the sphere planar:
meshop.makeFacesPlanar obj #{1..obj.numfaces/2.0}
update obj
|
|
AutoSmooth Faces
meshop.autoSmooth <Mesh mesh> <facelist> <float threshold>
Smooths the specified faces based on the threshold angle.
FOR EXAMPLE
|
obj =geosphere() --create a GeoSphere
convertToMesh obj --collapse to EditableMesh
--Smooth half of the faces:
meshop.autoSmooth obj #{obj.numfaces/2..obj.numfaces} 0.0
update obj --update the mesh
|
|