Skip to content

Instantly share code, notes, and snippets.

@mottosso
Last active February 10, 2025 13:56
Show Gist options
  • Select an option

  • Save mottosso/e02a2d58482eae3e36be9d9cbd3040d1 to your computer and use it in GitHub Desktop.

Select an option

Save mottosso/e02a2d58482eae3e36be9d9cbd3040d1 to your computer and use it in GitHub Desktop.

Revisions

  1. mottosso revised this gist Feb 10, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -63,6 +63,6 @@ Your simplest option is to replace any devkit example with this source and build
    cd c:\maya_devkit\plug-ins\simpleSimulationNode
    $env:DEVKIT_LOCATION="c:\maya_devkit"
    cmake . -G Ninja
    cmake --build
    cmake --build .
    # [2/2] Linking CXX shared library simpleSimulationNode.mll
    ```
  2. mottosso created this gist Feb 4, 2025.
    68 changes: 68 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    ### dirtyNode

    The node has 3 attributes.

    - `shapeType`
    - `outputMesh`
    - `state`

    The affect each other, like so.

    ```cpp
    shapeType -> outputMesh -> state
    ```

    Reading from `state` should compute `outputMesh` which depend on `shapeType`. But `state` is not dirtied when `shapeType` changes. Why?

    <br>

    ### Usage

    ```py
    import os
    import sys
    from maya import cmds

    fname = "c:/maya_devkit/plug-ins/simpleSimulationNode/simpleSimulationNode.mll" # or .so on Linux
    cmds.loadPlugin(fname)

    node = cmds.createNode("dirtyNode")

    # Suceeds
    assert cmds.getAttr(node + ".state") == 1

    cmds.setAttr(node + ".shapeType", 2)

    # Fails
    assert cmds.getAttr(node + ".state") == 2


    # I can force `outputMesh` to be dirty, and cause `simulation` to be dirty
    cmds.setAttr(node + ".outputMesh", 0) # Should not be writable

    # Succeeds, but should be 2
    assert cmds.getAttr(node + ".state") == 0


    # Succeeds
    cmds.setAttr(node + ".outputMesh", 2) # Should not be writable
    assert cmds.getAttr(node + ".state") == 2
    ```

    <br>

    ### Build

    Your simplest option is to replace any devkit example with this source and build from there.

    1. Copy/paste the contents of `dirtynode.cpp` into `simpleSimulationNode.cpp`
    1. Launch a Visual Studio 2019+ environment
    2. Call the below.

    ```bash
    cd c:\maya_devkit\plug-ins\simpleSimulationNode
    $env:DEVKIT_LOCATION="c:\maya_devkit"
    cmake . -G Ninja
    cmake --build
    # [2/2] Linking CXX shared library simpleSimulationNode.mll
    ```
    146 changes: 146 additions & 0 deletions dirtynode.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,146 @@
    #include <maya/MPxNode.h>

    #include <maya/MFnNumericAttribute.h>

    #include <maya/MString.h>
    #include <maya/MTypeId.h>
    #include <maya/MPlug.h>
    #include <maya/MDataBlock.h>
    #include <maya/MGlobal.h>
    #include <maya/MDataHandle.h>

    #include <maya/MFnPlugin.h>


    class dirtyNode final : public MPxNode {
    public:
    MStatus compute(const MPlug& plug, MDataBlock& data) override;
    static void* creator() { return new dirtyNode(); }
    static MStatus initialize();

    public:
    // Attributes
    static MObject aShapeType;
    static MObject aOutputMesh;
    static MObject aState;

    public:
    static MTypeId id;
    static MString nodeName;
    };

    MTypeId dirtyNode::id = { 0x00081165 };
    MString dirtyNode::nodeName = { "dirtyNode" };

    MObject dirtyNode::aShapeType;
    MObject dirtyNode::aOutputMesh;
    MObject dirtyNode::aState;



    MStatus dirtyNode::initialize() {
    MFnNumericAttribute nAttr;
    MStatus status;

    aShapeType = nAttr.create("shapeType", "shty", MFnNumericData::kInt);
    nAttr.setDefault(1);
    nAttr.setKeyable(false);
    nAttr.setWritable(true); // Mark as user-provided (not computed)
    nAttr.setReadable(true);
    nAttr.setStorable(true);
    nAttr.setChannelBox(true);

    aOutputMesh = nAttr.create("outputMesh", "oume", MFnNumericData::kInt);
    nAttr.setKeyable(false);
    nAttr.setChannelBox(true);
    nAttr.setWritable(false); // Mark as computed (non-writable)
    nAttr.setStorable(false); // Don't store this computed value
    nAttr.setReadable(true);

    aState = nAttr.create("state", "stat", MFnNumericData::kInt);
    nAttr.setWritable(false);
    nAttr.setStorable(false);
    nAttr.setReadable(true);

    status = addAttribute(aShapeType);
    status = addAttribute(aOutputMesh);
    status = addAttribute(aState);

    status = attributeAffects(aShapeType, aOutputMesh);
    status = attributeAffects(aOutputMesh, aState);

    return MS::kSuccess;
    }


    MStatus dirtyNode::compute(const MPlug& plug, MDataBlock& data) {
    MStatus status;

    if (plug == aOutputMesh) {
    MDataHandle shapeTypeHandle = data.inputValue(aShapeType, &status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    int shapeType = shapeTypeHandle.asInt();

    // Compute outputMesh based on shapeType
    MDataHandle outputMeshHandle = data.outputValue(aOutputMesh, &status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    outputMeshHandle.setInt(shapeType); // Or more complex logic
    outputMeshHandle.setClean();

    MGlobal::displayInfo("Computed aOutputMesh");
    return MS::kSuccess;
    }

    else if (plug == aState) {

    // Get outputMesh value
    MDataHandle outputMeshHandle = data.inputValue(aOutputMesh, &status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    int outputMesh = outputMeshHandle.asInt();

    // Compute currentStatus based on outputMesh and initialStatus
    MDataHandle stateHandle = data.outputValue(aState, &status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    stateHandle.setInt(outputMesh);
    stateHandle.setClean();

    MGlobal::displayInfo("Computed aState");
    return MS::kSuccess;
    }

    // Let Maya handle it
    return MS::kUnknownParameter;
    }


    MStatus initializePlugin(MObject obj) {
    MStatus status;
    MFnPlugin plugin(obj, PLUGIN_COMPANY, "1.0", "Any");

    status = plugin.registerNode(
    dirtyNode::nodeName,
    dirtyNode::id,
    dirtyNode::creator,
    dirtyNode::initialize
    );
    if (!status) {
    status.perror("registerNode");
    return status;
    }

    return status;
    }


    MStatus uninitializePlugin(MObject obj) {
    MStatus status;
    MFnPlugin plugin(obj);

    status = plugin.deregisterNode(dirtyNode::id);
    if (!status) {
    status.perror("deregisterNode");
    return status;
    }

    return status;
    }