maya c++ API 创建一个polySphere

API 学习

Posted by elmagnifico on June 6, 2019

Maya-C++ API

如何使用maya写一个polySphere,就像maya自身的创建一个球

或者是类似MEL/python的命令

maya.cmds.polySphere()

分析

首先maya里的结构是:

transform -> mesh

polySphere -> mesh

mesh -> shadingEngine(shadingGroup)

material(lambert) -> shadingEngine(shadingGroup)

总的来说就是要如何创建一个transform,然后创建一个mesh,连接二者.

创建一个shadingGroup,然后创建一个材质 lambert,连接二者.

最后再把 mesh 分配到这个 shadingGroup,就ok了.

看起来非常简单,实际上很多细节不注意就错了.

创建 transfrom 与 mesh

MFnDependencyNode fnPolySp;
MFnDagNode fnPolyTrans;
MFnDagNode fnPolyShape;
MDGModifier	dgMod;

MObject	objPolySp = fnPolySp.create("polySphere");
MObject objPolyTrans = fnPolyTrans.create("transform");
MObject objPolyShp = fnPolyShape.create("mesh", objPolyTrans);

MPlug srcPlug = fnPolySp.findPlug("output");
MPlug destPlug = fnPolyShape.findPlug("inMesh");

dgMod.connect(srcPlug, destPlug);
dgMod.doIt();

创建 shadingGroup 和 lambert

如何创建 shadingGroup

这段代码出自 AbcImport/CreateSceneHelper.cpp 和 util.cpp

// create a shading group
MObject shadingGroup;

const MString& faceSetName(faceSet.getName().c_str());

// check if this SG already exists.
status = getObjectByName(faceSetName, shadingGroup);
if (shadingGroup.isNull())
{
    shadingGroup = createShadingGroup(faceSetName);
}

MStatus getObjectByName(const MString & name, MObject & object)
{
    object = MObject::kNullObj;

    MSelectionList sList;
    MStatus status = sList.add(name);
    if (status == MS::kSuccess)
    {
        status = sList.getDependNode(0, object);
    }

    return status;
}
MObject createShadingGroup(const MString& iName)
{
    MStatus status;

    MFnSet fnSet;
    MSelectionList selList;
    MObject shadingGroup = fnSet.create(selList,
                                        MFnSet::kRenderableOnly,
                                        &status);
    if (status != MS::kSuccess)
    {
        MString theError("Could not create shading engine: ");
        theError += iName;
        MGlobal::displayError(theError);

        return shadingGroup;
    }

    fnSet.setName(iName);

    return shadingGroup;
}

完全API写法
MObject lmmo;
MFnLambertShader lm;
MFnSet fnSet;
MSelectionList selList;
MDGModifier	dgMod1;

lmmo = lm.create();
lm.setName("mylambert");

MObject shadingGroup = fnSet.create(selList,MFnSet::kRenderableOnly);
fnSet.setName("myshadinggroup");

MPlug srcPlug1 = lm.findPlug("outColor", status);
MPlug destPlug1 = fnSet.findPlug("surfaceShader", status);


// check connection
MPlugArray mpa;
destPlug1.connectedTo(mpa, true, false);

// break it
dgMod1.disconnect(mpa[0], destPlug1);

dgMod1.connect(srcPlug1, destPlug1);
dgMod1.doIt();
问题

其中遇到最致命的问题就是在把lambert连接到shadingGroup,默认创建的lambert会自动连接到默认的shadinggroup中. 在上面的代码里如果不加入打断,就会在最后的 dgMod1.doIt(); 发现返回了失败.

而maya的脚本提示行里显示的是目标不可写,也就是shadingGroup.surfaceShader属性不可写,而不可写的原因是已有连接存在.

而平常使用python的时候一般都是直接建好以后,直接分配就完事了,根本不会注意到其实创建的时候就是没有输入连接的,或者最后连接的时候用了 force 参数,强制连接,这样就算是有连接也会被打断以后再连接上.

custom = cmds.shadingNode("lambert", asShader = True, name = name1)
SG = cmds.sets(renderable=True, noSurfaceShader=True, empty=True, name = name1)
cmds.connectAttr(custom+".outColor", SG+".surfaceShader", force=True)
cmds.select(name1, r = True)
cmds.hyperShade(assign = SG)

mesh 分配给 shadingGroup

官方例程

maya 的官方例程里面有很多地方分配材质或者shadingGroup的时候都是直接调MEL,而不是用API来写,导致很多人用的时候也是学官方的这个方法,非性能相关的时候没什么问题,当有性能问题的时候这里就会成为瓶颈.

例程出自 \devkit\plug-ins\polyPrimitiveCmd

MStatus polyPrimitive::assignShadingGroup(MObject transform, MString groupName)
{
    MStatus st;

    // Get the name of the mesh node.
    //
    // We need to use an MFnDagNode rather than an MFnMesh because the mesh
    // is not fully realized at this point and would be rejected by MFnMesh.
    MFnDagNode  dagFn(transform);
    dagFn.setObject(dagFn.child(0));

    MString     meshName = dagFn.name();

    // Use the DAG modifier to put the mesh into a shading group
    MString     cmd("sets -e -fe ");
    cmd += groupName + " " + meshName;
    st = dagMod.commandToExecute(cmd);
    checkErr(st, "Could not add mesh to shading group");

    // Use the DAG modifier to select the new mesh.
    cmd = MString("select ") + meshName;
    st = dagMod.commandToExecute(cmd);
    checkErr(st, "Could not select new mesh");

    return st;
}
完全API写法
MDagPath dp;
MStatus status;
MDagPath::getAPathTo(objPolyTrans, dp);

dp.extendToShape();
if (fnSet.restriction() != MFnSet::kRenderableOnly)
    return MS::kFailure;

status = fnSet.addMember(dp);
if (status != MS::kSuccess)
{
    cerr << " ! MShadingGroup::assignToShadingGroup could not add Dag/Component to SG ! " << endl;
}

总结

最后就可以看到生成的一个多边形球体,除了命名和默认的不一样以外,结构上是一样的.

通过这里创建一个球体,大致了解 maya api 的编程模型,后面按照这个思路来基本就可以了.

参考

http://ewertb.mayasound.com/api/api.008.php

https://forums.cgsociety.org/t/create-primitives-with-the-api/1444485/15

https://forums.autodesk.com/t5/maya-programming/mesh-texturing/m-p/4233962/highlight/false#M2608

https://forums.autodesk.com/t5/maya-programming/material-outcolor-gt-surfaceshader-fail-bug-or-something-wrong/m-p/8872435/thread-id/10076/highlight/false#M10077

http://ewertb.mayasound.com/api/api.021.php