The following methods exposed by the meshOp Struct provide advanced access to the mesh faces.
Methods:
<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.
<point3> meshop.getFace <Mesh mesh> <int faceIndex>
Available in 3ds Max 2018.2 and higher: 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>
Available in 3ds Max 2018.2 and higher: Returns the face vertex indices for the indexed faces as a point3 array. Each component of each point3 value contains a vertex index.
The facelist
parameter can be #all
, in which case all faces in the mesh are returned.
meshop.getFacesByMatId <Mesh mesh> <material_id>
This method returns an array of faces from the specified mesh
that match the specified material_id
.
<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.
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.
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.
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>
.
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
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
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.
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
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
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.
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
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
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
For more mesh-related methods, see