Having probelem with OpenMaya create a basic one face mesh with four vertices

I’m trying to get a better understanding of how openmaya works, especially on how to construct my own geometry. I started the most basic example using chatGPT, create a face with the given 4 point:

import maya.api.OpenMaya as om

def createFace():
    # Define the points
    points = [om.MPoint(1, 0, 1), om.MPoint(-1, 0, 1),
              om.MPoint(-1, 0, -1), om.MPoint(1, 0, -1)]

    # Convert to MFloatPointArray
    floatPoints = om.MFloatPointArray()
    for point in points:
        floatPoints.append(om.MFloatPoint(point.x, point.y, point.z))

    # Create the mesh
    meshFn = om.MFnMesh()
    numVertices = 4
    numPolygons = 1

    # Polygon counts (number of vertices per face)
    polygonCounts = [4]

    # Polygon connects (vertex indices for each face)
    polygonConnects = om.MIntArray([0, 1, 2, 3])

    # Create the face
    meshFn.create(numVertices, numPolygons, floatPoints, polygonCounts, polygonConnects)

# Run the function
createFace()

And it Failed, maya gave a crazy error message:
Error: SystemError: file line 25: <built-in method length_hint of iterator object at 0x00000239AE5D3C70> returned a result with an exception set
chatGPT 3.5 or 4.0 can’t make sense of this error, keep changing point’s data type to different maya object, but maya still throws out the same error when calling meshFn.create().
I can easily create this face with

cmds.polyCreateFacet(p=[(1, 0, 1), (-1, 0, 1), (-1, 0, -1), (1, 0, -1)])

image
Even thought it’s normal is flip, but that’s the next thing I need to worry about…

Hope someone can help!

The numVertices and numPolygons flags, in this case, are not acceptable!

OpenMaya.MFnMesh Class Reference

OpenMaya.MFnMesh.create()
create(vertices, polygonCounts, polygonConnects, uValues=None, vValues=None, parent=kNullObj) -> MObject

Creates a new polygonal mesh and sets this function set to operate on it.
This method is meant to be as efficient as possible and thus assumes that all the given data is topologically correct.
If no parent is provided then a transform node will be created and returned and a mesh node will be created and parented under the transform.

Signature: create(vertices, polygonCounts, polygonConnects, uValues=None, vValues=None, parent=kNullObj)

vertices: sequence of MPoints or MFloatPoints.

Positions of the mesh’s unique vertices.
Shared vertices should only appear once.
For example: a cube has 6 faces of 4 vertices each, but each vertex is shared by three faces, so there would only be 8 unique vertices in the mesh.

polygonCounts: sequence of ints.

One element per polygon in the mesh, giving the number of vertices in the polygon.
So if the mesh consists of a triangle and a rectangle then the counts would be [3, 4].

polygonConnects: sequence of ints.

Indices into the sequence of vertices, mapping them onto the individual polygons.
This sequence is partitioned according to the polygonCounts.
So if the counts were [3, 4] then the first 3 elements of polygonConnects would be the indices for the first polygon’s vertices and the next 4 elements would be the indices for the second polygon’s vertices.

The minimal version would look something like this:

import maya.api.OpenMaya as om

def create_face():
    vertices = om.MFloatPointArray(
                                   [om.MFloatPoint(1,0,1),
                                    om.MFloatPoint(-1,0,1),
                                    om.MFloatPoint(-1,0,-1),
                                    om.MFloatPoint(1,0,-1)
                                   ]
                                  )

    polygon_counts = om.MIntArray([4])
    polygon_connects = om.MIntArray([0,1,2,3])
    fn_mesh = om.MFnMesh()
    fn_mesh.create(vertices,           # vertices: sequence MFloatPoints or seq. MPoints
                   polygon_counts,     # polygonCount: sequence of ints
                   polygon_connects    # polygonConnects: sequence of ints
                  )

create_face()

Good luck!

It works, thanks, now how do I go one step forward, creat 2 faces with 6 vertices.

vertices = om.MFloatPointArray(
                                   [om.MFloatPoint(1,0,1),
                                    om.MFloatPoint(-1,0,1),
                                    om.MFloatPoint(-1,0,0),
                                    om.MFloatPoint(-1,0,-1),
                                    om.MFloatPoint(1,0,-1),
                                    om.MFloatPoint(1,0,0)
                                   ]
                                  )
# connect
polygon_connects = om.MIntArray([0,1,2,3,4,5])

If I connect like this, the result like below:


How to I connect the center two points?
And where can I learn more about constructing polygon with openmaya?

There are 2 things that make up a mesh: The vertices and the faces. Edges only exist as the borders of each face, and aren’t explicitly defined. So you’re not really “connecting the two center points”, you’re splitting the 6 sided polygon into two 4 sided polygons.

The vertex list of a mesh is easy to specify. It’s just a list of x/y/z points in space. You already understand that part.

The faces are a little more complicated.

It would have made it so much easier to just have a list of lists of the vertex indices that make up each face.
Using your example, you would make two faces with data like this.

verts = [(1,0,1), (-1,0,1), (-1,0,0), (-1,0,-1), (1,0,-1), (1,0,0)]
faces = [[0, 1, 2, 5], [5, 2, 3, 4]]

Each sub-list in faces just picks the verts at those indices, and makes a face out of it.
So one face is made up of verts[0], verts[1], verts[2], verts[5] from the [0, 1, 2, 5]
And the other face is made up of verts[5], verts[2], verts[3], verts[4] from the [5, 2, 3, 4]


Unfortunately, doing it the easy way like that is slow.

Because of how computers access memory, each sub-list would have to be fetched separately. Computers like accessing big chunks of memory all at once, so accessing little snippets of memory one at a time can slow things way down.

So maya uses a trick. It stores all the vertex indices as one long flat list.
[[0, 1, 2, 5], [5, 2, 3, 4]]
becomes
[0, 1, 2, 5, 5, 2, 3, 4]

But doing that loses some information: How long was each sub-list? So we have to store that information in a second list. In this case it would be [4, 4]

Using that trick the computer only has to get 2 chunks of data from memory instead of a chunk of data per-face.


If we’re not worried about speed (or if we just want the convenience), we can work with our data the “easy” way, and convert it to the maya way when we need to. This is how I usually work with things.

easyFaces = [[0, 1, 2, 5], [5, 2, 3, 4]]

connects = []
counts = []
for sublist in easyFaces:
    connects.extend(sublist)
    counts.append(len(sublist))

Or we could just fill the connects and counts variables directly.

connects = [0, 1, 2, 5, 5, 2, 3, 4]
counts = [4, 4]
2 Likes

OMG, that’s so helpful, and now the counts and connects MIntArray makes so much sense, and the resulted mesh, I can even guess it’s face index even it’s edge and vertice index, cuz how it was created. many thanks~

Just a minor warning… Maya explicitly stores the edge numbers when it saves out .ma or .mb files.

So if you ask a mesh for its counts/connects (using the MFnMesh.getVertices method) and try to figure out the edge indices just from that, you might get incorrect values.

Unfortunately there is no way to get or set the edge numbering when using the counts/connects mesh representation. And the maya function that lets you define the edge numbering seems to be C++ only.

But you can get the vertex and face indices just fine.

I will keep that in mind, Thanks~