├── .gitignore ├── scripts ├── houdini_engine_for_maya │ └── __init__.py ├── shelf_HoudiniTools.mel ├── houdiniEngineDeleteUI.mel ├── AEhoudiniInputCurveTemplate.mel ├── AEhoudiniInputMergeTemplate.mel ├── AEhoudiniInputTransformTemplate.mel ├── AEhoudiniInputGeometryTemplate.mel ├── houdiniEngineAssetSync.mel ├── houdiniEngineUtils.py ├── houdiniEngineSelection.py ├── houdiniEngineBakeAsset.mel ├── houdiniEngineAssetOptions.mel ├── houdiniEngineDeleteHistory.mel ├── houdiniEngineAssetLibraryManager.mel └── houdiniEngineRemoveHistory.mel ├── HoudiniVersion ├── doc ├── Maya_Menu.png ├── Maya_Session.png ├── Maya_BrowseHDA.png ├── Maya_Debugging.png ├── Maya_AssetOptions.png ├── Maya_Bake_Group.png ├── Maya_Bake_Ungroup.png ├── Maya_ChooseAsset.png ├── Maya_Code_BadGeo.png ├── Maya_Freeze_Attrs.png ├── Maya_Freeze_Menu.png ├── Maya_InputOptions.png ├── Maya_Input_Input.png ├── Maya_RelatedTabs.png ├── Maya_Mesh_CompGroup.png ├── Maya_Code_MeshConnect.png ├── Maya_Input_Parameter.png ├── Maya_Nodes_AssetNode.png ├── Maya_Nodes_InputCurve.png ├── Maya_Nodes_InputHair.png ├── Maya_Nodes_InputMerge.png ├── Maya_Nodes_OutputFluid.png ├── Maya_Parameters_Basic.png ├── Maya_Parameters_Button.png ├── Maya_Parameters_Folder.png ├── Maya_Parameters_Menu.png ├── Maya_Parameters_Ramp.png ├── Maya_History_AssetNodes.png ├── Maya_Nodes_InputGeometry.png ├── Maya_Nodes_InputParticle.png ├── Maya_Nodes_InputTransform.png ├── Maya_Parameters_Multiparm.png ├── Maya_Parameters_OperatorPath.png ├── Maya_Parameters_Basic_MultiLineString.png ├── Maya_Parameters_AnimationAndExpression.png ├── Maya_Parameters_Basic_ComponentSelection.png ├── Maya_Locator.dox ├── Maya_Curve.dox ├── mainpage.dox ├── Maya_GettingStarted.dox ├── Maya_Hair.dox ├── Maya_Bake.dox ├── Maya_Volume.dox ├── Maya_Group.dox ├── Maya_Material.dox ├── Maya_Instancing.dox ├── Maya_Shelves.dox ├── Maya_Freeze.dox ├── Maya_Outputs.dox ├── Maya_Install.dox ├── Maya_Particle.dox ├── Maya_Debugging.dox ├── Maya_Time.dox ├── Maya_History.dox └── Maya_Session.dox ├── icons ├── he_boolean.png ├── he_set_pivot.png ├── houdiniAsset.png ├── he_polyreduce.png ├── he_curve_instancer.png ├── he_surface_instancer.png ├── he_calculate_occlusion.png └── he_delete_small_parts.png ├── AssetNodeOptions.C ├── module_description_absolute.in ├── module_description.in ├── InputCurve.h ├── SyncAttribute.h ├── InputParticle.h ├── EngineCommand.h ├── InputCurveNode.h ├── FluidGridConvert.h ├── AssetSubCommandLoadAsset.h ├── InputMergeNode.h ├── InputTransformNode.h ├── SyncOutputMaterial.h ├── OutputMaterial.h ├── OutputGeometry.h ├── AssetCommand.h ├── SubCommand.C ├── OutputObject.h ├── AssetSubCommandSync.h ├── SyncOutputObject.h ├── LICENSE.txt ├── InputGeometryNode.h ├── AssetNodeOptions.h ├── OutputGeometryObject.h ├── OutputObject.C ├── OutputInstancerObject.h ├── SubCommand.h ├── InputMesh.h ├── Platform.C ├── SyncOutputInstance.h ├── houdiniEnginePlugin.sln ├── MayaTypeID.h ├── OutputDeform.h ├── SyncOutputGeometryPart.h ├── Input.h ├── OptionVars.h ├── Asset.h ├── AssetDraw2.h ├── README.md ├── InputCurve.C ├── OutputGeometryPart.h ├── cmake ├── Modules │ ├── FindMaya.cmake │ └── FindHoudiniEngine.cmake └── PluginFiles.cmake ├── AssetSubCommandLoadAsset.C ├── OutputGeometry.C ├── InputMergeNode.C ├── NodeOptions.h ├── InputTransformNode.C └── OutputDeform.C /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | build-maya* 3 | -------------------------------------------------------------------------------- /scripts/houdini_engine_for_maya/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /HoudiniVersion: -------------------------------------------------------------------------------- 1 | Houdini Version: 21.0.456 2 | Houdini Engine Version: 8.0 (API: 1) 3 | -------------------------------------------------------------------------------- /doc/Maya_Menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Menu.png -------------------------------------------------------------------------------- /doc/Maya_Session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Session.png -------------------------------------------------------------------------------- /icons/he_boolean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/icons/he_boolean.png -------------------------------------------------------------------------------- /doc/Maya_BrowseHDA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_BrowseHDA.png -------------------------------------------------------------------------------- /doc/Maya_Debugging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Debugging.png -------------------------------------------------------------------------------- /icons/he_set_pivot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/icons/he_set_pivot.png -------------------------------------------------------------------------------- /icons/houdiniAsset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/icons/houdiniAsset.png -------------------------------------------------------------------------------- /AssetNodeOptions.C: -------------------------------------------------------------------------------- 1 | #include "AssetNodeOptions.h" 2 | 3 | AssetNodeOptions::Definition assetNodeOptionsDefinition; 4 | -------------------------------------------------------------------------------- /doc/Maya_AssetOptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_AssetOptions.png -------------------------------------------------------------------------------- /doc/Maya_Bake_Group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Bake_Group.png -------------------------------------------------------------------------------- /doc/Maya_Bake_Ungroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Bake_Ungroup.png -------------------------------------------------------------------------------- /doc/Maya_ChooseAsset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_ChooseAsset.png -------------------------------------------------------------------------------- /doc/Maya_Code_BadGeo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Code_BadGeo.png -------------------------------------------------------------------------------- /doc/Maya_Freeze_Attrs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Freeze_Attrs.png -------------------------------------------------------------------------------- /doc/Maya_Freeze_Menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Freeze_Menu.png -------------------------------------------------------------------------------- /doc/Maya_InputOptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_InputOptions.png -------------------------------------------------------------------------------- /doc/Maya_Input_Input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Input_Input.png -------------------------------------------------------------------------------- /doc/Maya_RelatedTabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_RelatedTabs.png -------------------------------------------------------------------------------- /icons/he_polyreduce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/icons/he_polyreduce.png -------------------------------------------------------------------------------- /doc/Maya_Mesh_CompGroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Mesh_CompGroup.png -------------------------------------------------------------------------------- /doc/Maya_Code_MeshConnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Code_MeshConnect.png -------------------------------------------------------------------------------- /doc/Maya_Input_Parameter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Input_Parameter.png -------------------------------------------------------------------------------- /doc/Maya_Nodes_AssetNode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Nodes_AssetNode.png -------------------------------------------------------------------------------- /doc/Maya_Nodes_InputCurve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Nodes_InputCurve.png -------------------------------------------------------------------------------- /doc/Maya_Nodes_InputHair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Nodes_InputHair.png -------------------------------------------------------------------------------- /doc/Maya_Nodes_InputMerge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Nodes_InputMerge.png -------------------------------------------------------------------------------- /doc/Maya_Nodes_OutputFluid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Nodes_OutputFluid.png -------------------------------------------------------------------------------- /doc/Maya_Parameters_Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_Basic.png -------------------------------------------------------------------------------- /doc/Maya_Parameters_Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_Button.png -------------------------------------------------------------------------------- /doc/Maya_Parameters_Folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_Folder.png -------------------------------------------------------------------------------- /doc/Maya_Parameters_Menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_Menu.png -------------------------------------------------------------------------------- /doc/Maya_Parameters_Ramp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_Ramp.png -------------------------------------------------------------------------------- /icons/he_curve_instancer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/icons/he_curve_instancer.png -------------------------------------------------------------------------------- /icons/he_surface_instancer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/icons/he_surface_instancer.png -------------------------------------------------------------------------------- /doc/Maya_History_AssetNodes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_History_AssetNodes.png -------------------------------------------------------------------------------- /doc/Maya_Nodes_InputGeometry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Nodes_InputGeometry.png -------------------------------------------------------------------------------- /doc/Maya_Nodes_InputParticle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Nodes_InputParticle.png -------------------------------------------------------------------------------- /icons/he_calculate_occlusion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/icons/he_calculate_occlusion.png -------------------------------------------------------------------------------- /icons/he_delete_small_parts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/icons/he_delete_small_parts.png -------------------------------------------------------------------------------- /module_description_absolute.in: -------------------------------------------------------------------------------- 1 | + houdiniEngine 1.0 ${install_module_absolute_dir} 2 | ${MODULE_DESCRIPTION_ABSOLUTE_ENVIRONMENTS} 3 | -------------------------------------------------------------------------------- /doc/Maya_Nodes_InputTransform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Nodes_InputTransform.png -------------------------------------------------------------------------------- /doc/Maya_Parameters_Multiparm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_Multiparm.png -------------------------------------------------------------------------------- /module_description.in: -------------------------------------------------------------------------------- 1 | + MAYAVERSION:${MAYA_VERSION} houdiniEngine 1.0 ${install_module_dir} 2 | ${MODULE_DESCRIPTION_ENVIRONMENTS} 3 | -------------------------------------------------------------------------------- /doc/Maya_Parameters_OperatorPath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_OperatorPath.png -------------------------------------------------------------------------------- /doc/Maya_Parameters_Basic_MultiLineString.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_Basic_MultiLineString.png -------------------------------------------------------------------------------- /doc/Maya_Parameters_AnimationAndExpression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_AnimationAndExpression.png -------------------------------------------------------------------------------- /doc/Maya_Parameters_Basic_ComponentSelection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideeffects/HoudiniEngineForMaya/HEAD/doc/Maya_Parameters_Basic_ComponentSelection.png -------------------------------------------------------------------------------- /scripts/shelf_HoudiniTools.mel: -------------------------------------------------------------------------------- 1 | global proc shelf_HoudiniTools () { 2 | global string $gBuffStr; 3 | global string $gBuffStr0; 4 | global string $gBuffStr1; 5 | } 6 | -------------------------------------------------------------------------------- /scripts/houdiniEngineDeleteUI.mel: -------------------------------------------------------------------------------- 1 | global proc houdiniEngineDeleteUI() 2 | { 3 | global int $houdiniEngine_scriptJobId; 4 | 5 | if(`menu -q -exists houdiniEngineMenu`) 6 | { 7 | deleteUI -menu houdiniEngineMenu; 8 | } 9 | if($houdiniEngine_scriptJobId > 0) { 10 | scriptJob -kill $houdiniEngine_scriptJobId; 11 | $houdiniEngine_scriptJobId = -1; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /doc/Maya_Locator.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Locator Locator 4 | 5 | The plug-in supports inputting Maya locators. 6 | 7 | @section Maya_Locator_Input Input 8 | 9 | Maya locators are inputted into a Houdini asset as points. For each locator that is being inputted, a point is created in the Houdini side. The translation, rotation, and scale of each locator are inputted as `P`, `orient`, and `scale` attribute of the respective point. 10 | 11 | */ 12 | -------------------------------------------------------------------------------- /InputCurve.h: -------------------------------------------------------------------------------- 1 | #ifndef __InputCurve_h__ 2 | #define __InputCurve_h__ 3 | 4 | #include "Input.h" 5 | 6 | #include 7 | 8 | class InputCurve : public Input 9 | { 10 | public: 11 | InputCurve(); 12 | virtual ~InputCurve(); 13 | 14 | virtual AssetInputType assetInputType() const; 15 | 16 | virtual void setInputGeo(MDataBlock &dataBlock, const MPlug &plug); 17 | 18 | protected: 19 | HAPI_NodeInfo myCurveNodeInfo; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /doc/Maya_Curve.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Curve Curve 4 | 5 | @tableofcontents 6 | 7 | @section Maya_Curve_Input Input 8 | 9 | Both Maya's NURBS curve and Bezier curve can be inputted into a Houdini asset. The curve will be inputted as either NURBS Curve or Bezier Curve, depending on the Maya type. 10 | 11 | @section Maya_Curve_Output Output 12 | 13 | NURBS Curve and Bezier Curve from a Houdini asset are outputted as `nurbsCurve` node or `bezierCurve` node respectively. 14 | 15 | If the asset output multiple curve primitives, each curve will be outputted into a separate curve node. 16 | 17 | */ 18 | -------------------------------------------------------------------------------- /SyncAttribute.h: -------------------------------------------------------------------------------- 1 | #ifndef __SyncAttribute_h__ 2 | #define __SyncAttribute_h__ 3 | 4 | #include "SubCommand.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | class SyncAttribute : public SubCommandAsset 12 | { 13 | public: 14 | SyncAttribute(const MObject &assetNodeObj); 15 | virtual ~SyncAttribute(); 16 | 17 | virtual MStatus doIt(); 18 | virtual MStatus undoIt(); 19 | virtual MStatus redoIt(); 20 | 21 | virtual bool isUndoable() const; 22 | 23 | protected: 24 | MDGModifier myDGModifier; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /scripts/AEhoudiniInputCurveTemplate.mel: -------------------------------------------------------------------------------- 1 | global proc AEhoudiniInputCurveTemplate(string $nodeName) 2 | { 3 | editorTemplate -beginScrollLayout; 4 | editorTemplate -beginLayout "Outputs" -collapse false; 5 | 6 | editorTemplate 7 | -label "Output Node Id" 8 | -addControl "outputNodeId"; 9 | editorTemplate 10 | -label "Preserve Houdini Scale" 11 | -addControl "preserveScale"; 12 | 13 | editorTemplate -suppress "inputCurve"; 14 | editorTemplate -endLayout; 15 | 16 | editorTemplate -addExtraControls; 17 | 18 | editorTemplate -endScrollLayout; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /InputParticle.h: -------------------------------------------------------------------------------- 1 | #ifndef __InputParticle_h__ 2 | #define __InputParticle_h__ 3 | 4 | #include "Input.h" 5 | 6 | #include 7 | 8 | class InputParticle : public Input 9 | { 10 | public: 11 | InputParticle(); 12 | virtual ~InputParticle(); 13 | 14 | virtual AssetInputType assetInputType() const; 15 | 16 | virtual void setInputGeo(MDataBlock &dataBlock, const MPlug &plug); 17 | 18 | protected: 19 | void setAttributePointData(const char *attributeName, 20 | HAPI_StorageType storage, 21 | int count, 22 | int tupleSize, 23 | void *data); 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /EngineCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef __EngineCommand_h__ 2 | #define __EngineCommand_h__ 3 | 4 | #include 5 | 6 | #include 7 | 8 | class SubCommand; 9 | 10 | class EngineCommand : public MPxCommand 11 | { 12 | public: 13 | static const char *commandName; 14 | 15 | public: 16 | static void *creator(); 17 | static MSyntax newSyntax(); 18 | 19 | public: 20 | EngineCommand(); 21 | virtual ~EngineCommand(); 22 | 23 | MStatus doIt(const MArgList &args); 24 | MStatus redoIt(); 25 | MStatus undoIt(); 26 | bool isUndoable() const; 27 | 28 | private: 29 | MStatus parseArgs(const MArgList &args); 30 | 31 | private: 32 | SubCommand *mySubCommand; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /InputCurveNode.h: -------------------------------------------------------------------------------- 1 | #ifndef __InputCurveNode_h__ 2 | #define __InputCurveNode_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | class InputCurveNode : public MPxNode 11 | { 12 | public: 13 | InputCurveNode(); 14 | virtual ~InputCurveNode(); 15 | 16 | virtual MStatus compute(const MPlug &plug, MDataBlock &data); 17 | 18 | static void *creator(); 19 | static MStatus initialize(); 20 | 21 | static MString typeName; 22 | static MTypeId typeId; 23 | 24 | static MObject preserveScale; 25 | 26 | private: 27 | HAPI_NodeId myNodeId; 28 | 29 | static MObject inputCurve; 30 | static MObject outputNodeId; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /FluidGridConvert.h: -------------------------------------------------------------------------------- 1 | #ifndef __FluidGridConvert_h__ 2 | #define __FluidGridConvert_h__ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | class FluidGridConvert : public MPxNode 10 | { 11 | public: 12 | FluidGridConvert(); 13 | virtual ~FluidGridConvert(); 14 | 15 | virtual MStatus compute(const MPlug &plug, MDataBlock &data); 16 | 17 | static void *creator(); 18 | static MStatus initialize(); 19 | 20 | static MString typeName; 21 | static MTypeId typeId; 22 | 23 | static MObject conversionMode; 24 | 25 | static MObject resolution; 26 | 27 | static MObject inGridX; 28 | static MObject inGridY; 29 | static MObject inGridZ; 30 | 31 | static MObject outGrid; 32 | }; 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /AssetSubCommandLoadAsset.h: -------------------------------------------------------------------------------- 1 | #ifndef __AssetSubCommandLoadAsset_h__ 2 | #define __AssetSubCommandLoadAsset_h__ 3 | 4 | #include "SubCommand.h" 5 | 6 | #include 7 | #include 8 | 9 | class AssetSubCommandSync; 10 | 11 | class AssetSubCommandLoadAsset : public SubCommand 12 | { 13 | public: 14 | AssetSubCommandLoadAsset(const MString &otlFilePath, 15 | const MString &assetName); 16 | virtual ~AssetSubCommandLoadAsset(); 17 | 18 | virtual MStatus doIt(); 19 | virtual MStatus redoIt(); 20 | virtual MStatus undoIt(); 21 | 22 | virtual bool isUndoable() const; 23 | 24 | protected: 25 | MString myOTLFilePath; 26 | MString myAssetName; 27 | 28 | MDagModifier myDagModifier; 29 | 30 | AssetSubCommandSync *myAssetSubCommandSync; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /InputMergeNode.h: -------------------------------------------------------------------------------- 1 | #ifndef __InputMergeNode_h__ 2 | #define __InputMergeNode_h__ 3 | 4 | #include 5 | 6 | #include 7 | 8 | class InputMergeNode : public MPxNode 9 | { 10 | public: 11 | static void *creator(); 12 | static MStatus initialize(); 13 | 14 | public: 15 | static MString typeName; 16 | static MTypeId typeId; 17 | 18 | static MObject inputTransform; 19 | static MObject inputNode; 20 | 21 | static MObject outputNodeId; 22 | 23 | static MObject packBeforeMerge; 24 | 25 | public: 26 | InputMergeNode(); 27 | virtual ~InputMergeNode(); 28 | 29 | virtual MStatus compute(const MPlug &plug, MDataBlock &dataBlock); 30 | 31 | private: 32 | void clearInput(); 33 | bool checkInput(MDataBlock &dataBlock); 34 | 35 | private: 36 | HAPI_NodeId myGeometryNodeId; 37 | }; 38 | 39 | #endif 40 | 41 | -------------------------------------------------------------------------------- /InputTransformNode.h: -------------------------------------------------------------------------------- 1 | #ifndef __InputTransformNode_h__ 2 | #define __InputTransformNode_h__ 3 | 4 | #include 5 | 6 | #include 7 | 8 | class InputTransformNode : public MPxNode 9 | { 10 | public: 11 | static void *creator(); 12 | static MStatus initialize(); 13 | 14 | public: 15 | static MString typeName; 16 | static MTypeId typeId; 17 | 18 | static MObject inputTransform; 19 | static MObject inputMatrix; 20 | static MObject preserveScale; 21 | 22 | static MObject outputNodeId; 23 | 24 | public: 25 | InputTransformNode(); 26 | virtual ~InputTransformNode(); 27 | 28 | virtual MStatus compute(const MPlug &plug, MDataBlock &dataBlock); 29 | 30 | private: 31 | void clearInput(); 32 | bool checkInput(MDataBlock &dataBlock); 33 | 34 | private: 35 | HAPI_NodeId myGeometryNodeId; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /SyncOutputMaterial.h: -------------------------------------------------------------------------------- 1 | #ifndef __SyncOutputMaterial_h__ 2 | #define __SyncOutputMaterial_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class SyncOutputMaterial 10 | { 11 | public: 12 | static MObject createOutputMaterial(MDGModifier &dgModifier, 13 | const MObject &assetObj, 14 | int nodeId); 15 | 16 | static MPlug createOutputMaterialPlug(MDGModifier &dgModifier, 17 | const MObject &assetObj, 18 | int nodeId); 19 | 20 | static MObject findShader(const MPlug &materialPlug); 21 | static MObject findFileTexture(const MPlug &materialPlug); 22 | static MObject findShadingGroup(const MObject &shaderFn); 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /OutputMaterial.h: -------------------------------------------------------------------------------- 1 | #ifndef __OutputMaterial_h__ 2 | #define __OutputMaterial_h__ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class OutputMaterial 14 | { 15 | public: 16 | OutputMaterial(HAPI_NodeId assetId); 17 | 18 | MStatus compute(const MTime &time, 19 | const MPlug &materialPlug, 20 | MDataBlock &data, 21 | MDataHandle &materialHandle, 22 | bool bakeTexture); 23 | 24 | private: 25 | void update(MDataHandle &materialHandle); 26 | 27 | private: 28 | HAPI_NodeId myAssetId; 29 | 30 | std::string myNodePath; 31 | 32 | HAPI_NodeId myNodeId; 33 | HAPI_NodeInfo myNodeInfo; 34 | int myMaterialLastCookCount; 35 | bool myBakeTexture; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /scripts/AEhoudiniInputMergeTemplate.mel: -------------------------------------------------------------------------------- 1 | global proc AEhoudiniInputMergeTemplate(string $nodeName) 2 | { 3 | editorTemplate -beginScrollLayout; 4 | 5 | editorTemplate -beginLayout "Inputs" -collapse false; 6 | editorTemplate 7 | -label "Input Nodes" 8 | -addControl "inputNode"; 9 | editorTemplate 10 | -label "Pack Before Merge" 11 | -addControl "packBeforeMerge"; 12 | editorTemplate -endLayout; 13 | 14 | editorTemplate -beginLayout "Outputs" -collapse false; 15 | editorTemplate 16 | -label "Output Node Id" 17 | -addControl "outputNodeId"; 18 | editorTemplate -endLayout; 19 | 20 | editorTemplate -suppress "inputGeometry"; 21 | editorTemplate -suppress "inputTransform"; 22 | editorTemplate -endLayout; 23 | 24 | editorTemplate -addExtraControls; 25 | 26 | editorTemplate -endScrollLayout; 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /scripts/AEhoudiniInputTransformTemplate.mel: -------------------------------------------------------------------------------- 1 | global proc AEhoudiniInputTransformTemplate(string $nodeName) 2 | { 3 | editorTemplate -beginScrollLayout; 4 | editorTemplate -beginLayout "Inputs" -collapse false; 5 | editorTemplate 6 | -label "Input Matrix" 7 | -addControl "inputMatrix"; 8 | editorTemplate 9 | -label "Preserve Houdini Scale" 10 | -addControl "preserveScale"; 11 | editorTemplate -endLayout; 12 | 13 | editorTemplate -beginLayout "Outputs" -collapse false; 14 | editorTemplate 15 | -label "Output Node Id" 16 | -addControl "outputNodeId"; 17 | editorTemplate -endLayout; 18 | 19 | editorTemplate -suppress "inputGeometry"; 20 | editorTemplate -suppress "inputComponents"; 21 | editorTemplate -suppress "inputTransform"; 22 | 23 | editorTemplate -addExtraControls; 24 | 25 | editorTemplate -endScrollLayout; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /OutputGeometry.h: -------------------------------------------------------------------------------- 1 | #ifndef __OutputGeometry_h__ 2 | #define __OutputGeometry_h__ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include "AssetNodeOptions.h" 12 | 13 | class OutputObject; 14 | class OutputGeometryPart; 15 | 16 | class OutputGeometry 17 | { 18 | public: 19 | OutputGeometry(HAPI_NodeId nodeId); 20 | ~OutputGeometry(); 21 | 22 | MStatus compute(const MTime &time, 23 | const MPlug &geoPlug, 24 | MDataBlock &data, 25 | MDataHandle &geoHandle, 26 | AssetNodeOptions::AccessorDataBlock &options, 27 | bool &needToSyncOutputs, 28 | const bool needToRecomputeOutputData); 29 | 30 | void update(); 31 | 32 | protected: 33 | HAPI_NodeId myNodeId; 34 | HAPI_NodeInfo myNodeInfo; 35 | HAPI_GeoInfo myGeoInfo; 36 | 37 | int myLastCookCount; 38 | 39 | std::vector myParts; 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /AssetCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef __AssetCommand_h__ 2 | #define __AssetCommand_h__ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | class SubCommand; 10 | 11 | class AssetCommand : public MPxCommand 12 | { 13 | public: 14 | static const char *commandName; 15 | 16 | public: 17 | static void *creator(); 18 | static MSyntax newSyntax(); 19 | 20 | private: 21 | static bool getMObjectFromFlag(const MArgDatabase &argData, 22 | const char *flag, 23 | MObject &obj, 24 | MStatus &status, 25 | const int index=0); 26 | 27 | public: 28 | AssetCommand(); 29 | virtual ~AssetCommand(); 30 | 31 | MStatus doIt(const MArgList &args); 32 | MStatus redoIt(); 33 | MStatus undoIt(); 34 | bool isUndoable() const; 35 | 36 | private: 37 | MStatus parseArgs(const MArgList &args); 38 | 39 | private: 40 | SubCommand *mySubCommand; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /SubCommand.C: -------------------------------------------------------------------------------- 1 | #include "SubCommand.h" 2 | 3 | #include 4 | 5 | #include "AssetNode.h" 6 | 7 | SubCommand::SubCommand() {} 8 | 9 | SubCommand::~SubCommand() {} 10 | 11 | MStatus 12 | SubCommand::doIt() 13 | { 14 | return MStatus::kSuccess; 15 | } 16 | 17 | MStatus 18 | SubCommand::redoIt() 19 | { 20 | return MStatus::kSuccess; 21 | } 22 | 23 | MStatus 24 | SubCommand::undoIt() 25 | { 26 | return MStatus::kSuccess; 27 | } 28 | 29 | bool 30 | SubCommand::isUndoable() const 31 | { 32 | return false; 33 | } 34 | 35 | SubCommandAsset::SubCommandAsset(const MObject &assetNodeObj) 36 | : myAssetNodeObj(assetNodeObj) 37 | { 38 | } 39 | 40 | AssetNode * 41 | SubCommandAsset::getAssetNode() const 42 | { 43 | MFnDependencyNode assetNodeFn(myAssetNodeObj); 44 | 45 | AssetNode *assetNode = dynamic_cast(assetNodeFn.userNode()); 46 | 47 | return assetNode; 48 | } 49 | 50 | Asset * 51 | SubCommandAsset::getAsset() const 52 | { 53 | Asset *asset = getAssetNode()->getAsset(); 54 | 55 | return asset; 56 | } 57 | -------------------------------------------------------------------------------- /OutputObject.h: -------------------------------------------------------------------------------- 1 | #ifndef __OutputObject_h__ 2 | #define __OutputObject_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | class Asset; 17 | 18 | class OutputObject 19 | { 20 | public: 21 | enum ObjectType 22 | { 23 | OBJECT_TYPE_GEOMETRY, 24 | OBJECT_TYPE_INSTANCER 25 | }; 26 | 27 | // static creator 28 | static OutputObject *createObject(HAPI_NodeId nodeId); 29 | 30 | OutputObject(HAPI_NodeId nodeId); 31 | virtual ~OutputObject(); 32 | 33 | void setObjectInfo(const HAPI_ObjectInfo &objectInfo); 34 | 35 | virtual ObjectType type() = 0; 36 | 37 | bool isVisible() const; 38 | 39 | protected: 40 | HAPI_NodeId myNodeId; 41 | 42 | HAPI_NodeInfo myNodeInfo; 43 | HAPI_ObjectInfo myObjectInfo; 44 | 45 | int myLastCookCount; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /AssetSubCommandSync.h: -------------------------------------------------------------------------------- 1 | #ifndef __AssetSubCommandSync_h__ 2 | #define __AssetSubCommandSync_h__ 3 | 4 | #include "SubCommand.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | class AssetSubCommandSync : public SubCommandAsset 13 | { 14 | public: 15 | AssetSubCommandSync(const MObject &assetNodeObj); 16 | virtual ~AssetSubCommandSync(); 17 | 18 | void setSyncAttributes(); 19 | void setSyncOutputs(); 20 | 21 | void setSyncOutputHidden(); 22 | void setSyncOutputTemplatedGeos(); 23 | void deleteMaterials(MPlug &materialPlug); 24 | 25 | virtual MStatus doIt(); 26 | virtual MStatus redoIt(); 27 | virtual MStatus undoIt(); 28 | virtual bool isUndoable() const; 29 | 30 | protected: 31 | bool mySyncAll; 32 | bool mySyncAttributes; 33 | bool mySyncOutputs; 34 | 35 | bool mySyncOutputHidden; 36 | bool mySyncOutputTemplatedGeos; 37 | 38 | MDagModifier myDagModifier; 39 | 40 | typedef std::vector AssetSyncs; 41 | AssetSyncs myAssetSyncs; 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /SyncOutputObject.h: -------------------------------------------------------------------------------- 1 | #ifndef __SyncOutputObject_h__ 2 | #define __SyncOutputObject_h__ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "SubCommand.h" 10 | 11 | class SyncOutputGeometryPart; 12 | 13 | class SyncOutputObject : public SubCommand 14 | { 15 | public: 16 | SyncOutputObject(const MPlug &outputPlug, 17 | const MObject &assetNodeObj, 18 | const bool visible, 19 | const bool syncTemplatedGeos); 20 | virtual ~SyncOutputObject(); 21 | 22 | virtual MStatus doIt(); 23 | virtual MStatus undoIt(); 24 | virtual MStatus redoIt(); 25 | 26 | virtual bool isUndoable() const; 27 | 28 | protected: 29 | MStatus createFluidShape(const MObject &objectTransform); 30 | 31 | const MPlug myOutputPlug; 32 | const MObject myAssetNodeObj; 33 | const bool myVisible; 34 | const bool mySyncTemplatedGeos; 35 | 36 | MDagModifier myDagModifier; 37 | 38 | typedef std::vector AssetSyncs; 39 | AssetSyncs myAssetSyncs; 40 | }; 41 | 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Side Effects Software Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /InputGeometryNode.h: -------------------------------------------------------------------------------- 1 | #ifndef __InputGeometryNode_h__ 2 | #define __InputGeometryNode_h__ 3 | 4 | #include 5 | 6 | class Input; 7 | 8 | class InputGeometryNode : public MPxNode 9 | { 10 | public: 11 | static void *creator(); 12 | static MStatus initialize(); 13 | 14 | public: 15 | static MString typeName; 16 | static MTypeId typeId; 17 | 18 | static MObject inputTransform; 19 | static MObject inputGeometry; 20 | static MObject inputComponents; 21 | static MObject primComponentGroup; 22 | static MObject pointComponentGroup; 23 | static MObject unlockNormals; 24 | static MObject materialPerFace; 25 | static MObject allowFacetSet; 26 | static MObject preserveScale; 27 | static MObject ignoreTransform; 28 | static MObject objectShadingGroup; 29 | 30 | static MObject outputNodeId; 31 | 32 | public: 33 | InputGeometryNode(); 34 | virtual ~InputGeometryNode(); 35 | 36 | virtual MStatus compute(const MPlug &plug, MDataBlock &dataBlock); 37 | 38 | private: 39 | void clearInput(); 40 | bool checkInput(MDataBlock &dataBlock); 41 | 42 | private: 43 | Input *myInput; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /AssetNodeOptions.h: -------------------------------------------------------------------------------- 1 | #ifndef __AssetNodeOptions_h__ 2 | #define __AssetNodeOptions_h__ 3 | 4 | #include "NodeOptions.h" 5 | 6 | NODE_OPTIONS_BEGIN(AssetNodeOptions) 7 | 8 | NODE_OPTION(syncWhenInputConnects, bool, true) 9 | NODE_OPTION(autoSyncOutputs, bool, false) 10 | 11 | NODE_OPTION(useAssetObjectTransform, bool, false) 12 | NODE_OPTION(splitGeosByGroup, bool, false) 13 | NODE_OPTION(outputHiddenObjects, bool, false) 14 | NODE_OPTION(outputTemplatedGeometries, bool, false) 15 | NODE_OPTION(outputGeometryGroups, bool, true) 16 | NODE_OPTION(outputCustomAttributes, bool, true) 17 | NODE_OPTION(outputMeshPreserveHardEdges, bool, true) 18 | NODE_OPTION(outputMeshPreserveLockedNormals, bool, true) 19 | NODE_OPTION(ungroupOnBake, bool, true) 20 | NODE_OPTION(updateParmsForEvalMode, bool, true) 21 | NODE_OPTION(connectGeoForAssetInputs, bool, false) 22 | NODE_OPTION(bakeOutputTextures, bool, true) 23 | NODE_OPTION(preserveScale, bool, false) 24 | NODE_OPTION(alwaysMergeInputGeometry, bool, false) 25 | NODE_OPTION(packBeforeMerge, bool, false) 26 | 27 | NODE_OPTION(useInstancerNode, bool, true) 28 | 29 | NODE_OPTIONS_END() 30 | 31 | extern AssetNodeOptions::Definition assetNodeOptionsDefinition; 32 | 33 | #endif 34 | 35 | -------------------------------------------------------------------------------- /OutputGeometryObject.h: -------------------------------------------------------------------------------- 1 | #ifndef __OutputGeometryObject_h__ 2 | #define __OutputGeometryObject_h__ 3 | 4 | #include "OutputObject.h" 5 | 6 | #include 7 | 8 | #include "AssetNodeOptions.h" 9 | 10 | class OutputGeometry; 11 | 12 | class OutputGeometryObject : public OutputObject 13 | { 14 | public: 15 | OutputGeometryObject(HAPI_NodeId nodeId); 16 | virtual ~OutputGeometryObject(); 17 | 18 | virtual MStatus compute(const MTime &time, 19 | const MPlug &objectPlug, 20 | MDataBlock &data, 21 | MDataHandle &objectHandle, 22 | const MIntArray &instancedObjIds, 23 | const MStringArray &instancedObjNames, 24 | AssetNodeOptions::AccessorDataBlock &options, 25 | bool &needToSyncOutputs, 26 | const bool needToRecomputeOutputData); 27 | 28 | virtual ObjectType type(); 29 | 30 | private: 31 | void update(); 32 | 33 | void updateTransform(MDataHandle &handle, const bool preserveScale); 34 | 35 | private: 36 | std::vector myGeos; 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /doc/mainpage.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @mainpage 4 | 5 | @section Maya Overview 6 | - @subpage Maya_Install "Install" 7 | - @subpage Maya_GettingStarted "Getting Started" 8 | - @subpage Maya_Session "Session" 9 | - @subpage Maya_Assets "Assets" 10 | - @subpage Maya_Nodes "Nodes" 11 | - @subpage Maya_Parameters "Parameters" 12 | 13 | Input and Output Geometries 14 | 15 | - @subpage Maya_Inputs "Inputs" 16 | - @subpage Maya_Outputs "Outputs" 17 | - @subpage Maya_Mesh "Mesh" 18 | - @subpage Maya_Particle "Particle" 19 | - @subpage Maya_Locator "Locator" 20 | - @subpage Maya_Curve "Curve" 21 | - @subpage Maya_Hair "Hair" 22 | - @subpage Maya_Volume "Volume" 23 | - @subpage Maya_Instancing "Instancing" 24 | 25 | Other topics 26 | 27 | - @subpage Maya_Group "Group" 28 | - @subpage Maya_Debugging "Debugging" 29 | - @subpage Maya_Time "Time" 30 | - @subpage Maya_Material "Material" 31 | - @subpage Maya_Shelves "Shelves" 32 | - @subpage Maya_Scripting "Scripting" 33 | - @subpage Maya_History "Construction History" 34 | - @subpage Maya_Bake "Baking Assets" 35 | - @subpage Maya_Freeze "Freeze Assets" 36 | - @subpage Maya_Compatibility "Maya vs. Houdini Incompatibilites" 37 | - @subpage Maya_Code "Intro to the Plugin Code" 38 | 39 | */ 40 | -------------------------------------------------------------------------------- /OutputObject.C: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Asset.h" 8 | #include "OutputGeometryObject.h" 9 | #include "OutputInstancerObject.h" 10 | #include "OutputObject.h" 11 | #include "util.h" 12 | 13 | OutputObject * 14 | OutputObject::createObject(HAPI_NodeId nodeId) 15 | { 16 | HAPI_Result hapiResult; 17 | 18 | HAPI_ObjectInfo objectInfo; 19 | hapiResult = HAPI_GetObjectInfo( 20 | Util::theHAPISession.get(), nodeId, &objectInfo); 21 | CHECK_HAPI(hapiResult); 22 | 23 | OutputObject *obj; 24 | 25 | if (objectInfo.isInstancer) 26 | obj = new OutputInstancerObject(nodeId); 27 | else 28 | { 29 | obj = new OutputGeometryObject(nodeId); 30 | } 31 | 32 | return obj; 33 | } 34 | 35 | OutputObject::~OutputObject() {} 36 | 37 | OutputObject::OutputObject(HAPI_NodeId nodeId) 38 | : myNodeId(nodeId), myLastCookCount(0) 39 | { 40 | } 41 | 42 | void 43 | OutputObject::setObjectInfo(const HAPI_ObjectInfo &objectInfo) 44 | { 45 | myObjectInfo = objectInfo; 46 | } 47 | 48 | bool 49 | OutputObject::isVisible() const 50 | { 51 | return myObjectInfo.isVisible; 52 | } 53 | -------------------------------------------------------------------------------- /doc/Maya_GettingStarted.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_GettingStarted Getting Started 4 | 5 | @tableofcontents 6 | 7 | @section Maya_GettingStarted_Load Load the plug-in 8 | 9 | Once the Maya plug-in has been installed, the `houdiniEngine` plugin can be loaded through Maya's plug-in manager. Once the plug-in has been loaded, the `Houdini Engine` menu appears at the main menu. In this menu, highlighted items are those added in the current release of Houdini, not the current release of Maya. 10 | 11 | @image html Maya_Menu.png 12 | 13 | @section Maya_GettingStarted_Instantiate Instantiate an asset 14 | 15 | A Houdini Digital Asset (HDA) can be instantiated through the menu `Houdini Engine->Load Asset...`. This will prompt a dialog to browse for the HDA file. 16 | 17 | @image html Maya_BrowseHDA.png 18 | 19 | After selecting the HDA file, the asset that is contained in the HDA file is instantiated. If the HDA file contains more than one asset, then a dialog would prompt for the asset to instantiate. 20 | 21 | @image html Maya_ChooseAsset.png 22 | 23 | Once the asset is instantiated in the Maya scene, a `houdiniAsset` node is created. Everything about the asset is exposed and controlled through this node, such as the asset's parameters, input geometries, and output geometries. 24 | 25 | */ 26 | -------------------------------------------------------------------------------- /doc/Maya_Hair.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Hair Hair 4 | 5 | @tableofcontents 6 | 7 | The plug-in has limited support for inputting hair. 8 | 9 | @section Maya_Hair_Input Input 10 | 11 | Maya's `hairSystem` can be inputted into Houdini assets. The guide curves are inputted. However, the attributes of the hair shape and follicles are not inputted into the Houdini assets. 12 | 13 | @section Maya_Hair_OutputtingGuideCurves Outputting Guide Curves 14 | 15 | There is no built-in support for outputting hair into Maya's `hairSystem`. Since Houdini's fur outputs curve primitives, each curve primitive will be outputted as a separate Maya curve node. For full density of fur and hair, the resulting Maya scene would become very heavy. 16 | 17 | Instead of outputting the full density of fur, an alternative approach is for the asset to output only the guide curves. Then, inside Maya, use the Maya `hairSystem`, or other tools, to generate the full fur. 18 | 19 | Guide curves can be created using Houdini's `fur` Object node. Since guide curves are regular curve primitives, they can also be created using any other Houdini tools that generate curves. Once the Houdini guide curves are outputted into the Maya scene, `nHair -> Make Selected Curves Dynamic` can be used to create a Maya `hairSystem` from the guide curves. 20 | 21 | */ 22 | -------------------------------------------------------------------------------- /doc/Maya_Bake.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Bake Baking 4 | 5 | @tableofcontents 6 | 7 | The outputs of an asset can now be baked. This disconnects all the outputs of an asset and unparents the child nodes from the houdiniAsset node. The asset is then sync'd again so that there are outputs to provide feedback for continuing work on the next version of the outputs. Note that a construction history asset should not be baked as that will turn it back into a Classic asset. 8 | 9 | 10 | @section Maya_Bake_Options Options 11 | 12 | - ungroup on bake 13 | 14 | If this option is turned on on the asset node, one additional level of hierarchy will be removed when unparenting the outputs from the asset: 15 | 16 | e.g. when this extrude asset was baked with Ungroup on, only the leaf transform remained 17 | @image html Maya_Bake_Ungroup.png 18 | 19 | but when Ungroup was off, another layer of grouping was preserved 20 | @image html Maya_Bake_Group.png 21 | 22 | @section Maya_Bake_Preferences Preferences 23 | 24 | - delete asset after baking 25 | 26 | If this preference is turned on, the asset will be deleted after the output is baked. 27 | 28 | The corresponding mel command, to bake the output of the selected asset is: 29 | 30 | @verbatim 31 | houdiniEngine_bakeAsset(string $assetNode) 32 | e.g. houdiniEngine_bakeAsset("|extrude1") 33 | @endverbatim 34 | 35 | 36 | 37 | */ 38 | -------------------------------------------------------------------------------- /OutputInstancerObject.h: -------------------------------------------------------------------------------- 1 | #ifndef __OutputInstancerObject_h__ 2 | #define __OutputInstancerObject_h__ 3 | 4 | #include "AssetNodeOptions.h" 5 | #include "OutputObject.h" 6 | 7 | class OutputInstancerObject : public OutputObject 8 | { 9 | public: 10 | OutputInstancerObject(HAPI_NodeId nodeId); 11 | virtual ~OutputInstancerObject(); 12 | 13 | MIntArray getInstancedObjIds(); 14 | MStringArray getUniqueInstObjNames(); 15 | 16 | virtual MStatus compute(const MTime &time, 17 | const MPlug &plug, 18 | MDataBlock &data, 19 | MDataHandle &handle, 20 | AssetNodeOptions::AccessorDataBlock &options, 21 | bool &needToSyncOutputs, 22 | const bool needToRecomputeOutputData); 23 | 24 | virtual ObjectType type(); 25 | 26 | protected: 27 | void update(); 28 | 29 | MStringArray getAttributeStringData(HAPI_AttributeOwner owner, 30 | MString name); 31 | 32 | private: 33 | HAPI_NodeInfo mySopNodeInfo; 34 | 35 | HAPI_GeoInfo myGeoInfo; 36 | HAPI_PartInfo myPartInfo; 37 | 38 | int myLastSopCookCount; 39 | 40 | MStringArray myInstancedObjectNames; 41 | MStringArray myUniqueInstObjNames; 42 | MIntArray myInstancedObjectIndices; 43 | 44 | MStringArray myHoudiniInstanceAttribute; 45 | MStringArray myHoudiniNameAttribute; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /doc/Maya_Volume.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Volume Volume 4 | 5 | @tableofcontents 6 | 7 | @section Maya_Volume_Input Input 8 | 9 | At the moment, the plug-in does not support inputting volumes into a Houdini asset. 10 | 11 | @section Maya_Volume_Output Output 12 | 13 | Since Maya does not support arbitrary volumes, multiple Houdini volumes that are outputted from the same SOP node are bundled together, and outputted into a single Maya `fluidShape` node. 14 | 15 | The Maya `fluidShape` node is designed to have a fixed set of volume grids, and all the volume grids have the same resolution. This creates several restrictions for outputting Houdini volumes. The Houdini volumes that are outputted must: 16 | 1. have match one of the names: `density`, `temperature`, `fuel`, `vel.[xyz]`, and `Cd.[xyz]` 17 | 2. have the same resolutions 18 | 19 | Note that for `vel.[xyz]` volumes, both face-sampled and center-sampled are supported. However, since Maya's `fluidShape` node requires face-sampled velocity grids, center-sampled velocity volumes will be extrapolated into face-sampled volumes. 20 | 21 | @subsection Maya_Volume_Output_OpenVDB OpenVDB 22 | 23 | Houdini supports VDB volumes, which are efficient for storing sparse volumes. However, Maya doesn't natively support sparse volumes. So while an asset can output VDB volumes into Maya, they would be converted into dense volumes. The effect of this can make the converted volumes much larger amount of memory and be difficult to work with. 24 | 25 | */ 26 | -------------------------------------------------------------------------------- /SubCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef __SubCommand_h__ 2 | #define __SubCommand_h__ 3 | 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | class AssetNode; 10 | class Asset; 11 | 12 | class SubCommand 13 | { 14 | public: 15 | SubCommand(); 16 | virtual ~SubCommand(); 17 | 18 | virtual MStatus doIt(); 19 | virtual MStatus redoIt(); 20 | virtual MStatus undoIt(); 21 | 22 | virtual bool isUndoable() const; 23 | }; 24 | 25 | class SubCommandAsset : public SubCommand 26 | { 27 | public: 28 | SubCommandAsset(const MObject &assetNodeObj); 29 | 30 | protected: 31 | AssetNode *getAssetNode() const; 32 | Asset *getAsset() const; 33 | 34 | protected: 35 | MObject myAssetNodeObj; 36 | }; 37 | 38 | #define GET_COMMAND_ASSET_OR_RETURN_FAIL() \ 39 | Asset *asset = getAsset(); \ 40 | if (!asset) \ 41 | { \ 42 | MFnDependencyNode assetNodeFn(myAssetNodeObj); \ 43 | DISPLAY_ERROR("^1s: The node contains an invalid asset." \ 44 | " Check the OTL file path and asset name.", \ 45 | assetNodeFn.name()); \ 46 | return MStatus::kFailure; \ 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /InputMesh.h: -------------------------------------------------------------------------------- 1 | #ifndef __InputMesh_h__ 2 | #define __InputMesh_h__ 3 | 4 | #include "Input.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | class InputMesh : public Input 11 | { 12 | public: 13 | InputMesh(); 14 | virtual ~InputMesh(); 15 | 16 | virtual AssetInputType assetInputType() const; 17 | 18 | virtual void setInputGeo(MDataBlock &dataBlock, const MPlug &plug); 19 | 20 | virtual void setInputComponents(MDataBlock &dataBlock, 21 | const MPlug &geoPlug, 22 | const MPlug &compPlug, 23 | const MPlug &primGroupPlug, 24 | const MPlug &pointGroupPlug); 25 | 26 | protected: 27 | bool processPoints(const MFnMesh &meshFn); 28 | bool processNormals(const MObject &meshObj, 29 | const MFnMesh &meshFn, 30 | std::vector vertexCount); 31 | bool processUVs(const MFnMesh &meshFn, 32 | std::vector vertexCount, 33 | std::vector vertexList); 34 | bool processColorSets(const MFnMesh &meshFn, 35 | std::vector vertexCount, 36 | std::vector vertexList); 37 | bool processSets(const MPlug &plug, const MFnMesh &meshFn); 38 | bool processShadingGroups(const MFnMesh &meshFn, 39 | const MStringArray &sgNames, 40 | const MObjectArray &sgCompObjs); 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /Platform.C: -------------------------------------------------------------------------------- 1 | #include "Platform.h" 2 | 3 | #include "util.h" 4 | 5 | #if defined(PLATFORM_WINDOWS) 6 | #include 7 | #include 8 | #elif defined(PLATFORM_LINUX) || defined(PLATFORM_MAC) 9 | #include 10 | #endif 11 | 12 | void* 13 | obtainHAPILHandle(const char *filename) 14 | { 15 | void *handle = nullptr; 16 | 17 | if (filename) 18 | { 19 | #if defined(PLATFORM_WINDOWS) 20 | handle = LoadLibraryA(filename); 21 | #elif defined(PLATFORM_LINUX) || defined(PLATFORM_MAC) 22 | handle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL); 23 | #endif 24 | 25 | if (handle) 26 | { 27 | Util::logVerboseSetupInfo("libHAPIL loaded!"); 28 | } 29 | else 30 | { 31 | char buffer[256] = { 0 }; 32 | #if defined(PLATFORM_WINDOWS) 33 | snprintf(buffer, 256, "libHAPIL failed to load!"); 34 | #else 35 | snprintf(buffer, 256, "libHAPIL failed to load! Error: %s", dlerror()); 36 | #endif 37 | Util::logVerboseSetupInfo(buffer, true); 38 | } 39 | } 40 | 41 | return handle; 42 | } 43 | 44 | void* 45 | fetchSymbol(void *handle, const char *symbolName) 46 | { 47 | void *symbol = nullptr; 48 | 49 | if (handle && symbolName) 50 | { 51 | #if defined(PLATFORM_WINDOWS) 52 | symbol = GetProcAddress((HMODULE)handle, symbolName); 53 | #elif defined(PLATFORM_LINUX) || defined(PLATFORM_MAC) 54 | symbol = dlsym(handle, symbolName); 55 | #endif 56 | } 57 | 58 | return symbol; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /scripts/AEhoudiniInputGeometryTemplate.mel: -------------------------------------------------------------------------------- 1 | global proc AEhoudiniInputGeometryTemplate(string $nodeName) 2 | { 3 | editorTemplate -beginScrollLayout; 4 | editorTemplate -beginLayout "Inputs" -collapse false; 5 | editorTemplate 6 | -label "Unlock Normals" 7 | -addControl "unlockNormals"; 8 | editorTemplate 9 | -label "Material Per Face" 10 | -addControl "materialPerFace"; 11 | editorTemplate 12 | -label "Allow Facet Only Sets" 13 | -addControl "allowFacetSet"; 14 | editorTemplate 15 | -label "Input Transform" 16 | -addControl "inputTransform"; 17 | editorTemplate 18 | -label "Object Shading Group" 19 | -addControl "objectShadingGroup"; 20 | editorTemplate 21 | -label "Primitive Component Group" 22 | -addControl "primComponentGroup"; 23 | editorTemplate 24 | -label "Point Component Group" 25 | -addControl "pointComponentGroup"; 26 | editorTemplate 27 | -label "Preserve Houdini Scale" 28 | -addControl "preserveScale"; 29 | editorTemplate 30 | -label "Ignore Transform" 31 | -addControl "ignoreTransform"; 32 | editorTemplate -endLayout; 33 | 34 | editorTemplate -beginLayout "Outputs" -collapse false; 35 | editorTemplate 36 | -label "Output Node Id" 37 | -addControl "outputNodeId"; 38 | editorTemplate -endLayout; 39 | 40 | editorTemplate -suppress "inputGeometry"; 41 | editorTemplate -suppress "inputComponents"; 42 | 43 | editorTemplate -addExtraControls; 44 | 45 | editorTemplate -endScrollLayout; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /SyncOutputInstance.h: -------------------------------------------------------------------------------- 1 | #ifndef __SyncOutputInstance_h__ 2 | #define __SyncOutputInstance_h__ 3 | 4 | #include 5 | #include 6 | 7 | #include "SubCommand.h" 8 | 9 | class SyncOutputInstance : public SubCommand 10 | { 11 | public: 12 | SyncOutputInstance(const MPlug &outputPlug, 13 | const int parentMultiIndex, 14 | const MObject &assetNodeObj); 15 | virtual ~SyncOutputInstance(); 16 | 17 | virtual MStatus doIt(); 18 | virtual MStatus undoIt(); 19 | virtual MStatus redoIt(); 20 | 21 | virtual bool isUndoable() const; 22 | 23 | protected: 24 | MStatus createOutput(); 25 | 26 | bool instanceObjects(MObject searchRoot, 27 | MObject instancerTransform, 28 | int pointIndex, 29 | const MString &objectToInstanceName, 30 | const MString &houdiniInstanceAttr, 31 | const MString &houdiniNameAttr); 32 | 33 | void instanceObject(MDagPath &objToInstance, 34 | MObject instancerTransform, 35 | int pointIndex); 36 | 37 | bool stringStartsWith(const MString &string, const MString &startsWith); 38 | 39 | protected: 40 | // This is the output plug from the asset node that is connected 41 | // to the instancer node (eg. assetNode.instancers[0]) 42 | const MPlug myOutputPlug; 43 | 44 | // the transform of the HAPI Asset 45 | const MObject myAssetNodeObj; 46 | 47 | // the multi-index of the parent attribute. We'll need this 48 | // to effectively evaluate any of the multi-attributes 49 | const int myParentMultiIndex; 50 | 51 | MDagModifier myDagModifier; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /houdiniEnginePlugin.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 11.00 2 | # Visual Studio 2010 3 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "houdiniEngine", "houdiniEnginePlugin.vcxproj", "{EBCF2E02-C072-083D-10BF-F9C69A73996A}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Win32 = Debug|Win32 8 | Debug|x64 = Debug|x64 9 | Release|Win32 = Release|Win32 10 | Release|x64 = Release|x64 11 | ReleaseDebug|Win32 = ReleaseDebug|Win32 12 | ReleaseDebug|x64 = ReleaseDebug|x64 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.Debug|Win32.ActiveCfg = Debug|Win32 16 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.Debug|Win32.Build.0 = Debug|Win32 17 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.Debug|x64.ActiveCfg = Debug|x64 18 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.Debug|x64.Build.0 = Debug|x64 19 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.Release|Win32.ActiveCfg = Release|Win32 20 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.Release|Win32.Build.0 = Release|Win32 21 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.Release|x64.ActiveCfg = Release|x64 22 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.Release|x64.Build.0 = Release|x64 23 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.ReleaseDebug|Win32.ActiveCfg = ReleaseDebug|Win32 24 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.ReleaseDebug|Win32.Build.0 = ReleaseDebug|Win32 25 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.ReleaseDebug|x64.ActiveCfg = ReleaseDebug|x64 26 | {EBCF2E02-C072-083D-10BF-F9C69A73996A}.ReleaseDebug|x64.Build.0 = ReleaseDebug|x64 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /doc/Maya_Group.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Group Group 4 | 5 | Houdini geometries can contain geometry groups, like primitive groups and point groups. A geometry group contains a subset of components of a geometry. For example, in a Houdini geometry, a subset of the polygons can belong to a primitive group. This is somewhat similar to Maya sets, where a subset of polygons can belong to a certain Maya set. 6 | 7 | There are two ways that groups can be interacted with in Maya: 8 | - With Maya sets 9 | - When Maya geometries that belong to certain Maya sets are input into an asset, Houdini groups will be created according to the Maya sets. When an asset outputs geometries that contain Houdini groups, Maya sets will be created according to the Houdini groups. 10 | - With promoted parameters 11 | - If a group node's "Base Group" parameter has been promoted to an HDA's parameters, you are able to define the group contents by component selection in Maya. To do so, you must add the "sidefx::maya_component_selection_type" tag to the promoted parameter. Valid selection types are "vertex", "edge", "face" and "uv". 12 | 13 | At the moment, Maya mesh, and Houdini polygons are the only geometry types that support Maya sets and Houdini groups. 14 | 15 | @section Maya_Group_Mesh Mesh 16 | 17 | When inputting Maya mesh, if the faces or points of the mesh belong to some Maya sets, then Houdini primitive groups or point groups will be created according to the Maya sets. When outputting Houdini polygons into Maya, if some of the polygons or points belong to some Houdini groups, then Maya sets will be created according to the Houdini groups. Note that in order to assign Maya geometry components to Maya sets, nodes and connections need to be created in the Maya scene. This means that if the output Houdini group changes, a @ref Maya_Outputs_Sync sync is needed in Maya in order to see the updated sets. 18 | 19 | */ 20 | -------------------------------------------------------------------------------- /scripts/houdiniEngineAssetSync.mel: -------------------------------------------------------------------------------- 1 | global proc 2 | houdiniEngine_syncSelectedAsset() 3 | { 4 | string $old_selection[] = `ls -selection -long`; 5 | 6 | string $assetNodes[] = houdiniEngine_getSelectedAssetNodes(); 7 | if(size($assetNodes) < 1) 8 | { 9 | error("Please select at least one houdiniAsset node to sync."); 10 | } 11 | 12 | for($assetNode in $assetNodes) 13 | { 14 | houdiniEngine_syncAsset($assetNode); 15 | } 16 | 17 | catchQuiet(`select -replace $old_selection`); 18 | } 19 | 20 | proc 21 | syncAsset(string $assetNode, int $syncAttributes, int $syncOutputs) 22 | { 23 | string $cmd; 24 | // pre and post sync callbacks have been moved inside the houdiniAsset command 25 | $cmd += "houdiniAsset "; 26 | 27 | if($syncAttributes) 28 | { 29 | $cmd += "-syncAttributes "; 30 | } 31 | 32 | if($syncOutputs) 33 | { 34 | $cmd += "-syncOutputs "; 35 | } 36 | 37 | if(`getAttr ($assetNode + ".outputHiddenObjects")`) 38 | { 39 | $cmd += "-syncHidden "; 40 | } 41 | 42 | if(`getAttr ($assetNode + ".outputTemplatedGeometries")`) 43 | { 44 | $cmd += "-syncTemplatedGeos "; 45 | } 46 | 47 | $cmd += "-sync " + $assetNode; 48 | eval($cmd); 49 | } 50 | 51 | global proc 52 | houdiniEngine_autoSyncAssetOutput(string $assetNode, int $autoSyncId) 53 | { 54 | if($autoSyncId != `houdiniAsset -autoSyncId $assetNode`) 55 | { 56 | return; 57 | } 58 | 59 | houdiniEngine_syncAssetOutput($assetNode); 60 | } 61 | 62 | global proc 63 | houdiniEngine_syncAssetOutput(string $assetNode) 64 | { 65 | syncAsset($assetNode, 0, 1); 66 | } 67 | global proc 68 | houdiniEngine_syncAssetAttributes(string $assetNode) 69 | { 70 | syncAsset($assetNode, 1, 0); 71 | } 72 | 73 | global proc 74 | houdiniEngine_syncAsset(string $assetNode) 75 | { 76 | syncAsset($assetNode, 0, 0); 77 | } 78 | -------------------------------------------------------------------------------- /doc/Maya_Material.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Material Material 4 | 5 | @tableofcontents 6 | 7 | At the moment, mesh is the only geometry that has material support. Material support is implemented through string attributes, namely `maya_shading_group` and `shop_materialpath`. Similar to a typical Houdini workflow, setting these string attributes is equivalent to assigning materials. 8 | 9 | - `maya_shading_group` - detail|primitive string 10 | 11 | When geometry is inputted from Maya into a Houdini asset, the name of the Maya shading group that is assigned to the geometry is stored in the `maya_shading_group` string attribute. When this string attribute is output from an asset, the output Maya geometry will be assigned to the corresponding shading group. 12 | 13 | The original intention of the attribute is to preserve the material assignment when the geometry is input into, and output from Houdini Engine. However, the attribute could also be used to control the material assignment of the output geometry. For example, the asset could explicitly create the `maya_shading_group` attribute, and set it to the name of a Maya shading group. Then when the geometry is output into Maya, if the shading group exists, the geometry will be assigned to the corresponding shading groups. 14 | 15 | - `shop_materialpath` - detail|primitive string 16 | 17 | In a typical Houdini workflow, materials are assigned through the `shop_materialpath` attribute. When an asset outputs geometries that contain Houdini materials, some basic parameters of the materials are recreated in the Maya scene. Materials in Maya are created as `phong`. And the basic parameters `ogl_diff`, `ogl_amb`, `ogl_alpha`, and `ogl_spec`, `ogl_tex1` of the Houdini material is output into the Maya material. 18 | 19 | If your HDA has materials where file textures are being baked out for Maya, we recommend that in Maya's Display Preferences you set the MaterialLoadingMode to Immediate in order to be able to see the changes to the output textures updating. 20 | 21 | */ 22 | -------------------------------------------------------------------------------- /scripts/houdiniEngineUtils.py: -------------------------------------------------------------------------------- 1 | from maya import mel 2 | 3 | import os 4 | import subprocess 5 | import platform 6 | 7 | def sanitizeAndRun(commands): 8 | """ 9 | Run a command, or series of commands, in a sanitized environment free from 10 | paths to Maya. Environment variables that start with "MAYA_" are not 11 | sanitized. 12 | """ 13 | def scrubMayaFromEnv(env_key, env_value): 14 | values = env_value.split(os.pathsep) 15 | 16 | maya_loc = os.environ['MAYA_LOCATION'] 17 | 18 | if not env_key.lower().startswith('maya_'): 19 | values = [value for value in values if maya_loc not in value and 20 | 'autodesk' not in value.lower() and 21 | 'mayausd' not in value.lower()] 22 | 23 | return os.pathsep.join(values) 24 | 25 | hfs_path = mel.eval('houdiniEngine_getHfsPath(false)') 26 | 27 | if not hfs_path: 28 | return False 29 | 30 | env = os.environ.copy() 31 | env['HFS'] = hfs_path 32 | 33 | for k, v in env.items(): 34 | env[k] = scrubMayaFromEnv(k, v) if not k.lower().startswith('maya_') else v 35 | 36 | env['PATH'] = os.path.join(hfs_path, 'bin') + os.pathsep + env['PATH'] 37 | 38 | command_sep = ' && ' 39 | 40 | if platform.system() == 'Windows': 41 | command_sep = ' & ' 42 | 43 | command_str = command_sep.join(commands) 44 | 45 | DEBUG = False 46 | 47 | if DEBUG: # noqa 48 | p = subprocess.Popen(command_str, env=env, 49 | stdout=subprocess.PIPE, stderr=subprocess.PIPE, 50 | shell=True) 51 | stdout, stderr = p.communicate() 52 | 53 | if p.returncode != 0: 54 | print(stdout) 55 | print(stderr) 56 | return False 57 | else: 58 | subprocess.Popen(command_str, env=env, 59 | stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, 60 | shell=True) 61 | 62 | return True 63 | -------------------------------------------------------------------------------- /MayaTypeID.h: -------------------------------------------------------------------------------- 1 | #ifndef __MayaTypeID_h__ 2 | #define __MayaTypeID_h__ 3 | 4 | // Inside Maya, the type IDs are used to identify nodes and dependency graph 5 | // data. So when we create custom nodes and data in any Maya plugins, they must 6 | // all be assigned unique IDs. For any plugins that will be used outside of 7 | // Side Effects, we *must* assign globally unique IDs. However, for plugins 8 | // that are only used internally inside Side Effects, we could assign the 9 | // "internal IDs" (0x0 - 0x7ffff). 10 | 11 | // Some important notes from the Maya SDK docs: 12 | 13 | // - In Maya, both intrinsic and user-defined Maya Objects are registered 14 | // and recognized by their type identifier or type id. 15 | 16 | // - It is very important to note that these ids are written into the Maya 17 | // binary file format. So, once an id is assigned to a node or data type it 18 | // can never be changed while any existing Maya file contains an instance 19 | // of that node or data type. If a change is made, such files will become 20 | // unreadable. 21 | 22 | // - For plug-ins that will forever be internal to your site use the 23 | // constructor that takes a single unsigned int parameter. The numeric 24 | // range 0 - 0x7ffff (524288 ids) has been reserved for such plug-ins. 25 | 26 | // For more information on these IDs, please refer to the documentation for 27 | // MTypeId in the Maya SDK. 28 | 29 | // Globally unique IDs assigned to Side Effects: 30 | // - 0x0011E240 - 0x0011E2BF : 128 IDs requested by Andrew Wong on 31 | // 2013-07-17 13:55 32 | 33 | // More IDs can be requested through the Autodesk Developer Network. 34 | 35 | // Globally unique IDs being used by Side Effects 36 | enum MayaTypeID 37 | { 38 | MayaTypeID_HoudiniAssetNode = 0x0011E240, 39 | MayaTypeID_HoudiniFluidGridConvert = 0x0011E241, 40 | MayaTypeID_HoudiniInputGeometryNode = 0x0011E242, 41 | MayaTypeID_HoudiniInputCurveNode = 0x0011E243, 42 | MayaTypeID_HoudiniInputTransformNode = 0x0011E244, 43 | MayaTypeID_HoudiniInputMergeNode = 0x0011E245, 44 | MayaTypeID_HoudiniOutputPartInstancerNode = 0x0011E246, 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /OutputDeform.h: -------------------------------------------------------------------------------- 1 | #ifndef __OutputDeform_h__ 2 | #define __OutputDeform_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | class OutputObject; 11 | 12 | class OutputDeformCache 13 | { 14 | public: 15 | OutputDeformCache() 16 | : myIsDouble(false) 17 | , myDirty(true) 18 | , myNeed(true) 19 | , myIsValid(false) 20 | , myStride(-1) 21 | {} 22 | 23 | std::vector myData32; 24 | std::vector myData64; 25 | bool myIsDouble; 26 | bool myDirty; 27 | bool myNeed; 28 | bool myIsValid; 29 | int myStride; 30 | 31 | }; 32 | 33 | class OutputDeform 34 | { 35 | public: 36 | OutputDeform(bool topo=false, bool normal=false, bool skippoints=false, bool uvs=false) 37 | : myNeedTopo(topo) 38 | , myTopoChanged(true) 39 | , myTopoDirty(true) 40 | , myTopoValid(false) 41 | , mySkipPointAttributes(skippoints) 42 | 43 | , myDelayedPointCount(0) 44 | , myDelayedNodeId(0) 45 | , myDelayedPartId(0) 46 | { 47 | myNormal.myNeed = normal; 48 | myTexture.myNeed = uvs; 49 | myTexture.myStride = 2; // Pull uv vector as vector2 50 | } 51 | 52 | bool getPointAttribute(const HAPI_Session *session, 53 | const HAPI_NodeId &nodeId, const HAPI_PartId &partId, 54 | const char *name, OutputDeformCache &cache, size_t n, 55 | float *buf=nullptr); 56 | 57 | bool getDelayedPointAttribute( 58 | const char *name, OutputDeformCache &cache, size_t n, 59 | float *buf); 60 | 61 | // Compute the positions of the first part of obj 62 | bool compute(OutputObject *obj, size_t &n); 63 | 64 | OutputDeformCache myPos; 65 | OutputDeformCache myNormal; 66 | OutputDeformCache myTexture; 67 | 68 | std::vector myFaceCounts; 69 | std::vector myVertexList; 70 | 71 | HAPI_PartInfo myPartInfo; 72 | 73 | bool myNeedTopo; 74 | bool myTopoChanged; 75 | bool myTopoDirty; // Dirty flag for the drawing code, The draw code resets it. 76 | bool myTopoValid; // Dirty flag for the drawing code, The draw code resets it. 77 | bool mySkipPointAttributes; // Delay the copy of the point attributes 78 | 79 | size_t myDelayedPointCount; 80 | HAPI_NodeId myDelayedNodeId; 81 | HAPI_PartId myDelayedPartId; 82 | }; 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /SyncOutputGeometryPart.h: -------------------------------------------------------------------------------- 1 | #ifndef __SyncOutputGeometryPart_h__ 2 | #define __SyncOutputGeometryPart_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "SubCommand.h" 9 | 10 | class SyncOutputGeometryPart : public SubCommand 11 | { 12 | public: 13 | SyncOutputGeometryPart(const MPlug &outputPlug, 14 | const MObject &objectTransform); 15 | virtual ~SyncOutputGeometryPart(); 16 | 17 | virtual MStatus doIt(); 18 | MStatus doItPost(SyncOutputGeometryPart *const *syncParts); 19 | virtual MStatus undoIt(); 20 | virtual MStatus redoIt(); 21 | 22 | virtual bool isUndoable() const; 23 | 24 | const MObject &partTransform() const { return myPartTransform; } 25 | 26 | bool isInstanced() { return myIsInstanced; } 27 | 28 | void setIsInstanced(bool isInstanced) { myIsInstanced = isInstanced; } 29 | 30 | protected: 31 | MStatus createOutputPart(const MObject &objectTransform, 32 | const MString &partName); 33 | MStatus createOutputMesh(const MString &partName, const MPlug &meshPlug); 34 | MStatus createOutputParticle(const MString &partName, 35 | const MPlug &particlePlug); 36 | MStatus createOutputCurves(MPlug curvesPlug, 37 | const MString &partName, 38 | bool isBezier); 39 | MStatus createOutputInstancer(const MString &partName, 40 | const MPlug &instancerPlug); 41 | MStatus createOutputInstancerPost(const MPlug &instancerPlug, 42 | SyncOutputGeometryPart *const *syncParts); 43 | MStatus createOutputExtraAttributes(const MObject &dstNode, 44 | MPlug *mayaSGAttributePlug = NULL); 45 | MStatus createOutputGroups(const MObject &dstNode, 46 | std::vector *hasMaterials = NULL); 47 | 48 | protected: 49 | // This is the output plug from the asset node that represents an object 50 | // eg. (assetNode.objects[1]) 51 | const MPlug myOutputPlug; 52 | 53 | // the transform of the HAPI Asset 54 | const MObject myObjectTransform; 55 | 56 | MDagModifier myDagModifier; 57 | 58 | MObject myPartTransform; 59 | MObjectArray myPartShapes; 60 | 61 | bool myIsInstanced; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /doc/Maya_Instancing.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Instancing Instancing 4 | 5 | @tableofcontents 6 | 7 | @section Maya_Instancing_Output Output 8 | 9 | The plug-in supports outputting instances. Instances can be outputted from an asset in two ways, packed primitive or instancer object. Previously, instancer object was the only way to output instances. However, since support for packed primitive was added, using packed primitive is the recommended way to output instances. Packed primitive can be represented in Maya in two ways, as a particle instancer, or as transform instances. Using particle instancer is also recommended, since it is more efficient than using transform instance, so it is able to handle large number of instances. 10 | 11 | @subsection Maya_Instancing_Output_ParticleInstancer Particle Instancer 12 | 13 | The recommended way to output instances is to use Maya's particle instancer. This will output the packed primitive using the instancer node, which is used for Maya's particle instancing. 14 | 15 | Since adding and removing instances does not require modifying the Maya scene, sync is not needed when instances are added or removed. 16 | 17 | Point and primitive attributes of the packed primitives are also available on the instancer node. 18 | 19 | @subsection Maya_Instancing_Output_TransformInstance Transform Instance 20 | 21 | Transform instances use DAG parenting to create instances. This creates DAG instances of the geometries in the Maya scene, which may be desirable depending on the situation. However, for large number of instances, this is less efficient than using particle instancer. 22 | 23 | Since DAG parenting require modification to the Maya scene, sync is required when instances are added or removed. 24 | 25 | Point and primitive attributes of the packed primitives are not available at the moment. 26 | 27 | @subsection Maya_Instancing_Output_NestedPackedPrimitive Nested Packed Primitive 28 | 29 | It's also possible to output nested packed primitives. This is where a packed primitive contains other packed primitives. 30 | 31 | When using particle instancer, the nested packed primitives will be flattened, and each geometry being instanced is put into its own instancer. This is because particle instancer doesn't support instancing another particle instancer. 32 | 33 | When using transform instances, the nested packed primitives are represented exactly. This is because DAG parenting is able to instance other DAG instances. 34 | 35 | */ 36 | -------------------------------------------------------------------------------- /scripts/houdiniEngineSelection.py: -------------------------------------------------------------------------------- 1 | from maya import cmds 2 | 3 | def get_selected_components(component_type): 4 | components = [] 5 | sel_mask = -1 6 | type_name = '' 7 | expand = False 8 | 9 | lwr_component_type = component_type.lower() 10 | 11 | if lwr_component_type == 'vertex': 12 | sel_mask = 31 13 | type_name = 'vertices' 14 | elif lwr_component_type == 'edge': 15 | sel_mask = 32 16 | type_name = 'edges' 17 | expand = True 18 | elif lwr_component_type == 'face': 19 | sel_mask = 34 20 | type_name = 'faces' 21 | elif lwr_component_type == 'uv': 22 | sel_mask = 35 23 | type_name = 'UVs' 24 | else: 25 | cmds.error('Unknown component type. Must be "vertex", "edge", "face" or "uv".') 26 | 27 | def extract_component(component): 28 | lidx = component.find('[') + 1 29 | ridx = component.rfind(']') 30 | 31 | if lidx <= 0 or ridx <= 0 or ridx <= lidx: 32 | cmds.warning('Could not determine %s index of "%s"' % (type_name, component)) 33 | 34 | return component[lidx:ridx] 35 | 36 | components_raw = cmds.filterExpand(cmds.ls(sl=True), sm=sel_mask, ex=expand) 37 | 38 | if not components_raw: 39 | return None 40 | 41 | # Edges are handled differently in Houdini. Instead of an edge index, it is 42 | # specified as two points with a "p" prefix. Eg "p1-3" represents an edge 43 | # between point 1 and point 3. 44 | if lwr_component_type == 'edge': 45 | vertex_components = [] 46 | 47 | for component in components_raw: 48 | raw_vtx_components = cmds.polyListComponentConversion(component, fe=True, tv=True) 49 | 50 | if len(raw_vtx_components) == 2: 51 | vertex_components.append('p' + '-'.join([extract_component(raw_vtx_component) for raw_vtx_component in raw_vtx_components])) 52 | elif len(raw_vtx_components) == 1: 53 | vertex_components.append('p' + extract_component(raw_vtx_components[0]).replace(':', '-')) 54 | else: 55 | cmds.error('Unable to convert edge with %d vertices to a single range of vertices.' % len(raw_vtx_components)) 56 | 57 | return ' '.join(vertex_components) 58 | 59 | for component in components_raw: 60 | components.append(extract_component(component).replace(':', '-')) 61 | 62 | return ' '.join(components) 63 | -------------------------------------------------------------------------------- /scripts/houdiniEngineBakeAsset.mel: -------------------------------------------------------------------------------- 1 | source houdiniEngineAssetSync; 2 | 3 | global proc int 4 | houdiniEngine_bakeAsset(string $assetNode) 5 | { 6 | // sync the asset to make sure all the outputs exist 7 | houdiniEngine_syncAssetOutput $assetNode; 8 | 9 | // disconnect output objects 10 | string $children[] = `listRelatives -c -f $assetNode`; 11 | 12 | // if an output was completely empty (only has transforms) delete it 13 | // delete construction history on mesh shapes: which doesn't delete plugin history 14 | // but will deal with sets and groups (i.e. complex material assignement) 15 | for($child in $children) { 16 | string $allDescendants[] = `listRelatives -ad -f $child`; 17 | int $hasShape = 0; 18 | for($desc in $allDescendants) { 19 | if(`objectType -isAType "shape" $desc`) { 20 | delete -ch $desc; 21 | $hasShape = 1; 22 | } 23 | if(`objectType -isAType "instancer" $desc`) { 24 | $hasShape = 1; 25 | break; 26 | } 27 | } 28 | if($hasShape) { 29 | if( `getAttr ($assetNode + ".ungroupOnBake")`) 30 | ungroup -a -w $child; 31 | else 32 | parent -a -w $child; 33 | } 34 | else 35 | delete $child; 36 | } 37 | 38 | refresh; 39 | 40 | string $outputObjCon[] = `listConnections -p on ($assetNode + ".outputObjects")`; 41 | for($con in $outputObjCon) { 42 | string $src = `connectionInfo -sfd $con`; 43 | disconnectAttr $src $con; 44 | } 45 | 46 | // disconnect output materials 47 | string $outputMatCon[] = `listConnections -p on ($assetNode + ".outputMaterials")`; 48 | 49 | for($con in $outputMatCon) { 50 | string $src = `connectionInfo -sfd $con`; 51 | disconnectAttr $src $con; 52 | } 53 | 54 | // disconnect output instancers 55 | string $outputInstCon[] = `listConnections -p on ($assetNode + ".outputInstancers")`; 56 | 57 | for($con in $outputInstCon) { 58 | string $src = `connectionInfo -sfd $con`; 59 | disconnectAttr $src $con; 60 | } 61 | 62 | // don't disconnect output transforms, since they connect back to the asset 63 | // connection is determinded by the asset options 64 | // and not a particular output 65 | 66 | int $deleteAfterBake = 0; 67 | if(`optionVar -exists houdiniEngineDeleteAfterBake`) 68 | $deleteAfterBake = `optionVar -q houdiniEngineDeleteAfterBake`; 69 | 70 | // either delete the asset after baking, 71 | // or sync it again - so there is a new output to provide feedback 72 | if($deleteAfterBake) 73 | delete $assetNode; 74 | else 75 | houdiniEngine_syncAssetOutput $assetNode; 76 | 77 | return 1; 78 | } 79 | 80 | 81 | -------------------------------------------------------------------------------- /Input.h: -------------------------------------------------------------------------------- 1 | #ifndef __Input_h__ 2 | #define __Input_h__ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | class Input; 15 | 16 | class Inputs 17 | { 18 | public: 19 | Inputs(HAPI_NodeId nodeId); 20 | ~Inputs(); 21 | 22 | MStatus compute(MDataBlock &dataBlock); 23 | 24 | void setNumInputs(int numInputs); 25 | 26 | private: 27 | HAPI_NodeId myNodeId; 28 | 29 | typedef std::vector AssetInputVector; 30 | AssetInputVector myAssetInputs; 31 | }; 32 | 33 | class Input 34 | { 35 | public: 36 | enum AssetInputType 37 | { 38 | AssetInputType_Invalid, 39 | AssetInputType_Mesh, 40 | AssetInputType_Curve, 41 | AssetInputType_Particle, 42 | }; 43 | 44 | static Input *createAssetInput(AssetInputType assetInputType); 45 | 46 | public: 47 | Input(); 48 | virtual ~Input(); 49 | 50 | virtual AssetInputType assetInputType() const = 0; 51 | 52 | HAPI_NodeId transformNodeId() const { return myTransformNodeId; }; 53 | HAPI_NodeId geometryNodeId() const { return myGeometryNodeId; }; 54 | void setUnlockNormals(bool unlockNormals); 55 | void setMatPerFace(bool matPerFace); 56 | void setAllowFacetSet(bool allowFacetSet); 57 | void setPreserveScale(bool preserveScale); 58 | 59 | void setInputName(HAPI_AttributeOwner owner, int count, const MPlug &plug); 60 | 61 | void setInputTransform(MDataHandle &dataHandle); 62 | 63 | virtual void setInputGeo(MDataBlock &dataBlock, const MPlug &plug) = 0; 64 | 65 | virtual void setInputComponents(MDataBlock &dataBlock, 66 | const MPlug &geoPlug, 67 | const MPlug &compPlug, 68 | const MPlug &primGroupPlug, 69 | const MPlug &pointGroupPlug); 70 | 71 | protected: 72 | void setTransformNodeId(HAPI_NodeId nodeId) { myTransformNodeId = nodeId; }; 73 | void setGeometryNodeId(HAPI_NodeId nodeId) { myGeometryNodeId = nodeId; }; 74 | 75 | bool myUnlockNormals; 76 | bool myMatPerFace; 77 | bool myAllowFacetSet; 78 | bool myPreserveScale; 79 | 80 | private: 81 | static void nameChangedCallback(MObject &node, 82 | const MString &str, 83 | void *clientData); 84 | void addNameChangedCallback(MObject &node); 85 | void removeNameChangedCallback(); 86 | 87 | private: 88 | HAPI_NodeId myTransformNodeId; 89 | HAPI_NodeId myGeometryNodeId; 90 | 91 | MPlug myGeoPlug; 92 | 93 | MCallbackIdArray myNameChangedCallbackIds; 94 | MObject myNameChangedCallbackNode; 95 | }; 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /doc/Maya_Shelves.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Shelves Shelves 4 | 5 | @tableofcontents 6 | 7 | There are now two shelves available for use with the houdiniEngine plugin in maya 8 | - @ref Maya_Shelves_HoudiniShelf "houdini" 9 | - @ref Maya_Shelves_HoudiniTools "houdiniTools" 10 | 11 | Since maya shelves are saved locally once they are loaded, you will need to update your shelves if they have been modified in a new install of houdini There are menu items to update the tools: 12 | - Update Houdini Shelf 13 | - Update Houdini Tools Shelf 14 | 15 | The Houdini shelf is static and may not need to be updated very often. The Houdini Tools shelf is built dyncamically based on the hda's currently in the engine Tools directory, so it needs to rebuild for a new install in order to point at the correct tools directory, as well as to include any new tools that may have been added 16 | 17 | @section Maya_Shelves_HoudiniShelf houdini Shelf 18 | 19 | The houdini shelf has two sections: 20 | 21 | Loading the plugin: 22 | - set session type to socket (SoS) 23 | - set session type to autostart named pipe 24 | - set session to custom named pipe 25 | - load plugin 26 | 27 | Menu items that operate on selected nodes 28 | - sync asset (SA) 29 | - bake asset (BA) 30 | - remove asset from history (RAH) 31 | - add asset to history (AAH) 32 | - delete construction history, including assets, from this mesh (DH) 33 | - reload asset definition (RA) 34 | 35 | @section Maya_Shelves_HoudiniTools houdini Tools 36 | 37 | The tools shelf is build from all the tools available in the shared engine Tools. Each tool has a JSON description file as well as the hda. 38 | 39 | These tool types are currently supported 40 | - OPERATOR_SINGLE 41 | - OPERATOR_MULTI 42 | - HISTORY 43 | 44 | A SINGLE tool will add the selected objects (merging if needed) to the first input of the asset. You can also apply a SINGLE tool to a selected mesh as construction history by using the shelf item's RMB menu. 45 | 46 | A MULTI tool will match one selected item to each of the tools input, in the order they were selected 47 | 48 | A HISTORY tool will be connected as construction history to a single selected mesh, or to the selected faces or vertices from a mesh 49 | 50 | The initial set of houdiniEngine tools are: 51 | - he_boolean 52 | - he_calculate_occlusion 53 | - he_curve_instancer 54 | - he_delete_small_parts 55 | - he_polyreduce 56 | - he_set_pivot 57 | - he_surface_instancer 58 | 59 | @section Maya_Shelves_CustomTools 60 | 61 | You can create custom shelf items for your own HDA's if the follow one of the connection styles above. For example, the shelf script for my extrudeFaces hda might look like: 62 | 63 | @verbatim 64 | if(!`pluginInfo -query -loaded houdiniEngine`) 65 | loadPlugin houdiniEngine; 66 | if(!`exists houdiniEngine_loadAndAddAsset`) 67 | source houdiniEngineHistoryAsset; 68 | houdiniEngine_loadAndAddAsset "/home/julia/houdini17.0/otls/extrudeFaces.hda" "Sop/extrudeFaces" "HISTORY" 69 | @endverbatim 70 | 71 | 72 | 73 | 74 | */ 75 | -------------------------------------------------------------------------------- /doc/Maya_Freeze.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Freeze Freezing Assets 4 | 5 | @tableofcontents 6 | 7 | Freezing an asset allows you to cache the assets outputs and disable the loading and cooking of the asset in engine. This allows a maya scene with frozen assets to be passed to a non-houdini artist, who can load the scene and operate on the non-houdini parts without pulling an engine license. This differs from baking in that it is reversable - Output shapes remain parented to the asset, we just replace the data connections with message connections, and the then swap back the real connections on unfreeze 8 | 9 | 10 | @section Maya_Freeze_Menus Menus 11 | 12 | Freezing or unfreezing an asset should be done from the menu oe from the mel commands. Just setting the frozen state on the asset is not enough, we also need to add and set the attributes for keeping track of the output connections to restore, and make sure that the output geometry is always cached. 13 | 14 | @image html Maya_Freeze_Menu.png 15 | 16 | - Freeze Asset 17 | 18 | Replaces all the output geometry connection with message connections and sets the Frozen attribute on (i.e. the assets inputs will not be dirtied by chagnges to the unputs. Adds proxy shapes where there was history between the asset and its output geometry. 19 | 20 | - Unfreeze Asset 21 | 22 | Restores the original output connections, turns off the frozen state so that the asset can pull on the input geo, loads the assets (pulling a license). 23 | 24 | The corresponding mel commands, for freezing and unfreezing the output of the selected asset are: 25 | 26 | @verbatim 27 | houdiniEngine_freezeAsset(string $assetNode) 28 | e.g. houdiniEngine_freezeAsset("|extrude1") 29 | houdiniEngine_unfreezeAsset(string $assetNode) 30 | e.g. houdiniEngine_unfreezeAsset("|extrude1") 31 | @endverbatim 32 | 33 | @section Maya_Freeze_Attributes Attributes 34 | 35 | Here's an example of how the asset nodes are modified when frozen, in this case using the he_boolean asset from the HoudiniTools shelf: 36 | 37 | @image html Maya_Freeze_Attrs.png 38 | 39 | We've added a list of cached source and destination attributes on the asset for all the output node connections, and we've added message attributes on the asset, and on each output node as placeholders for the connections. By keeping connections rather than node names we allow renaming of the nodes, so we can support workflows such as unfreezing a referenced asset. And yes, having multiple proxy connections to the same output node may seem excessive, but keeping the attribute and node mappings lined up does make the attribute structure simpler. 40 | 41 | For the most part breaking the data connection to the output maya geo will leave the geometry definition saved on the shape, but in case there is intermediate history betweent the asset and the output shape, we add a proxy geometry node to cache the output geo. In the example, we see that there is a houdiniAssetProxyMesh anchoring the history chain connected to the output shape. This proxy shape will be removed when the asset is unfrozen. 42 | 43 | You'll note that the input geomtry is still connected, but does not dirty the asset because the asset is frozen. 44 | 45 | */ 46 | -------------------------------------------------------------------------------- /scripts/houdiniEngineAssetOptions.mel: -------------------------------------------------------------------------------- 1 | proc string[] getUpstreamNodes(string $node, string $type) 2 | { 3 | string $upstreamNodes[] = `hyperShade -lun $node`; 4 | 5 | string $filteredNodes[]; 6 | 7 | for ($upstreamNode in $upstreamNodes) 8 | { 9 | if (`nodeType $upstreamNode` == $type) 10 | $filteredNodes[size($filteredNodes)] = $upstreamNode; 11 | } 12 | 13 | return $filteredNodes; 14 | } 15 | 16 | global proc 17 | houdiniEngine_preserveHoudiniScaleChanged(string $assetNode) 18 | { 19 | string $upstreamInputGeos[] = getUpstreamNodes($assetNode, "houdiniInputGeometry"); 20 | string $upstreamAssets[] = getUpstreamNodes($assetNode, "houdiniAsset"); 21 | string $upstreamInputXforms[] = getUpstreamNodes($assetNode, "houdiniInputTransform"); 22 | string $upstreamInputCurves[] = getUpstreamNodes($assetNode, "houdiniInputCurve"); 23 | 24 | int $preserveScale = `getAttr ($assetNode + ".preserveScale")`; 25 | 26 | for ($upstreamInputGeo in $upstreamInputGeos) 27 | setAttr ($upstreamInputGeo + ".preserveScale") $preserveScale; 28 | 29 | for ($upstreamAsset in $upstreamAssets) 30 | setAttr ($upstreamAsset + ".preserveScale") $preserveScale; 31 | 32 | for ($upstreamInputXform in $upstreamInputXforms) 33 | setAttr ($upstreamInputXform + ".preserveScale") $preserveScale; 34 | 35 | for ($upstreamInputCurve in $upstreamInputCurves) 36 | setAttr ($upstreamInputCurve + ".preserveScale") $preserveScale; 37 | } 38 | 39 | global proc 40 | houdiniEngine_alwaysMergeInputGeometryChanged(string $assetNode) 41 | { 42 | // Handle inputs connected to asset parms 43 | if (`attributeExists "houdiniAssetParm" $assetNode`) 44 | { 45 | string $parmAttrNames[] = `listAttr -st "*__node" ($assetNode + ".houdiniAssetParm")`; 46 | 47 | for ($parmAttrName in $parmAttrNames) 48 | { 49 | string $attr = $assetNode + ".houdiniAssetParm." + $parmAttrName; 50 | houdiniEngine_setAssetInput($attr, houdiniEngine_getAssetInput($attr)); 51 | } 52 | 53 | int $inputMultiIndices[] = `getAttr -multiIndices ($assetNode + ".input")`; 54 | 55 | for ($inputMultiIndex in $inputMultiIndices) 56 | { 57 | string $attr = $assetNode + ".input[" + $inputMultiIndex + "].inputNodeId"; 58 | houdiniEngine_setAssetInput($attr, houdiniEngine_getAssetInput($attr)); 59 | } 60 | } 61 | 62 | // Handle inputs that are connected to asset inputs 63 | int $nInputs = `getAttr -s ($assetNode + ".input")`; 64 | 65 | for ($i = 0; $i < $nInputs; $i++) 66 | { 67 | houdiniEngine_setAssetInput($assetNode + ".input[" + $i + "].inputNodeId", 68 | houdiniEngine_getAssetInput($assetNode + ".input[" + $i + "].inputNodeId")); 69 | } 70 | 71 | // Make sure that the packing setting has been applied properly 72 | houdiniEngine_packBeforeMergeChanged($assetNode); 73 | } 74 | 75 | global proc 76 | houdiniEngine_packBeforeMergeChanged(string $assetNode) 77 | { 78 | string $upstreamInputMerges[] = getUpstreamNodes($assetNode, "houdiniInputMerge"); 79 | 80 | int $packBeforeMerge = `getAttr ($assetNode + ".packBeforeMerge")`; 81 | 82 | for ($upstreamInputMerge in $upstreamInputMerges) 83 | setAttr ($upstreamInputMerge + ".packBeforeMerge") $packBeforeMerge; 84 | } 85 | -------------------------------------------------------------------------------- /doc/Maya_Outputs.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Outputs Outputs 4 | 5 | @tableofcontents 6 | 7 | The plug-in supports outputting various types of Maya geometries from Houdini assets. 8 | 9 | The supported Maya geometry types for input are: 10 | - @ref Maya_Mesh_Output "Mesh" 11 | - @ref Maya_Particle_Output "Particle" 12 | - @ref Maya_Curve_Output "Curve" 13 | - @ref Maya_Volume_Output "Volume" 14 | - @ref Maya_Instancing_Output "Instancing" 15 | 16 | Outputting geometries from Houdini assets is fairly straightforward. Geometries are outputted from the SOP nodes that have the display flag set. For SOP-type assets, the display SOP within the SOP subnet will be outputted. For Object-type assets, the display SOP of each Object node within the Object subnet will be outputted. 17 | 18 | @section Maya_Outputs_ExtraAttributes Custom Attributes 19 | 20 | Depending on which geometry type is being outputted, the plug-in will recognize and use certain standard attributes. For example, `N` and `uv` attributes are used to output Maya meshes, and `v` and `id` attributes are used to output Maya particles. In addition to the standard attributes, it is common for Houdini assets to output custom attributes. When the output Houdini geometry contains attributes that are not recognized by the geometry type, the attributes will be treated as custom attributes. Custom attributes will be outputted onto the Maya shape node as an extra attribute. 21 | 22 | Detail, primitive, vertex, and point class attributes are supported. The supported data types for custom attributes are: 23 | - float 24 | - int 25 | - string 26 | 27 | Tuples of the above data types are also supported. 28 | 29 | There are some restrictions when outputting particles. See @ref Maya_Particle_Output "particles" for more details. 30 | 31 | @section Maya_Outputs_Sync Sync 32 | 33 | In Houdini, a SOP can output any combination of geometry types, and the output geometry can change dynamically depending on many factors, such as parameters, input geometries, and asset logic. On the other hand, Maya requires a specific shape node for each specific kind of geometry. For example, creating polygons in Maya would require a `mesh` node, and creating particles require a `nParticle` node. 34 | 35 | This means that depending on what an asset outputs, the corresponding output nodes on the Maya side have to change. Otherwise, some of the geometries that the asset outputs would not incorrect or missing available on the Maya side. 36 | 37 | For example, it's easy to create an asset that uses a Switch SOP to switch between a box and some points. Depending on the parameter on the Switch SOP, the output geometry could be polygons or points. If the asset is outputting the box, then the Maya scene would require a `mesh` node. And if the parameter is changed, and the asset is outputting points, then the Maya scene would require a `nParticle` node. So it's important to keep the Maya shape nodes in sync with what the asset is outputting. 38 | 39 | Another common scenario is an asset that emits particles. In the initial frame, the asset might not be emitting any particles yet. This means if the asset is instantiated in the initial frame, the asset won't be outputting any geometry at all, and no shape node will be created on the Maya side. So when the asset does begin to emit particles, they will not be visible in Maya because no shape node was created. You will need to sync the asset representation in Maya to match what the asset is actually outputting. 40 | 41 | */ 42 | -------------------------------------------------------------------------------- /doc/Maya_Install.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Install Install 4 | 5 | @tableofcontents 6 | 7 | Houdini Engine for Maya is bundled together with the Houdini installer. Once the Houdini installer is started, simply select the Houdini Engine for Maya component, and the installer will handle the rest. The plug-in will be available inside Maya, when the installation finished. 8 | 9 | The installer performs the following steps: 10 | 11 | -# Extracts the Maya plug-in for all supported Maya versions. This includes the plug-in binary, and various scripts. Files for all the supported Maya versions are extracted, regardless of which Maya versions are actually installed on the system. The extracted files can be found under the `$HFS/engine/maya directory`. 12 | 13 | -# Registers the newly installed Maya plug-in with Maya. This step is only done for Maya versions that the installer is able to detect. 14 | 15 | Normally, the installer would perform the necessary steps to register the plug-in with with Maya, if Maya is installed locally in the default location. However, if another Maya version is later installed, or in a custom software deployment scenario, the plug-in may not be registered with Maya automatically. In these cases, extra steps may be needed to register the plug-in with Maya. 16 | 17 | The plug-in uses the Maya module description file to register with Maya. This also sets up the correct environment variables that are needed to run the plug-in. A common installation problem is that users would attempt to manually set the `MAYA_PLUG_IN_PATH` and `MAYA_SCRIPT_PATH` environment variables. However, this may not lead to a correct environment to run the plug-in. The module description file also contains additional information used to resolve library conflicts; without access to this information crashes may result. Using Maya's module description file is the recommended way to register the plug-in. 18 | 19 | # Copying the module description file to an existing module directory 20 | 21 | The module description file contains an absolute path to the plug-in files: 22 | 23 | $HFS/engine/maya/maya/houdiniEngine-maya 24 | 25 | and is designed to be copied to one of the directories that Maya searches, such as: 26 | 27 | $MAYA_LOCATION/modules 28 | 29 | To register the plug-in, for example on Windows, copy the file: 30 | 31 | C:\Program Files\Side Effects Software\Houdini 17.0.544\engine\maya\maya2018\houdiniEngine-maya2018 32 | 33 | into: 34 | 35 | C:\Program Files\Autodesk\Maya2018\modules 36 | 37 | Or for example on Linux, copy the file: 38 | 39 | /opt/hfs17.5.342/engine/maya/maya2018/houdiniEngine-maya2018 40 | 41 | into: 42 | 43 | /usr/autodesk/maya2018/modules 44 | 45 | This is exactly what the installer would do to register the plug-in with Maya. 46 | 47 | # Setting the MAYA_MODULE_PATH environment variable 48 | 49 | Setting the `MAYA_MODULE_PATH` environment variable is a more flexible way to register the plug-in. The module description file contains a relative path: 50 | 51 | $HFS/engine/maya/houdiniEngine-maya 52 | 53 | and is designed to be used with the `MAYA_MODULE_PATH` environment variable. For example, insert the following path into `MAYA_MODULE_PATH`: 54 | 55 | C:\Program Files\Side Effects Software\Houdini 17.0.544\engine\maya 56 | 57 | Or on Linux, you can add something like: 58 | 59 | /opt/hfs17.5.342/engine/maya 60 | 61 | Note that the path does not contain the `maya 7 | class OptionVarBase 8 | { 9 | public: 10 | OptionVarBase(const char *name, const VAL &defaultValue) 11 | : myDefaultValue(defaultValue) 12 | { 13 | myName = "houdiniEngine"; 14 | myName += name; 15 | 16 | bool exists = false; 17 | static_cast(*this).getImpl(exists); 18 | 19 | if (!exists) 20 | { 21 | MGlobal::setOptionVarValue(myName.c_str(), myDefaultValue); 22 | } 23 | } 24 | 25 | VAL get() const 26 | { 27 | bool exists = false; 28 | const VAL value = static_cast(*this).getImpl(exists); 29 | return exists ? value : myDefaultValue; 30 | } 31 | 32 | bool set(VAL value) const 33 | { 34 | return static_cast(*this).setImpl(value); 35 | } 36 | 37 | protected: 38 | std::string myName; 39 | const VAL myDefaultValue; 40 | 41 | private: 42 | OptionVarBase &operator=(const OptionVarBase &); 43 | }; 44 | 45 | class IntOptionVar : public OptionVarBase 46 | { 47 | public: 48 | typedef OptionVarBase Base; 49 | 50 | IntOptionVar(const char *name, int defaultValue) : Base(name, defaultValue) 51 | { 52 | } 53 | 54 | int getImpl(bool &exists) const 55 | { 56 | return MGlobal::optionVarIntValue(myName.c_str(), &exists); 57 | } 58 | 59 | bool setImpl(int value) const 60 | { 61 | return MGlobal::setOptionVarValue(myName.c_str(), value); 62 | } 63 | }; 64 | 65 | class StringOptionVar : public OptionVarBase 66 | { 67 | public: 68 | typedef OptionVarBase Base; 69 | 70 | StringOptionVar(const char *name, const char *defaultValue) 71 | : Base(name, defaultValue) 72 | { 73 | } 74 | 75 | MString getImpl(bool &exists) const 76 | { 77 | return MGlobal::optionVarStringValue(myName.c_str(), &exists); 78 | } 79 | 80 | bool setImpl(MString &value) const 81 | { 82 | return MGlobal::setOptionVarValue(myName.c_str(), value); 83 | } 84 | }; 85 | 86 | struct OptionVars 87 | { 88 | OptionVars() 89 | : hfsLocation("HfsLocation", ""), 90 | hapilLocation("HapilLocation", ""), 91 | asyncMode("AsynchronousMode", 1), 92 | sessionType("SessionType", 2), // named pipe 93 | thriftServer("ThriftServer", "localhost"), 94 | thriftPort("ThriftPort", 9090), 95 | sessionPipeCustom("SessionPipeCustom", 0), 96 | thriftPipe("ThriftPipe", "hapi"), 97 | unsetLLP("UnsetLLP", 1), 98 | unsetPP("UnsetPP", 0), 99 | viewProduct("ViewProduct", "Houdini Core"), 100 | timeout("Timeout", 10 * 1000), 101 | disableCooking("DisableCooking", 0) 102 | { 103 | } 104 | 105 | StringOptionVar hfsLocation; 106 | StringOptionVar hapilLocation; 107 | IntOptionVar asyncMode; 108 | IntOptionVar sessionType; 109 | StringOptionVar thriftServer; 110 | IntOptionVar thriftPort; 111 | IntOptionVar sessionPipeCustom; 112 | StringOptionVar thriftPipe; 113 | IntOptionVar unsetLLP; 114 | IntOptionVar unsetPP; 115 | StringOptionVar viewProduct; 116 | IntOptionVar timeout; 117 | IntOptionVar disableCooking; 118 | 119 | private: 120 | OptionVars &operator=(const OptionVars &); 121 | }; 122 | } 123 | 124 | #endif 125 | 126 | -------------------------------------------------------------------------------- /Asset.h: -------------------------------------------------------------------------------- 1 | #ifndef __Asset_h__ 2 | #define __Asset_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "AssetNodeOptions.h" 13 | #include "OutputObject.h" 14 | 15 | #include 16 | #include 17 | 18 | class Inputs; 19 | class OutputMaterial; 20 | class ParmNameCache; 21 | 22 | class Asset 23 | { 24 | public: 25 | Asset(const MString &otlFilePath, const MString &assetName); 26 | ~Asset(); 27 | 28 | bool isValid() const; 29 | 30 | MString getOTLFilePath() const; 31 | MString getAssetName() const; 32 | MString getAssetHelpText() const; 33 | MString getAssetHelpURL() const; 34 | 35 | HAPI_AssetInfo getAssetInfo() { return myAssetInfo; } 36 | HAPI_NodeInfo getNodeInfo() { return myNodeInfo; } 37 | MString getRelativePath(HAPI_NodeId id); 38 | 39 | void resetSimulation(); 40 | 41 | MString getCookMessages(); 42 | 43 | MTime getTime() const; 44 | 45 | void setTime(const MTime &mayaTime); 46 | 47 | void setInputs(const MPlug &plug, MDataBlock &data); 48 | 49 | MStatus compute(const MPlug &plug, 50 | MDataBlock &data, 51 | AssetNodeOptions::AccessorDataBlock &options, 52 | bool &needToSyncOutputs, 53 | const bool needToRecomputeOutputData); 54 | void computeMaterial(const MPlug &plug, 55 | MDataBlock &data, 56 | bool bakeTextures, 57 | bool &needToSyncOutputs); 58 | 59 | void fillParmNameCache(); 60 | void getParmValues(MDataBlock &dataBlock, 61 | const MFnDependencyNode &nodeFn, 62 | const std::vector *attrs); 63 | 64 | void setParmValues(MDataBlock &dataBlock, 65 | const MFnDependencyNode &nodeFn, 66 | const std::vector *attrs, 67 | bool checkMismatch); 68 | 69 | MString getAttrNameFromParm(const HAPI_ParmInfo &parmInfo) const; 70 | 71 | private: 72 | void update(); 73 | 74 | void computeInstancerObjects(const MPlug &plug, 75 | MDataBlock &data, 76 | MIntArray &instancedObjIds, 77 | MStringArray &instancedObjNames, 78 | AssetNodeOptions::AccessorDataBlock &options, 79 | bool &needToSyncOutputs, 80 | const bool needToRecomputeOutputData); 81 | void computeGeometryObjects(const MPlug &plug, 82 | MDataBlock &data, 83 | const MIntArray &instancedObjIds, 84 | const MStringArray &instancedObjNames, 85 | AssetNodeOptions::AccessorDataBlock &options, 86 | bool &needToSyncOutputs, 87 | const bool needToRecomputeOutputData); 88 | 89 | private: 90 | typedef std::vector OutputObjects; 91 | typedef std::vector OutputMaterials; 92 | 93 | MTime myTime; 94 | MString myAssetName; 95 | MString myAssetHelpText; 96 | MString myAssetHelpURL; 97 | 98 | HAPI_AssetInfo myAssetInfo; 99 | bool myIsObjSubnet; 100 | 101 | HAPI_NodeInfo myNodeInfo; 102 | 103 | Inputs *myAssetInputs; 104 | OutputObjects myObjects; // the OutputObject class contains a 1 to 1 map 105 | // with HAPI_ObjectInfos. 106 | 107 | OutputMaterials myMaterials; 108 | std::unique_ptr myParmNameCache; 109 | }; 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /AssetDraw2.h: -------------------------------------------------------------------------------- 1 | #ifndef __AssetDraw_h__ 2 | #define __AssetDraw_h__ 3 | 4 | #include 5 | 6 | #include "Asset.h" 7 | #include "OutputDeform.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class MFnPlugin; 15 | 16 | class AssetDrawTraits 17 | { 18 | public: 19 | AssetDrawTraits(); 20 | MObject inputNodeId; 21 | MObject shader; 22 | MObject output; 23 | MObject shaderfile; 24 | }; 25 | 26 | class AssetDraw : public MPxLocatorNode 27 | { 28 | public: 29 | static AssetDrawTraits theTraits; 30 | AssetDraw(); 31 | ~AssetDraw() override; 32 | 33 | static void *creator(); 34 | static MStatus initialize(); 35 | 36 | static MStatus initializePlugin( MFnPlugin &plugin, MObject obj ); 37 | static MStatus uninitializePlugin( MFnPlugin &plugin, MObject obj ); 38 | 39 | 40 | MStatus compute( const MPlug& plug, MDataBlock& data ) override; 41 | MStatus computeOutput(); 42 | 43 | void draw( M3dView & view, 44 | const MDagPath & path, 45 | M3dView::DisplayStyle style, 46 | M3dView::DisplayStatus status ) override{} 47 | 48 | bool isBounded() const override; 49 | MBoundingBox boundingBox() const override; 50 | 51 | MSelectionMask getShapeSelectionMask() const override; 52 | 53 | MStatus preEvaluation(const MDGContext& context, 54 | const MEvaluationNode& evaluationNode) override; 55 | 56 | SchedulingType schedulingType() const override {return SchedulingType::kParallel;} 57 | 58 | static MTypeId id; 59 | static MString drawDbClassification; 60 | static MString drawRegistrantId; 61 | 62 | OutputDeform myOutputDeform; 63 | 64 | private: 65 | 66 | }; 67 | 68 | // Viewport 2.0 override implementation 69 | class AssetDrawGeometryOverride : public MHWRender::MPxGeometryOverride 70 | { 71 | public: 72 | static MHWRender::MPxGeometryOverride* Creator(const MObject& obj) 73 | { 74 | return new AssetDrawGeometryOverride(obj); 75 | } 76 | 77 | ~AssetDrawGeometryOverride() override; 78 | 79 | MHWRender::DrawAPI supportedDrawAPIs() const override; 80 | 81 | bool hasUIDrawables() const override { return false; } 82 | 83 | void updateDG() override; 84 | bool isIndexingDirty(const MHWRender::MRenderItem &item) override; 85 | bool isStreamDirty(const MHWRender::MVertexBufferDescriptor &desc) override; 86 | 87 | void updateRenderItems(const MDagPath &path, MHWRender::MRenderItemList& list) override; 88 | void populateGeometry(const MHWRender::MGeometryRequirements &requirements, const MHWRender::MRenderItemList &renderItems, MHWRender::MGeometry &data) override; 89 | void cleanUp() override {}; 90 | 91 | bool traceCallSequence() const override 92 | { 93 | return false; // Return true if internal tracing is desired. 94 | } 95 | 96 | void handleTraceMessage( const MString &message ) const override 97 | { 98 | MGlobal::displayInfo("houdiniDrawGeometryOverride: " + message); 99 | fputs("houdiniDrawGeometryOverride: ",stderr); 100 | fputs(message.asChar(),stderr); 101 | fputs("\n",stderr); 102 | } 103 | 104 | void preDrawCallback(MDrawContext& ctx, const MRenderItemList& renderItemList, MShaderInstance *sh); 105 | 106 | private: 107 | AssetDrawGeometryOverride(const MObject& obj); 108 | 109 | MHWRender::MShaderInstance* getShader(const MDagPath& path, bool &newshader); 110 | void releaseShader(); 111 | 112 | MObject mLocatorNode; 113 | OutputDeform *myOutput; 114 | 115 | MObject myShaderNode; 116 | MString myShaderFile; 117 | MShaderInstance *myShaderInstance; 118 | std::vector myTextures; 119 | std::vector myTextureParms; 120 | size_t myLightCount; 121 | }; 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /doc/Maya_Particle.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Particle Particle 4 | 5 | @tableofcontents 6 | 7 | The plug-in supports both inputting and outputting particle. 8 | 9 | @section Maya_Particle_Input Input 10 | 11 | Maya particles are inputted into Houdini assets as points. Both Maya particle and `nParticle` nodes can be used for inputting. When inputting Maya particles, all of the custom Maya per-particle attributes are input as well. Since Maya only supports double and vector per-particle attributes, the input point attributes can only be `float` and `float3` point attributes. 12 | 13 | @section Maya_Particle_Output Output 14 | 15 | Points from a Houdini asset are output as an `nParticle` node. When points are output, the plug-in will output all Houdini point attributes as Maya per-particle attributes. Since Maya only supports float and vector per-particle attributes, `float` and `float3` point attributes are output into Maya particles. Since Maya particles does not support integer per-particle attributes, `int` point attributes would be converted and outputted as `float` attributes. Any other attributes, such as string attributes, are ignored. 16 | 17 | @section Maya_Particle_MappedAttributes Mapped Attributes 18 | 19 | Some standard per-particle attributes in Maya have different names in Houdini. These attributes are automatically mapped to the equivalent point attributes in Houdini. 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
MayaHoudini
position (vector)P (float3)
velocity (vector)v (float3)
acceleration (vector)force (float3)
rgbPP (vector)Cd (float3)
opacityPP (double)Alpha (float)
radiusPP (double)pscale (float)
finalLifespanPP (double)life (float)
particleId (double)id (float)
59 | 60 | Also note that while age is explicitly represented in Houdini, in Maya it is computed from birthTime and the current frame. If an asset has explicitly defined a birthTime attribute, we use for output, otherwise we compute birthTime from age. 61 | 62 | @section Maya_Particle_NucleusSolver Nucleus Solver 63 | 64 | When the asset creates an `nParticle` node for outputting points/particles, the `nParticle` node is assigned to the active Nucleus solver. This means that the output particles would then participate in any simulations that are assigned to the same Nucleus solver. This may or may not be the desired result. 65 | 66 | One example is a Houdini asset that takes some input points, removes some points, and outputs the resulting points. If a simulated `nParticle` is used as the input, the output `nParticle` will be assigned to the same Nucleus Solver as the input `nParticle`. Since the output particles have the exact same positions as the input particles, this input particles would collide with the output particles, which affects the motion of the input particles. In this case, to prevent the collisions, one of the `nParticle` nodes should be assigned to a different Nucleus solver. 67 | 68 | @section Maya_Particle_StartFrame Start Frame 69 | 70 | In order to create the output nParticle node, the Houdini asset needs to be synced at (or after) the Houdini particle's start frame. If you have loaded your asset before the start frame, you will not initally see any output particles until you have set the time to the start frame and resynced. 71 | 72 | The output nParticle's start frame needs to be set at (or before) the Houdini particle's start frame in order to display the particles. If your start frame is before the default Nucleus start frame, you should set the start frame on the Nucleus node to match. If this would conflict with other objects attached to the same Nucleus solver, the output nParticle should be assigned to a different Nucleus solver. 73 | 74 | */ 75 | -------------------------------------------------------------------------------- /doc/Maya_Debugging.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Debugging Debugging 4 | 5 | When using Houdini Engine, Houdini Engine is internally managing a Houdini scene. When a Houdini asset is instantiated inside Maya, Houdini Engine actually instantiates the Houdini asset inside the Houdini scene. When input geometries from Maya are connected to the Houdini asset, the Maya geometry is replicated into the Houdini scene. Asset parameters that are set in Maya are also mirrored in the Houdini scene. Finally, the geometries that the Houdini asset outputs from the Houdini scene are replicated back into Maya. So the internal Houdini scene is at the very core of how Houdini Engine works. 6 | 7 | The internals are normally hidden away from users. However, there are times when there is a need to investigate into why an asset isn't behaving as expected. So being able to inspect the internal Houdini scene can be very useful. This is especially true in cases where an asset is behaving correctly inside Houdini, but behaving correctly through Houdini Engine. 8 | 9 | @image html Maya_Debugging.png 10 | 11 | In the `Houdini Engine -> Debugging` menu, there are options to view, or save out, the internal Houdini scene. The internal Houdini scene can then be inspected in a separate Houdini UI session. This is extremely helpful when troubleshooting various Houdini Engine issues. This also means that inspecting the internal Houdini scene requires a license capable of running the Houdini UI. 12 | 13 | Another interesting use of this debugging feature is to see exactly what data and metadata is being passed into the Houdini scene when inputting geometry. For example, simply create a null asset and connect a Maya geometry to the asset. The exact data and metadata can then be seen from the Houdini scene. This is much quicker and reliable than checking the documentations. 14 | 15 | Another useful workflow is to author assets while viewing this internal Houdini scene. The benefit is that the replicated Maya geometry is immediately available in this Houdini scene. Asset authors can directly test how the Maya geometry would behave in the asset. The author can also have Houdini and Maya running at the same time. Then, the author can make changes to the asset in the Houdini UI, save the asset in the Houdini UI, and @ref Maya_Assets_Options_ReloadAsset "reload the asset" inside Maya to immediately see the changes. 16 | 17 | @section Maya_Debugging_Debugger Houdini Engine Debugger 18 | 19 | In order to use the Houdini Engine Debugger, Maya must connect directly to the debugger session. We do not have an auto-start option for the debugger, so the debugging session needs to be started independently. 20 | 21 | To use the Houdini Engine Debugger session: 22 | 23 | -# First start a Houdini Engine Debugger session in Houdini via **Window** > **Houdini Engine Debugger** and start one of session types. 24 | -# In Maya, you will either need to set the session option vars in the script editor before loading the plugin, or you can load the plugin normally, open the Houdini Preferences Window to set the port or pipe name for the Debugger session, and then unload and reload the plugin to start using the debugging session. 25 | 26 | e.g. if the debugger has a socket session, on port 9090, set: 27 | @verbatim 28 | optionVar -iv "houdiniEngineSessionType" 1; 29 | optionVar -iv "houdiniEngineThriftPort" 9090; 30 | @endverbatim 31 | 32 | If the debugger is using a named pipe called "hapi_debug_session", set: 33 | 34 | @verbatim 35 | optionVar -iv "houdiniEngineSessionType" 2; 36 | optionVar -sv "houdiniEngineThriftPipe" "hapi_debug_session"; 37 | optionVar -iv "houdiniEngineSessionPipeCustom" 1; 38 | @endverbatim 39 | 40 | Once connected, any asset instantiated in Maya will also be instantiated in the Houdini session. Parameter values and input geometry will also be reflected in the Houdini scene after cooking. If the user makes a change in Houdini (e.g. parameters, node state, network graph), in order to reflect that change in Maya, the asset definition must be saved in Houdini and reloaded in Maya. 41 | 42 | When you are finished with the debugger, reset your preferences back to your normal Back End settings for the next time that you load the plugin. 43 | 44 | See the 45 | @ref Maya_Session "Session" 46 | section for more detail on how to configure a socket or named pipe session. 47 | */ 48 | -------------------------------------------------------------------------------- /doc/Maya_Time.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Time Time 4 | 5 | @section Maya_Time_TimeAndFrame Time and Frame 6 | 7 | Whenever time changes in Maya, Houdini Engine is also updated with the new time. This means assets can use time, and be time-dependent. It's important to note that, in Houdini, "frame 1" is "0 seconds". However, in Maya, "frame 0" is "0 seconds". The plugin sets time such that Houdini Engine's frame number match with Maya's frame number. This means `$F` in Houdini will evaluate to the same frame number as Maya. However, this also means that, when compared to Maya, `$T` in Houdini will look to be shifted by one frame. 8 | 9 | Maya's unit of time (frames per second) is also passed into Houdini. This is important for assets that relies on time in seconds for evaluation, such as dynamic simulations. 10 | 11 | @section Maya_Time_TimeDependentInputs Time-dependent Inputs 12 | 13 | Assets can be time-dependent, input geometries to the asset can be animated, and asset parameters can also be animated. 14 | 15 | Houdini Engine is designed to use a push mechanism for passing input geometries and asset parameters from Maya to Houdini Engine. Before evaluating an asset through Houdini Engine at the current frame, the input geometries and asset parameters of the current frame are passed into Houdini Engine. And while an asset is evaluating, the asset is unable to request additional data from Maya. So the asset only has access to the current frame's data that. 16 | 17 | When evaluating an asset for the current frame, most assets only use input data from the current frame, so they will evaluate correctly under this push mechanism. However, in some more advanced assets, assets may contain nodes and expressions that need to access input geometries and asset parameters from other frames. From the point of view of these nodes and expressions, the input geometries and asset parameters from Maya would seem static for all frames, and these nodes and expressions won't produce the correct results. 18 | 19 | Some nodes that are likely to run into this limitation are easily identifiable: 20 | - `timeshift` SOP 21 | - `timewarp` SOP 22 | - `timeblend` SOP 23 | 24 | The nature of these nodes are to produce an output in the current frame, by using geometries from other frames. So it's quite clear that these nodes would have run into this limitation. However, there are some nodes that are slightly less obvious. For example, when using the `trail` SOP under "Compute Velocity" mode, the SOP would still require geometries from other frames to produce the correct output. 25 | 26 | Another less obvious example is using ROP nodes, like `rop_geometry` and `rop_alembic`, to output frame sequences. In order for ROP nodes to render out frame sequences, they also need to access input geometry from multiple frames. In this case, a potential workaround on the Maya side would be to use a script to iterate the frame range, and render out the geometry of the each frame one at a time. This would ensure the ROP node only writes out the frame that the input geometry is available for. 27 | 28 | It is also important to note that this limitation only applies to accessing time-dependent input from Maya. If the time-dependent data is generated from inside the asset, then nodes and expressions are still able to access the time-dependent data freely without limitation. 29 | 30 | @subsection Maya_Time_Cache_SOP_Workaround Workaround using a Cache SOP 31 | 32 | It's possible to workaround the issue by using a `Cache SOP`. A `Cache SOP` could be added in the asset to cache the input geometry at multiple frames. Note that the "Clear Cache When Change Upstream" option needs to be disabled for the caching to work correctly through Houdini Engine. 33 | 34 | The Maya frames also needs to be played sequentially in order for `Cache SOP` to output the correct geometry from other frames. Scrubbing the timeline could lead to incorrect results, since some frames may not be evaluated while scrubbing. 35 | 36 | We don't currently recommend setting the "Cache Any Frame" option since it does not seem to be working reliably from Maya. You can export the frame range to the hda parms instead to be able to control the range of frames to be cached from Maya. This will allow scrubbing frames to work after all the frames have been cached (by playing through animation once). 37 | 38 | Since the input geometry from Maya is cached in the asset, geometry updates may not be available to the rest of the asset until the cache is cleared. The "Clear Cache" button can be promoted to the asset, so that the cache can be cleared explicitly when needed. 39 | 40 | */ 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Houdini Engine for Maya 2 | Houdini Engine for Maya is a Maya plug-in that allows deep integration of 3 | Houdini technology into Autodesk Maya through the use of Houdini Engine. 4 | 5 | This plug-in brings Houdini's powerful and flexible procedural workflow into 6 | Maya through Houdini Digital Assets. Artists can interactively adjust the 7 | asset's parameters inside Maya, and use Maya geometries as an asset's inputs. 8 | Houdini's procedural engine will then "cook" the asset and the results will be 9 | available right inside Maya. 10 | 11 | The easiest way for artists to access the plug-in is to download the latest 12 | production build of Houdini or the [FREE Houdini Apprentice Learning 13 | Edition](https://www.sidefx.com/products/houdini-apprentice/) 14 | and install the Maya plug-in along with the Houdini interactive software. 15 | Houdini Digital Assets created in either Houdini or Apprentice can then be 16 | loaded into Maya through the plug-in. A growing library of Digital Assets for 17 | use in Maya will be available at the [Orbolt Smart 3D Asset 18 | Store](http://www.orbolt.com/maya). 19 | 20 | For more information: 21 | 22 | * [Houdini Engine for Maya](https://www.sidefx.com/products/houdini-engine/maya-plug-in/) 23 | * [Documentation](http://www.sidefx.com/docs/maya/) 24 | * [FAQ](https://www.sidefx.com/faq/houdini-engine-faq/) 25 | 26 | For support and reporting bugs: 27 | 28 | * [Houdini Engine for Maya forum](https://www.sidefx.com/forum/46/) 29 | * [Bug Submission](https://www.sidefx.com/bugs/submit/) 30 | 31 | ## Supported Maya versions 32 | Currently, the supported Maya versions are: 33 | 34 | * 2026 35 | * 2025 36 | * 2024 37 | * 2023 38 | * 2022 39 | 40 | The plug-in is supported on Linux, Windows, and Mac OS X. 41 | 42 | ## Compiling with CMake 43 | The Maya plugin uses CMake to compile on all platforms. 44 | 45 | To get started: 46 | 47 | * Fork this repository to your own Github account using the Fork button at the top. 48 | * Clone the forked repository onto your system. 49 | 50 | In order to compile and run the plug-in, both Houdini and Maya have to be 51 | installed. Houdini needs to be installed because the plug-in uses the Houdini 52 | Engine API, which is currently distributed together with Houdini. Since the 53 | Houdini Engine API could change between daily builds, the plug-in also needs to 54 | be compiled with a matching Houdini version. For now, always download and 55 | install the latest available daily build from www.sidefx.com 56 | 57 | The main CMake variables for configuration are: 58 | * `CMAKE_INSTALL_PREFIX` - Specifies the destination directory to create the module directory and files. 59 | * `HoudiniEngine_ROOT` - Specifies the directory of the Houdini installation. (e.g. `$HFS`) 60 | * `Maya_ROOT` - Specifies the directory of the Maya installation. (e.g. `$MAYA_LOCATION`) 61 | * `MAYA_VERSION` - Specifies the Maya version to compile for. (e.g. `2018`, `2017`, `2016.5`, `2016`,...). 62 | 63 | The CMake install step will create a module directory and file. 64 | 65 | For example, on Linux: 66 | ``` 67 | cd ~/HoudiniEngineForMaya 68 | mkdir build-maya2018 69 | cd build-maya2018 70 | cmake \ 71 | -DHoudiniEngine_ROOT=/opt/hfs16.0.633 \ 72 | -DMaya_ROOT=/usr/autodesk/maya2018 \ 73 | -DCMAKE_INSTALL_PREFIX=../install-maya2018 \ 74 | -DMAYA_VERSION=2018 \ 75 | .. 76 | make && make install 77 | ``` 78 | 79 | ## UI-dependent DSOs 80 | 81 | The Maya plug-in disables certain Houdini DSOs by setting the 82 | `HOUDINI_DSO_EXCLUDE_PATTERN` environment variable (see `CMakeLists.txt`). 83 | These Houdini DSOs depend on Houdini's UI libraries, namely: 84 | - `HoudiniUI` 85 | - `HoudiniAPPS1` 86 | - `HoudiniAPPS2` 87 | - `HoudiniAPPS3` 88 | 89 | These Houdini UI libraries use Qt, and thus conflict with Maya's Qt libraries. 90 | These Houdini DSOs are excluded to avoid pulling in the Houdini's UI 91 | dependencies. 92 | 93 | This same mechanism can be used to exclude your own UI-dependent DSO plugins. 94 | 95 | This should only be an issue when using the in-process backend. If the backend 96 | is loaded with out-of-process backends (named pipe or socket), then it should 97 | be possible to load the UI-dependent DSOs, and load in the UI libraries. 98 | 99 | The backend can be set through the Houdini Engine's preferences in Maya. The 100 | following MEL command could also be used to change the preference before 101 | loading the Maya plug-in: 102 | ```optionVar -iv "houdiniEngineSessionType" 2 -iv "houdiniEngineSessionPipeCustom" 0;``` 103 | -------------------------------------------------------------------------------- /InputCurve.C: -------------------------------------------------------------------------------- 1 | #include "InputCurve.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "util.h" 13 | 14 | InputCurve::InputCurve() : Input() 15 | { 16 | Util::PythonInterpreterLock pythonInterpreterLock; 17 | 18 | HAPI_NodeId nodeId; 19 | CHECK_HAPI(HAPI_CreateNode( 20 | Util::theHAPISession.get(), -1, "Sop/curve", NULL, false, &nodeId)); 21 | if (!Util::statusCheckLoop()) 22 | { 23 | DISPLAY_ERROR(MString("Unexpected error when creating input curve.")); 24 | } 25 | 26 | HAPI_GetNodeInfo(Util::theHAPISession.get(), nodeId, &myCurveNodeInfo); 27 | 28 | setTransformNodeId(myCurveNodeInfo.parentId); 29 | setGeometryNodeId(nodeId); 30 | } 31 | 32 | InputCurve::~InputCurve() 33 | { 34 | if (!Util::theHAPISession.get()) 35 | return; 36 | HAPI_DeleteNode(Util::theHAPISession.get(), geometryNodeId()); 37 | } 38 | 39 | InputCurve::AssetInputType 40 | InputCurve::assetInputType() const 41 | { 42 | return Input::AssetInputType_Curve; 43 | } 44 | 45 | void 46 | InputCurve::setInputGeo(MDataBlock &dataBlock, const MPlug &plug) 47 | { 48 | MDataHandle dataHandle = dataBlock.inputValue(plug); 49 | 50 | MObject curveObj = dataHandle.asNurbsCurve(); 51 | if (curveObj.isNull()) 52 | { 53 | return; 54 | } 55 | 56 | // find coords parm 57 | std::vector parms(myCurveNodeInfo.parmCount); 58 | HAPI_GetParameters(Util::theHAPISession.get(), myCurveNodeInfo.id, 59 | &parms[0], 0, myCurveNodeInfo.parmCount); 60 | int typeParmIndex = Util::findParm(parms, "type"); 61 | int coordsParmIndex = Util::findParm(parms, "coords"); 62 | int orderParmIndex = Util::findParm(parms, "order"); 63 | int closeParmIndex = Util::findParm(parms, "close"); 64 | if (coordsParmIndex < 0 || coordsParmIndex < 0 || orderParmIndex < 0 || 65 | closeParmIndex < 0) 66 | { 67 | return; 68 | } 69 | 70 | const HAPI_ParmInfo &typeParm = parms[typeParmIndex]; 71 | const HAPI_ParmInfo &coordsParm = parms[coordsParmIndex]; 72 | const HAPI_ParmInfo &orderParm = parms[orderParmIndex]; 73 | const HAPI_ParmInfo &closeParm = parms[closeParmIndex]; 74 | 75 | MFnNurbsCurve curveFn(curveObj); 76 | 77 | // type 78 | { 79 | const char *type = "nurbs"; 80 | if (curveObj.apiType() == MFn::kBezierCurveData) 81 | { 82 | type = "bezier"; 83 | } 84 | 85 | HAPI_ParmChoiceInfo *choices = 86 | new HAPI_ParmChoiceInfo[typeParm.choiceCount]; 87 | HAPI_GetParmChoiceLists(Util::theHAPISession.get(), myCurveNodeInfo.id, 88 | choices, typeParm.choiceIndex, 89 | typeParm.choiceCount); 90 | 91 | int nurbsIdx = -1; 92 | for (int i = 0; i < typeParm.choiceCount; i++) 93 | { 94 | if (Util::HAPIString(choices[i].valueSH) == type) 95 | { 96 | nurbsIdx = i; 97 | break; 98 | } 99 | } 100 | 101 | delete[] choices; 102 | 103 | if (nurbsIdx < 0) 104 | { 105 | return; 106 | } 107 | 108 | HAPI_SetParmIntValues(Util::theHAPISession.get(), myCurveNodeInfo.id, 109 | &nurbsIdx, typeParm.intValuesIndex, 1); 110 | } 111 | 112 | // coords 113 | { 114 | MPointArray cvs; 115 | curveFn.getCVs(cvs); 116 | 117 | // Maya has curveFn.degree() more cvs in it's data definition 118 | // than houdini for periodic curves--but they are conincident 119 | // with the first ones. Houdini ignores them, so we don't 120 | // output them. 121 | int num_houdini_cvs = cvs.length(); 122 | if (curveFn.form() == MFnNurbsCurve::kPeriodic) 123 | num_houdini_cvs -= curveFn.degree(); 124 | 125 | std::ostringstream coords; 126 | for (unsigned int i = 0; i < (unsigned int)num_houdini_cvs; i++) 127 | { 128 | const MPoint &pt = cvs[i]; 129 | 130 | coords << pt.x << "," << pt.y << "," << pt.z << " "; 131 | } 132 | HAPI_SetParmStringValue(Util::theHAPISession.get(), myCurveNodeInfo.id, 133 | coords.str().c_str(), coordsParm.id, 134 | coordsParm.stringValuesIndex); 135 | } 136 | 137 | // order 138 | { 139 | int order = curveFn.degree() + 1; 140 | 141 | HAPI_SetParmIntValues(Util::theHAPISession.get(), myCurveNodeInfo.id, 142 | &order, orderParm.intValuesIndex, 1); 143 | } 144 | 145 | // periodicity 146 | { 147 | int close = curveFn.form() == MFnNurbsCurve::kPeriodic; 148 | HAPI_SetParmIntValues(Util::theHAPISession.get(), myCurveNodeInfo.id, 149 | &close, closeParm.intValuesIndex, 1); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /OutputGeometryPart.h: -------------------------------------------------------------------------------- 1 | #ifndef __OutputGeometryPart_h__ 2 | #define __OutputGeometryPart_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class Asset; 12 | 13 | class OutputGeometryPart 14 | { 15 | public: 16 | OutputGeometryPart(HAPI_NodeId nodeId, HAPI_PartId partId); 17 | ~OutputGeometryPart(); 18 | 19 | bool needCompute(AssetNodeOptions::AccessorDataBlock &options) const; 20 | 21 | MStatus compute(const MTime &time, 22 | const MPlug &partPlug, 23 | MDataBlock &data, 24 | MDataHandle &handle, 25 | AssetNodeOptions::AccessorDataBlock &options, 26 | bool &needToSyncOutputs); 27 | 28 | protected: 29 | void update(); 30 | 31 | private: 32 | void computeMaterial(const MTime &time, 33 | const MPlug &materialPlug, 34 | MDataBlock &data, 35 | MDataHandle &materialHandle); 36 | 37 | void computeMesh(const MTime &time, 38 | const MPlug &hasMeshPlug, 39 | const MPlug &meshPlug, 40 | MDataBlock &data, 41 | MDataHandle &hasMeshHandle, 42 | MDataHandle &meshHandle, 43 | AssetNodeOptions::AccessorDataBlock &options); 44 | void computeParticle(const MTime &time, 45 | const MPlug &hasParticlePlug, 46 | const MPlug &particlePlug, 47 | MDataBlock &data, 48 | MDataHandle &hasParticlesHandle, 49 | MDataHandle &particleHandle, 50 | AssetNodeOptions::AccessorDataBlock &options); 51 | void computeCurves(const MTime &time, 52 | const MPlug &curvesPlug, 53 | const MPlug &curvesIsBezierPlug, 54 | MDataBlock &data, 55 | MDataHandle &curvesHandle, 56 | MDataHandle &curvesIsBezierHandle, 57 | AssetNodeOptions::AccessorDataBlock &options); 58 | void computeVolume(const MTime &time, 59 | const MPlug &volumePlug, 60 | MDataBlock &data, 61 | MDataHandle &volumeHandle, 62 | const bool preserveScale); 63 | void computeVolumeTransform(const MTime &time, 64 | MDataHandle &volumeTransformHandle, 65 | const bool preserveScale); 66 | void computeInstancer(const MTime &time, 67 | const MPlug &hasInstancerPlug, 68 | const MPlug &instancePlug, 69 | MDataBlock &data, 70 | MDataHandle &hasInstancerHandle, 71 | MDataHandle &instanceHandle, 72 | const bool preserveScale); 73 | void computeExtraAttributes(const MTime &time, 74 | const MPlug &extraAttributesPlug, 75 | MDataBlock &data, 76 | MDataHandle &extraAttributesHandle, 77 | AssetNodeOptions::AccessorDataBlock &options, 78 | bool &needToSyncOutputs); 79 | void computeGroups(const MTime &time, 80 | const MPlug &groupsPlug, 81 | MDataBlock &data, 82 | MDataHandle &groupsHandle, 83 | AssetNodeOptions::AccessorDataBlock &options, 84 | bool &needToSyncOutputs); 85 | 86 | template 87 | bool getAttributeData(std::vector &array, 88 | const char *name, 89 | HAPI_AttributeOwner owner); 90 | 91 | template 92 | bool convertParticleAttribute(T arrayDataFn, 93 | const char *houdiniName, 94 | bool preserveScale); 95 | 96 | bool computeExtraAttribute(const MPlug &extraAttributePlug, 97 | MDataBlock &data, 98 | MDataHandle &extraAttributeHandle, 99 | HAPI_AttributeOwner attributeOwner, 100 | const char *attributeName); 101 | 102 | void markAttributeUsed(const std::string &attributeName); 103 | bool isAttributeUsed(const std::string &attributeName); 104 | void clearAttributesUsed(); 105 | 106 | private: 107 | HAPI_NodeId myNodeId; 108 | HAPI_PartId myPartId; 109 | 110 | std::vector myAttributesUsed; 111 | 112 | HAPI_GeoInfo myGeoInfo; 113 | HAPI_PartInfo myPartInfo; 114 | HAPI_VolumeInfo myVolumeInfo; 115 | HAPI_CurveInfo myCurveInfo; 116 | 117 | bool myLastOutputGeometryGroups; 118 | bool myLastOutputCustomAttributes; 119 | }; 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /scripts/houdiniEngineDeleteHistory.mel: -------------------------------------------------------------------------------- 1 | 2 | proc int 3 | only_geo_upstream(string $dstNode) { 4 | // is there non-trivial mesh history upstream, or just a copy of the mesh? 5 | // and set/group membership 6 | string $historyInput = ""; 7 | if(`nodeType $dstNode` == "mesh") { 8 | $historyInput = ($dstNode + ".inMesh"); 9 | } else if(`nodeType $dstNode` == "groupParts") { 10 | $historyInput = ($dstNode + ".inputGeometry"); 11 | } else { 12 | return 0; 13 | } 14 | string $srcNodes[] = `listConnections -sh true -d false $historyInput`; 15 | if(size($srcNodes) > 0) 16 | return(only_geo_upstream($srcNodes[0])); 17 | else 18 | return 1; 19 | } 20 | 21 | global proc string[] 22 | get_upstream_assets(string $node) 23 | { 24 | // find any immediate upstream asset nodes 25 | // 26 | string $result[]; 27 | string $srcNodes[] = `listConnections -d false $node`; 28 | for ($srcNode in $srcNodes) { 29 | if(`nodeType $srcNode` == "houdiniAsset") { 30 | $result[size($result)] = $srcNode; 31 | } 32 | else { 33 | string $assets[] = get_upstream_assets($srcNode); 34 | appendStringArray($result, $assets, size($assets)); 35 | } 36 | } 37 | return $result; 38 | } 39 | 40 | global proc string[] 41 | delete_all_upstream_assets(string $node) 42 | { 43 | // find any immediate upstream asset nodes 44 | // 45 | string $result[]; 46 | string $srcNodes[] = get_upstream_assets($node); 47 | for ($srcNode in $srcNodes) { 48 | $result[size($result)] = $srcNode; 49 | string $assets[] = delete_all_upstream_assets($srcNode); 50 | appendStringArray($result, $assets, size($assets)); 51 | // should also do: get_all_upstream_geo and delete 52 | delete $srcNode; 53 | } 54 | return $result; 55 | } 56 | 57 | global proc int 58 | houdiniEngine_deleteHistory(string $node) 59 | { 60 | /// when invoked from the menu, we're already called with a shape, 61 | // but in general, better to check if it is a shape itself first 62 | string $objects[]; 63 | if(`objectType -isAType "shape" $node`) 64 | $objects[0] = $node; 65 | else 66 | $objects = `listRelatives -fullPath -shapes -ni $node`; 67 | // validate to get the meshes and curves 68 | // warn if any particles volumes or instancers were selected 69 | 70 | string $object = $objects[0]; 71 | 72 | string $upstreamAssets[] = get_upstream_assets($object); 73 | 74 | if( `nodeType $object` != "mesh" 75 | && `nodeType $object` != "nurbsCurve" 76 | && `nodeType $object` != "bezierCurve") { 77 | print("Error: can only delete asset history for curves or meshes. For particles and Volumes, export cache and reimport to maya object, for instancers, utrn off instancing option to get output geometry\n"); 78 | return 0; 79 | } 80 | 81 | // find the upstream assets 82 | // then delete history on the target objects 83 | // unparent all the stuff under the assets (shouldn't be any for the history asset) 84 | 85 | // For a non-history asset, deleteing history for one output object 86 | // deletes it for all the objects parented under the asset, then deletes the asset 87 | 88 | string $assetNode = $upstreamAssets[0]; 89 | if($assetNode == "") { 90 | print "Error: the selected node does not appear to have asset history: use the regular Delete History.\n"; 91 | return 0; 92 | } 93 | 94 | // in 2019, if we do the delete history with the evaluationManager on 95 | // it crashes in some EM operations on idle, so we go back to DG mode 96 | string $prevEMMode = "off"; 97 | if(`about -apiVersion` >= 20190000) { 98 | string $emMode[] = `evaluationManager -q -mode`; 99 | $prevEMMode = $emMode[0]; 100 | evaluationManager -mode "off"; 101 | } 102 | 103 | // unparent children of asset (history asset should not have any) 104 | string $children[] = `listRelatives -c -f $assetNode`; 105 | for($child in $children) { 106 | string $allDescendants[] = `listRelatives -ad -f $child`; 107 | int $hasShape = 0; 108 | for($desc in $allDescendants) { 109 | if(`objectType -isAType "shape" $desc`) { 110 | delete -ch $desc; 111 | $hasShape = 1; 112 | break; 113 | } 114 | if(`objectType -isAType "instancer" $desc`) { 115 | $hasShape = 1; 116 | break; 117 | } 118 | } 119 | if($hasShape) { 120 | delete -ch $child; 121 | if( `getAttr ($assetNode + ".ungroupOnBake")`) 122 | ungroup -a -w $child; 123 | else 124 | parent -a -w $child; 125 | } 126 | else 127 | delete $child; 128 | } 129 | 130 | // if it was a history asset 131 | if(size($children) == 0) 132 | delete -ch $object; 133 | delete_all_upstream_assets($assetNode); 134 | delete $assetNode; 135 | if( $prevEMMode != "off") { 136 | evaluationManager -mode $prevEMMode; 137 | } 138 | 139 | return 0; 140 | } 141 | 142 | 143 | -------------------------------------------------------------------------------- /cmake/Modules/FindMaya.cmake: -------------------------------------------------------------------------------- 1 | # Output variables: 2 | # Maya_FOUND 3 | # Maya_INCLUDE_DIRS 4 | # Maya_LIBRARIES 5 | # 6 | # Hints for finding package: 7 | # Maya_ROOT 8 | # Maya_INCLUDEDIR 9 | # Maya_LIBRARYDIR 10 | 11 | # All the Maya libraries link with Foundation. 12 | set( 13 | _maya_libraries 14 | Foundation 15 | ${Maya_FIND_COMPONENTS} 16 | ) 17 | 18 | if ( NOT Maya_FIND_VERSION ) 19 | message( FATAL_ERROR "Maya version is not specified." ) 20 | endif () 21 | 22 | ######################################## 23 | # Determine search directories 24 | ######################################## 25 | # Maya_INCLUDEDIR 26 | if ( DEFINED Maya_INCLUDEDIR ) 27 | list( APPEND _maya_include_search_dirs "${Maya_INCLUDEDIR}" ) 28 | endif () 29 | 30 | # Maya_LIBRARYDIR 31 | if ( DEFINED Maya_LIBRARYDIR ) 32 | list( APPEND _maya_library_search_dirs "${Maya_LIBRARYDIR}" ) 33 | endif () 34 | 35 | # Maya_ROOT 36 | if ( DEFINED Maya_ROOT ) 37 | set( _maya_root ${Maya_ROOT} ) 38 | else () 39 | if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" ) 40 | if ( ${Maya_FIND_VERSION} LESS 2016 ) 41 | set( _maya_root "/usr/autodesk/maya${Maya_FIND_VERSION}-x64" ) 42 | else () 43 | set( _maya_root "/usr/autodesk/maya${Maya_FIND_VERSION}" ) 44 | endif () 45 | elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) 46 | set( _maya_root "C:/Program\ Files/Autodesk/Maya${Maya_FIND_VERSION}" ) 47 | elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) 48 | set( _maya_root "/Applications/Autodesk/maya${Maya_FIND_VERSION}" ) 49 | endif () 50 | endif () 51 | if ( _maya_root ) 52 | if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" ) 53 | list( APPEND _maya_include_search_dirs "${_maya_root}/include" ) 54 | list( APPEND _maya_library_search_dirs "${_maya_root}/lib" ) 55 | elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) 56 | list( APPEND _maya_include_search_dirs "${_maya_root}/include" ) 57 | list( APPEND _maya_library_search_dirs "${_maya_root}/lib" ) 58 | elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) 59 | if ( ${Maya_FIND_VERSION} EQUAL 2016.5 ) 60 | list( APPEND _maya_include_search_dirs "${_maya_root}/include" ) 61 | else () 62 | list( APPEND _maya_include_search_dirs "${_maya_root}/devkit/include" ) 63 | endif () 64 | list( APPEND _maya_library_search_dirs "${_maya_root}/Maya.app/Contents/MacOS" ) 65 | endif () 66 | endif () 67 | 68 | ######################################## 69 | # Find 70 | ######################################## 71 | find_path( 72 | Maya_INCLUDE_DIRS 73 | maya/MFn.h 74 | HINTS ${_maya_include_search_dirs} 75 | ) 76 | list( APPEND _maya_required_vars Maya_INCLUDE_DIRS ) 77 | 78 | foreach ( lib ${_maya_libraries} ) 79 | find_library( 80 | Maya_${lib} 81 | NAMES ${lib} 82 | HINTS ${_maya_library_search_dirs} 83 | NO_DEFAULT_PATH 84 | ) 85 | list( APPEND _maya_required_vars Maya_${lib} ) 86 | list( APPEND Maya_LIBRARIES ${Maya_${lib}} ) 87 | endforeach () 88 | 89 | ######################################## 90 | # Create library targets 91 | ######################################## 92 | set( 93 | _maya_compile_definitions 94 | _BOOL 95 | REQUIRE_IOSTREAM 96 | ) 97 | 98 | if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) 99 | list( 100 | APPEND _maya_compile_definitions 101 | OSMac_ 102 | OSMac_MachO_ 103 | ) 104 | endif () 105 | 106 | foreach ( lib ${_maya_libraries} ) 107 | # SHARED so that IMPORTED_NO_SONAME works 108 | add_library( ${lib} SHARED IMPORTED ) 109 | 110 | if ( NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) 111 | set_target_properties( 112 | ${lib} 113 | PROPERTIES 114 | IMPORTED_LOCATION "${Maya_${lib}}" 115 | # Maya 2015 and older libraries do not have SONAME on Linux. This 116 | # would cause cmake to pass in absolute paths to the linker, which 117 | # we don't want. 118 | IMPORTED_NO_SONAME TRUE 119 | ) 120 | else () 121 | # IMPORTED_IMPLIB is used on Windows. 122 | set_target_properties( 123 | ${lib} 124 | PROPERTIES 125 | IMPORTED_IMPLIB "${Maya_${lib}}" 126 | ) 127 | endif () 128 | 129 | set_target_properties( 130 | ${lib} 131 | PROPERTIES 132 | INTERFACE_COMPILE_DEFINITIONS "${_maya_compile_definitions}" 133 | INTERFACE_INCLUDE_DIRECTORIES "${Maya_INCLUDE_DIRS}" 134 | ) 135 | 136 | # All the Maya libraries link with Foundation. 137 | if ( NOT ${lib} EQUAL "Foundation" ) 138 | set_target_properties( 139 | ${lib} 140 | PROPERTIES 141 | INTERFACE_LINK_LIBRARIES "${Maya_Foundation}" 142 | ) 143 | endif () 144 | endforeach () 145 | 146 | ######################################## 147 | # Handle standard arguments 148 | ######################################## 149 | include( FindPackageHandleStandardArgs ) 150 | find_package_handle_standard_args( 151 | Maya 152 | DEFAULT_MSG 153 | ${_maya_required_vars} 154 | ) 155 | -------------------------------------------------------------------------------- /AssetSubCommandLoadAsset.C: -------------------------------------------------------------------------------- 1 | #include "AssetSubCommandLoadAsset.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "AssetNode.h" 8 | #include "AssetSubCommandSync.h" 9 | #include "util.h" 10 | 11 | AssetSubCommandLoadAsset::AssetSubCommandLoadAsset(const MString &otlFilePath, 12 | const MString &assetName) 13 | : myOTLFilePath(otlFilePath), 14 | myAssetName(assetName), 15 | myAssetSubCommandSync(NULL) 16 | { 17 | } 18 | 19 | AssetSubCommandLoadAsset::~AssetSubCommandLoadAsset() {} 20 | 21 | MStatus 22 | AssetSubCommandLoadAsset::doIt() 23 | { 24 | MStatus status; 25 | 26 | // create houdiniAsset node 27 | MObject assetNodeObj = myDagModifier.createNode( 28 | AssetNode::typeId, MObject::kNullObj, &status); 29 | CHECK_MSTATUS_AND_RETURN_IT(status); 30 | 31 | // rename houdiniAsset node 32 | { 33 | int slashPosition = myAssetName.index('/'); 34 | MString nodeName; 35 | if (slashPosition >= 0) 36 | { 37 | const char *tempName = myAssetName.asChar(); 38 | // keep the part that's before the node table name 39 | // i.e. namespace 40 | for (int i = slashPosition; i-- > 0;) 41 | { 42 | if (!(('A' <= tempName[i] && tempName[i] <= 'Z') || 43 | ('a' <= tempName[i] && tempName[i] <= 'z'))) 44 | { 45 | nodeName += myAssetName.substring(0, i); 46 | break; 47 | } 48 | } 49 | 50 | // keep everything that's after the slash 51 | nodeName += myAssetName.substring( 52 | slashPosition + 1, myAssetName.length() - 1) + 53 | "1"; 54 | } 55 | else 56 | { 57 | // this is probably an invalid node type name 58 | nodeName = myAssetName; 59 | } 60 | nodeName = Util::sanitizeStringForNodeName(nodeName); 61 | status = myDagModifier.renameNode(assetNodeObj, nodeName); 62 | CHECK_MSTATUS_AND_RETURN_IT(status); 63 | } 64 | 65 | // set otl file attribute 66 | { 67 | MPlug plug(assetNodeObj, AssetNode::otlFilePath); 68 | status = myDagModifier.newPlugValueString(plug, myOTLFilePath); 69 | CHECK_MSTATUS_AND_RETURN_IT(status); 70 | } 71 | 72 | // set asset name attribute 73 | { 74 | MPlug plug(assetNodeObj, AssetNode::assetName); 75 | status = myDagModifier.newPlugValueString(plug, myAssetName); 76 | CHECK_MSTATUS_AND_RETURN_IT(status); 77 | } 78 | 79 | // time1.outTime -> houdiniAsset.inTime 80 | { 81 | MObject srcNode = Util::findNodeByName("time1"); 82 | MPlug srcPlug = MFnDependencyNode(srcNode).findPlug("outTime", true); 83 | MPlug dstPlug(assetNodeObj, AssetNode::inTime); 84 | 85 | status = myDagModifier.connect(srcPlug, dstPlug); 86 | CHECK_MSTATUS_AND_RETURN_IT(status); 87 | } 88 | 89 | // cannot simply call redoIt, because when we use AssetSubCommandSync, we 90 | // need to distinguish between doIt and redoIt 91 | status = myDagModifier.doIt(); 92 | CHECK_MSTATUS_AND_RETURN_IT(status); 93 | 94 | MFnTransform assetNodeFn(assetNodeObj); 95 | 96 | // The asset should have been instantiated by now. If we couldn't 97 | // instantiate the asset, then don't operate on the asset any further. This 98 | // avoids generating repeated errors. 99 | { 100 | AssetNode *assetNode = 101 | dynamic_cast(assetNodeFn.userNode()); 102 | if (!assetNode->getAsset()) 103 | { 104 | // If we couldn't instantiate the asset, then an error message 105 | // should have displayed already. No need to display error here. 106 | status = MStatus::kFailure; 107 | } 108 | } 109 | 110 | // Only attempt to sync if the node is valid 111 | if (!MFAIL(status)) 112 | { 113 | myAssetSubCommandSync = new AssetSubCommandSync(assetNodeObj); 114 | myAssetSubCommandSync->doIt(); 115 | } 116 | 117 | // select the node 118 | MGlobal::select(assetNodeObj); 119 | 120 | // set result 121 | MPxCommand::setResult(assetNodeFn.partialPathName()); 122 | 123 | return MStatus::kSuccess; 124 | } 125 | 126 | MStatus 127 | AssetSubCommandLoadAsset::redoIt() 128 | { 129 | MStatus status; 130 | 131 | status = myDagModifier.doIt(); 132 | CHECK_MSTATUS_AND_RETURN_IT(status); 133 | 134 | if (myAssetSubCommandSync) 135 | { 136 | status = myAssetSubCommandSync->redoIt(); 137 | CHECK_MSTATUS_AND_RETURN_IT(status); 138 | } 139 | 140 | return MStatus::kSuccess; 141 | } 142 | 143 | MStatus 144 | AssetSubCommandLoadAsset::undoIt() 145 | { 146 | MStatus status; 147 | 148 | if (myAssetSubCommandSync) 149 | { 150 | status = myAssetSubCommandSync->undoIt(); 151 | CHECK_MSTATUS_AND_RETURN_IT(status); 152 | } 153 | 154 | status = myDagModifier.undoIt(); 155 | CHECK_MSTATUS_AND_RETURN_IT(status); 156 | 157 | return MStatus::kSuccess; 158 | } 159 | 160 | bool 161 | AssetSubCommandLoadAsset::isUndoable() const 162 | { 163 | return true; 164 | } 165 | -------------------------------------------------------------------------------- /doc/Maya_History.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_History History 4 | 5 | @tableofcontents 6 | 7 | Certain assets can now be connected directly as Maya Construction History. Typically a SOP asset fits well as mesh construction history, and an Object asset, with a more complex collection of possible outputs works better with the classic asset structure. But any asset with at least one geo input and one geo output can be applied as construction history 8 | 9 | @section Maya_History_constructionHistory Assets as Maya Construction History 10 | 11 | The history asset differs from a classic asset in the way that is gets connected to maya objects. We make certain simpliflying assumptions 12 | 13 | - the asset node is connected upstream from the target maya geo - no new shapes are created 14 | - history assets cah be chained together directly or intermixed with native mayua history nodes 15 | - history assets can be applied to a mesh or to component selection on a mesh 16 | 17 | We make certain simpliflying assumptions in order for this to work - if your asset does not meet these criteria it should continue to be used as a classic asset 18 | - the exsisting mesh history will be connected to the first input 19 | - the first output will be connected to the existing mesh shape, all other outputs will be ignored 20 | - additional inputs can be connected to the history asset, however we are only interested in their effect on the first output. 21 | - if the target mesh has no construction history, we push a copy of the target mesh shape upstream to anchor the history chain 22 | - if there is face or vertex component selection, we create a group of the appropriate type on the input geo (named inputPrimitiveComponent or inputPointComponent), however the asset must use the named component group for it to have any effect 23 | - history assets should never be sync'd - their output needs to stay connected to the original target shape, If your asset has more than one output, it should be applied as a classic asset. 24 | 25 | Here are some additional menu items for manipulation of history assets: 26 | 27 | - @ref Maya_History_AddAsset "addAssetToHistory" 28 | - @ref Maya_History_RemoveAsset "removeAssetFromHistory" 29 | - @ref Maya_History_DeleteHistory "deleteHistory" 30 | - @ref Maya_History_ShowHide "hideAssetNodes" 31 | 32 | The houdiniAsset node outputs both the information needed for Sync to create the output maya geometry shapes, and the maya geometry data to connerct to those nodes once they are created. 33 | 34 | @section Maya_History_AddAsset Add Asset To History 35 | 36 | Applies a selected existing asset node as history to the selected mesh 37 | 38 | @image html Maya_History_AssetNodes.png 39 | 40 | The corresponding mel command, to add an asset as history to the selected mesh is: 41 | 42 | @verbatim 43 | houdiniEngine_addHistory($assetNode) 44 | @endverbatim 45 | 46 | @section Maya_History_RemoveAsset Remove Asset From History 47 | 48 | When a native maya history node is deleted, maya will reconnect the upstream and downstream history across the gap. Because Maya does not recognize plugin nodes as history, we provide this command to remove an asset node and and its associated inputGeometry nodes , and reconnect history across the gap. 49 | 50 | The corresponding mel command is: 51 | 52 | @verbatim 53 | houdiniEngine_removeHistory($assetNode); 54 | @endverbatim 55 | 56 | @section Maya_History_DeleteHistory Delete History 57 | 58 | When the native Maya delete history command is used, Maya will only delete history as far back as the nearest upstream asset node, since it recognize plugin nodes as history. We provide this command to delete construction history including any houdinAsset nodes, and their history. 59 | 60 | The corresponding mel command is: 61 | 62 | @verbatim 63 | houdiniEngine_deleteHistory($meshShape); 64 | @endverbatim 65 | 66 | @section Maya_History_ShowHide Show/Hide Assets In Outliner 67 | 68 | While history asset nodes are still derived from transform nodes, we are not parenting any outputs under them, so it may not be useful to see them in the outliner. Under the Show/Hide submenu are 3 options 69 | - Hide all Assets in Outliner 70 | - Hide history Assets in Outliner 71 | - Show All Assets in Outliner 72 | 73 | These menu items set the hiddenInOutliner attribute on the asset node to control how it is displayed 74 | 75 | The corresponding mel command is: 76 | 77 | @verbatim 78 | houdiniEngine_showOutliner(int $show, int $history) 79 | @endverbatim 80 | 81 | @section Maya_History_ShelfAssets Apply Assets from the Shelf 82 | 83 | It's convenient to have shelf buttons to apply your favorite houdini assests directly to meshes. Shelf buttons make this easy, but we can't assume that the plugin is already loaded when that first shelf button is pressed. Here's an example shelf button that will load the plugin and source some UI files before attempting to load my exampl extrude asset and connect it. This currently (17.0 Beta) assumes that the asset may located somewhere in $HOUDINI_MAYA_ASSET_PATH, as well as your default asset locations, however this may be subject to change depending on Beta beedback 84 | 85 | @verbatim 86 | if(!`pluginInfo -query -loaded houdiniEngine`) 87 | loadPlugin houdiniEngine; 88 | if(!`exists houdiniEngine_loadAndAddHistory`) 89 | source houdiniEngineHistoryAsset; 90 | if(!`exists houdiniEngine_findAsset`) 91 | source houdiniEngineUtils; 92 | $assetNode = `houdiniEngine_loadAndAddHistory "extrude.hda" "Object/extrude"`; 93 | @endverbatim 94 | 95 | 96 | */ 97 | -------------------------------------------------------------------------------- /cmake/PluginFiles.cmake: -------------------------------------------------------------------------------- 1 | set(MAYA_PLUGIN_SOURCES 2 | ${MAYA_SOURCE_DIRECTORY}/Asset.C 3 | ${MAYA_SOURCE_DIRECTORY}/AssetCommand.C 4 | ${MAYA_SOURCE_DIRECTORY}/AssetNode.C 5 | ${MAYA_SOURCE_DIRECTORY}/AssetNodeOptions.C 6 | ${MAYA_SOURCE_DIRECTORY}/AssetSubCommandLoadAsset.C 7 | ${MAYA_SOURCE_DIRECTORY}/AssetSubCommandSync.C 8 | ${MAYA_SOURCE_DIRECTORY}/EngineCommand.C 9 | ${MAYA_SOURCE_DIRECTORY}/FluidGridConvert.C 10 | ${MAYA_SOURCE_DIRECTORY}/HoudiniApi.C 11 | ${MAYA_SOURCE_DIRECTORY}/Input.C 12 | ${MAYA_SOURCE_DIRECTORY}/InputCurve.C 13 | ${MAYA_SOURCE_DIRECTORY}/InputCurveNode.C 14 | ${MAYA_SOURCE_DIRECTORY}/InputGeometryNode.C 15 | ${MAYA_SOURCE_DIRECTORY}/InputMergeNode.C 16 | ${MAYA_SOURCE_DIRECTORY}/InputMesh.C 17 | ${MAYA_SOURCE_DIRECTORY}/InputParticle.C 18 | ${MAYA_SOURCE_DIRECTORY}/InputTransformNode.C 19 | ${MAYA_SOURCE_DIRECTORY}/OutputGeometry.C 20 | ${MAYA_SOURCE_DIRECTORY}/OutputGeometryObject.C 21 | ${MAYA_SOURCE_DIRECTORY}/OutputGeometryPart.C 22 | ${MAYA_SOURCE_DIRECTORY}/OutputInstancerObject.C 23 | ${MAYA_SOURCE_DIRECTORY}/OutputMaterial.C 24 | ${MAYA_SOURCE_DIRECTORY}/OutputObject.C 25 | ${MAYA_SOURCE_DIRECTORY}/Platform.C 26 | ${MAYA_SOURCE_DIRECTORY}/plugin.C 27 | ${MAYA_SOURCE_DIRECTORY}/SubCommand.C 28 | ${MAYA_SOURCE_DIRECTORY}/SyncAttribute.C 29 | ${MAYA_SOURCE_DIRECTORY}/SyncOutputGeometryPart.C 30 | ${MAYA_SOURCE_DIRECTORY}/SyncOutputInstance.C 31 | ${MAYA_SOURCE_DIRECTORY}/SyncOutputMaterial.C 32 | ${MAYA_SOURCE_DIRECTORY}/SyncOutputObject.C 33 | ${MAYA_SOURCE_DIRECTORY}/util.C 34 | ) 35 | 36 | set(MAYA_PLUGIN_HEADERS 37 | ${MAYA_SOURCE_DIRECTORY}/Asset.h 38 | ${MAYA_SOURCE_DIRECTORY}/AssetCommand.h 39 | ${MAYA_SOURCE_DIRECTORY}/AssetNode.h 40 | ${MAYA_SOURCE_DIRECTORY}/AssetNodeOptions.h 41 | ${MAYA_SOURCE_DIRECTORY}/AssetSubCommandLoadAsset.h 42 | ${MAYA_SOURCE_DIRECTORY}/AssetSubCommandSync.h 43 | ${MAYA_SOURCE_DIRECTORY}/EngineCommand.h 44 | ${MAYA_SOURCE_DIRECTORY}/FluidGridConvert.h 45 | ${MAYA_SOURCE_DIRECTORY}/hapiutil.h 46 | ${MAYA_SOURCE_DIRECTORY}/HoudiniApi.h 47 | ${MAYA_SOURCE_DIRECTORY}/Input.h 48 | ${MAYA_SOURCE_DIRECTORY}/InputCurve.h 49 | ${MAYA_SOURCE_DIRECTORY}/InputCurveNode.h 50 | ${MAYA_SOURCE_DIRECTORY}/InputGeometryNode.h 51 | ${MAYA_SOURCE_DIRECTORY}/InputMergeNode.h 52 | ${MAYA_SOURCE_DIRECTORY}/InputMesh.h 53 | ${MAYA_SOURCE_DIRECTORY}/InputParticle.h 54 | ${MAYA_SOURCE_DIRECTORY}/InputTransformNode.h 55 | ${MAYA_SOURCE_DIRECTORY}/MayaTypeID.h 56 | ${MAYA_SOURCE_DIRECTORY}/NodeOptions.h 57 | ${MAYA_SOURCE_DIRECTORY}/OptionVars.h 58 | ${MAYA_SOURCE_DIRECTORY}/OutputGeometry.h 59 | ${MAYA_SOURCE_DIRECTORY}/OutputGeometryObject.h 60 | ${MAYA_SOURCE_DIRECTORY}/OutputGeometryPart.h 61 | ${MAYA_SOURCE_DIRECTORY}/OutputInstancerObject.h 62 | ${MAYA_SOURCE_DIRECTORY}/OutputMaterial.h 63 | ${MAYA_SOURCE_DIRECTORY}/OutputObject.h 64 | ${MAYA_SOURCE_DIRECTORY}/Platform.h 65 | ${MAYA_SOURCE_DIRECTORY}/SubCommand.h 66 | ${MAYA_SOURCE_DIRECTORY}/SyncAttribute.h 67 | ${MAYA_SOURCE_DIRECTORY}/SyncOutputGeometryPart.h 68 | ${MAYA_SOURCE_DIRECTORY}/SyncOutputInstance.h 69 | ${MAYA_SOURCE_DIRECTORY}/SyncOutputMaterial.h 70 | ${MAYA_SOURCE_DIRECTORY}/SyncOutputObject.h 71 | ${MAYA_SOURCE_DIRECTORY}/traits.h 72 | ${MAYA_SOURCE_DIRECTORY}/types.h 73 | ${MAYA_SOURCE_DIRECTORY}/util.h 74 | ) 75 | 76 | set(MAYA_PLUGIN_SCRIPTS 77 | ${MAYA_SCRIPT_DIRECTORY}/AEhoudiniAssetTemplate.mel 78 | ${MAYA_SCRIPT_DIRECTORY}/AEhoudiniInputCurveTemplate.mel 79 | ${MAYA_SCRIPT_DIRECTORY}/AEhoudiniInputGeometryTemplate.mel 80 | ${MAYA_SCRIPT_DIRECTORY}/AEhoudiniInputMergeTemplate.mel 81 | ${MAYA_SCRIPT_DIRECTORY}/AEhoudiniInputTransformTemplate.mel 82 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineAssetInput.mel 83 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineAssetLibraryManager.mel 84 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineAssetOptions.mel 85 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineAssetSync.mel 86 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineBakeAsset.mel 87 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineCreateUI.mel 88 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineDeleteHistory.mel 89 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineDeleteUI.mel 90 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineFreezeAsset.mel 91 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineHistoryAsset.mel 92 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEnginePreferences.mel 93 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineRemoveHistory.mel 94 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineUtils.mel 95 | ${MAYA_SCRIPT_DIRECTORY}/houdiniEngineUtils.py 96 | ${MAYA_SCRIPT_DIRECTORY}/shelf_Houdini.mel 97 | ${MAYA_SCRIPT_DIRECTORY}/shelf_HoudiniTools.mel 98 | ${MAYA_SCRIPT_DIRECTORY}/houdini_engine_for_maya/__init__.py 99 | ${MAYA_SCRIPT_DIRECTORY}/houdini_engine_for_maya/asset_store.py 100 | ) 101 | 102 | set(MAYA_PLUGIN_ICONS 103 | ${MAYA_ICON_DIRECTORY}/he_boolean.png 104 | ${MAYA_ICON_DIRECTORY}/he_calculate_occlusion.png 105 | ${MAYA_ICON_DIRECTORY}/he_curve_instancer.png 106 | ${MAYA_ICON_DIRECTORY}/he_delete_small_parts.png 107 | ${MAYA_ICON_DIRECTORY}/he_polyreduce.png 108 | ${MAYA_ICON_DIRECTORY}/he_set_pivot.png 109 | ${MAYA_ICON_DIRECTORY}/he_surface_instancer.png 110 | ${MAYA_ICON_DIRECTORY}/houdiniAsset.png 111 | ) 112 | 113 | -------------------------------------------------------------------------------- /OutputGeometry.C: -------------------------------------------------------------------------------- 1 | #include "AssetNode.h" 2 | #include "OutputGeometry.h" 3 | #include "OutputGeometryPart.h" 4 | #include "OutputObject.h" 5 | #include "util.h" 6 | 7 | OutputGeometry::OutputGeometry(HAPI_NodeId nodeId) 8 | : myNodeId(nodeId), myLastCookCount(0) 9 | { 10 | update(); 11 | } 12 | 13 | OutputGeometry::~OutputGeometry() 14 | { 15 | for (int i = myParts.size(); i-- > 0;) 16 | { 17 | delete myParts.back(); 18 | myParts.pop_back(); 19 | } 20 | } 21 | 22 | void 23 | OutputGeometry::update() 24 | { 25 | HAPI_Result hapiResult; 26 | 27 | hapiResult = HAPI_GetNodeInfo( 28 | Util::theHAPISession.get(), myNodeId, &myNodeInfo); 29 | CHECK_HAPI(hapiResult); 30 | 31 | hapiResult = HAPI_GetGeoInfo( 32 | Util::theHAPISession.get(), myNodeId, &myGeoInfo); 33 | if (HAPI_FAIL(hapiResult)) 34 | { 35 | // Make sre myGeoInfo is properly initialized. 36 | HAPI_GeoInfo_Init(&myGeoInfo); 37 | 38 | // Even when HAPI_GetGeoInfo() failed, there's always at least one 39 | // part. So we want the below code to initialize myParts. 40 | } 41 | 42 | if (myGeoInfo.type == HAPI_GEOTYPE_DEFAULT || 43 | myGeoInfo.type == HAPI_GEOTYPE_INTERMEDIATE || 44 | myGeoInfo.type == HAPI_GEOTYPE_INPUT || 45 | myGeoInfo.type == HAPI_GEOTYPE_CURVE) 46 | { 47 | // Create any new OutputGeometryPart 48 | myParts.reserve(myGeoInfo.partCount); 49 | for (int i = myParts.size(); i < myGeoInfo.partCount; i++) 50 | { 51 | OutputGeometryPart *outputGeometryPart = new OutputGeometryPart( 52 | myNodeId, i); 53 | myParts.push_back(outputGeometryPart); 54 | } 55 | 56 | // Destroy the extra OutputGeometryPart 57 | for (int i = myParts.size(); i-- > myGeoInfo.partCount;) 58 | { 59 | delete myParts.back(); 60 | myParts.pop_back(); 61 | } 62 | } 63 | } 64 | 65 | MStatus 66 | OutputGeometry::compute(const MTime &time, 67 | const MPlug &geoPlug, 68 | MDataBlock &data, 69 | MDataHandle &geoHandle, 70 | AssetNodeOptions::AccessorDataBlock &options, 71 | bool &needToSyncOutputs, 72 | const bool needToRecomputeOutputData) 73 | { 74 | MStatus stat; 75 | 76 | data.setClean(geoPlug); 77 | 78 | update(); 79 | 80 | MDataHandle geoNameHandle = geoHandle.child(AssetNode::outputGeoName); 81 | MString geoName; 82 | if (myGeoInfo.nameSH != 0) 83 | { 84 | geoName = Util::HAPIString(myGeoInfo.nameSH); 85 | } 86 | geoNameHandle.setString(geoName); 87 | 88 | MDataHandle isTemplatedHandle = 89 | geoHandle.child(AssetNode::outputGeoIsTemplated); 90 | isTemplatedHandle.setBool(myGeoInfo.isTemplated); 91 | 92 | MDataHandle isDisplayGeoHandle = 93 | geoHandle.child(AssetNode::outputGeoIsDisplayGeo); 94 | isDisplayGeoHandle.setBool(myGeoInfo.isDisplayGeo); 95 | 96 | MPlug partsPlug = geoPlug.child(AssetNode::outputParts); 97 | MDataHandle partsHandle = geoHandle.child(AssetNode::outputParts); 98 | MArrayDataHandle partsArrayHandle(partsHandle); 99 | 100 | bool partCountChanged = partsArrayHandle.elementCount() != 101 | (unsigned int)myGeoInfo.partCount; 102 | if (partCountChanged) 103 | { 104 | Util::resizeArrayDataHandle(partsArrayHandle, myGeoInfo.partCount); 105 | needToSyncOutputs = true; 106 | } 107 | 108 | bool forceCompute = needToRecomputeOutputData; 109 | for (int i = 0; i < myGeoInfo.partCount; i++) 110 | { 111 | forceCompute |= myParts[i]->needCompute(options); 112 | } 113 | 114 | // if we got here it's an output, even if HAPI thinks it's an input too 115 | if (myGeoInfo.type == HAPI_GEOTYPE_DEFAULT || 116 | myGeoInfo.type == HAPI_GEOTYPE_INTERMEDIATE || 117 | myGeoInfo.type == HAPI_GEOTYPE_INPUT || 118 | myGeoInfo.type == HAPI_GEOTYPE_CURVE) 119 | { 120 | if (myNodeInfo.totalCookCount > myLastCookCount || partCountChanged || 121 | forceCompute) 122 | { 123 | // Compute the OutputGeometryPart 124 | for (int i = 0; i < myGeoInfo.partCount; i++) 125 | { 126 | MPlug partPlug = partsPlug.elementByLogicalIndex(i); 127 | 128 | CHECK_MSTATUS(partsArrayHandle.jumpToArrayElement(i)); 129 | MDataHandle partHandle = partsArrayHandle.outputValue(); 130 | 131 | stat = myParts[i]->compute(time, partPlug, data, partHandle, 132 | options, needToSyncOutputs); 133 | CHECK_MSTATUS_AND_RETURN(stat, MS::kFailure); 134 | } 135 | } 136 | else 137 | { 138 | // even if nothing changed, clean the plugs 139 | for (int i = 0; i < myGeoInfo.partCount; i++) 140 | { 141 | MPlug partPlug = partsPlug.elementByLogicalIndex(i); 142 | 143 | MPlugArray childPlugs; 144 | Util::getChildPlugs(childPlugs, partPlug); 145 | for (unsigned int j = 0; j < childPlugs.length(); j++) 146 | { 147 | data.setClean(childPlugs[j]); 148 | } 149 | } 150 | } 151 | } 152 | 153 | myLastCookCount = myNodeInfo.totalCookCount; 154 | 155 | return MS::kSuccess; 156 | } 157 | -------------------------------------------------------------------------------- /InputMergeNode.C: -------------------------------------------------------------------------------- 1 | #include "InputMergeNode.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "MayaTypeID.h" 7 | #include "hapiutil.h" 8 | #include "util.h" 9 | 10 | MString InputMergeNode::typeName("houdiniInputMerge"); 11 | MTypeId InputMergeNode::typeId(MayaTypeID_HoudiniInputMergeNode); 12 | 13 | MObject InputMergeNode::inputTransform; 14 | MObject InputMergeNode::inputNode; 15 | MObject InputMergeNode::outputNodeId; 16 | MObject InputMergeNode::packBeforeMerge; 17 | 18 | void * 19 | InputMergeNode::creator() 20 | { 21 | return new InputMergeNode(); 22 | } 23 | 24 | MStatus 25 | InputMergeNode::initialize() 26 | { 27 | MFnMatrixAttribute mAttr; 28 | MFnNumericAttribute nAttr; 29 | 30 | InputMergeNode::inputTransform = mAttr.create( 31 | "inputTransform", "inputTransform"); 32 | addAttribute(InputMergeNode::inputTransform); 33 | 34 | InputMergeNode::inputNode = nAttr.create( 35 | "inputNode", "inputNode", MFnNumericData::kInt, -1); 36 | nAttr.setArray(true); 37 | nAttr.setCached(false); 38 | nAttr.setStorable(false); 39 | addAttribute(InputMergeNode::inputNode); 40 | 41 | InputMergeNode::outputNodeId = nAttr.create( 42 | "outputNodeId", "outputNodeId", MFnNumericData::kInt, -1); 43 | nAttr.setCached(false); 44 | nAttr.setStorable(false); 45 | addAttribute(InputMergeNode::outputNodeId); 46 | 47 | InputMergeNode::packBeforeMerge = nAttr.create( 48 | "packBeforeMerge", "packBeforeMerge", MFnNumericData::kBoolean, false); 49 | nAttr.setCached(false); 50 | nAttr.setStorable(true); 51 | addAttribute(InputMergeNode::packBeforeMerge); 52 | 53 | attributeAffects( 54 | InputMergeNode::inputTransform, InputMergeNode::outputNodeId); 55 | attributeAffects(InputMergeNode::inputNode, InputMergeNode::outputNodeId); 56 | attributeAffects(InputMergeNode::packBeforeMerge, InputMergeNode::outputNodeId); 57 | 58 | return MStatus::kSuccess; 59 | } 60 | 61 | InputMergeNode::InputMergeNode() : myGeometryNodeId(-1) {} 62 | 63 | InputMergeNode::~InputMergeNode() 64 | { 65 | if (!Util::theHAPISession.get()) 66 | return; 67 | 68 | // the merge is a sop, so it will have a parent geo 69 | // and an objectMerge to remove as well 70 | HAPI_NodeInfo node_info; 71 | HoudiniApi::GetNodeInfo(Util::theHAPISession.get(), myGeometryNodeId, &node_info); 72 | if (node_info.parentId >= 0) 73 | { 74 | CHECK_HAPI( 75 | HoudiniApi::DeleteNode(Util::theHAPISession.get(), node_info.parentId)); 76 | } 77 | } 78 | 79 | MStatus 80 | InputMergeNode::compute(const MPlug &plug, MDataBlock &dataBlock) 81 | { 82 | if (myGeometryNodeId == -1) 83 | { 84 | Util::PythonInterpreterLock pythonInterpreterLock; 85 | 86 | MFnDependencyNode mergeNodeFn(thisMObject()); 87 | MPlug frozenPlug = mergeNodeFn.findPlug("frozen", true); 88 | bool frozen = frozenPlug.asBool(); 89 | 90 | if (!frozen) 91 | { 92 | CHECK_HAPI(HoudiniApi::CreateNode(Util::theHAPISession.get(), -1, 93 | "Sop/merge", NULL, false, 94 | &myGeometryNodeId)); 95 | if (!Util::statusCheckLoop()) 96 | { 97 | DISPLAY_ERROR( 98 | MString("Unexpected error when creating merge node.")); 99 | } 100 | } 101 | else 102 | { 103 | // if the node has been frozen ever since file open 104 | // we don't create a merge or compute 105 | return MStatus::kSuccess; 106 | } 107 | } 108 | 109 | if (plug == InputMergeNode::outputNodeId) 110 | { 111 | MArrayDataHandle inputNodeArrayHandle = 112 | dataBlock.inputArrayValue(InputMergeNode::inputNode); 113 | 114 | const unsigned int mergeCount = inputNodeArrayHandle.elementCount(); 115 | 116 | for (unsigned int i = 0; i < mergeCount; i++) 117 | { 118 | inputNodeArrayHandle.jumpToElement(i); 119 | MDataHandle inputNodeHandle = inputNodeArrayHandle.inputValue(); 120 | 121 | HAPI_NodeId inputNode = inputNodeHandle.asInt(); 122 | 123 | CHECK_HAPI(HoudiniApi::ConnectNodeInput( 124 | Util::theHAPISession.get(), myGeometryNodeId, i, inputNode, 0)); 125 | } 126 | 127 | // Get the object merges connected to this merge node 128 | HAPI_NodeInfo mergeNodeInfo; 129 | CHECK_HAPI(HoudiniApi::GetNodeInfo(Util::theHAPISession.get(), 130 | myGeometryNodeId, &mergeNodeInfo)); 131 | 132 | MPlug packBeforeMergePlug(thisMObject(), InputMergeNode::packBeforeMerge); 133 | bool packBeforeMerge = packBeforeMergePlug.asBool(); 134 | 135 | for (int i = 0; i < mergeNodeInfo.inputCount; i++) 136 | { 137 | HAPI_NodeId objectMergeId; 138 | 139 | if (HoudiniApi::QueryNodeInput(Util::theHAPISession.get(), myGeometryNodeId, 140 | i, &objectMergeId) != HAPI_RESULT_SUCCESS || objectMergeId < 0) 141 | break; 142 | 143 | CHECK_HAPI(HoudiniApi::SetParmIntValue(Util::theHAPISession.get(), 144 | objectMergeId, "pack", 0, (int)packBeforeMerge)); 145 | } 146 | 147 | MDataHandle outputNodeIdHandle = 148 | dataBlock.outputValue(InputMergeNode::outputNodeId); 149 | 150 | outputNodeIdHandle.setInt(myGeometryNodeId); 151 | 152 | return MStatus::kSuccess; 153 | } 154 | 155 | return MPxNode::compute(plug, dataBlock); 156 | } 157 | 158 | -------------------------------------------------------------------------------- /cmake/Modules/FindHoudiniEngine.cmake: -------------------------------------------------------------------------------- 1 | # Output variables: 2 | # HoudiniEngine_FOUND 3 | # HoudiniEngine_INCLUDE_DIRS 4 | # HoudiniEngine_LIBRARIES 5 | # HoudiniEngine_LIBRARY_DIR 6 | # HoudiniEngine_BINARY_DIR 7 | # 8 | # Hints for finding package: 9 | # HoudiniEngine_ROOT 10 | # HoudiniEngine_INCLUDEDIR 11 | # HoudiniEngine_LIBRARYDIR 12 | # HoudiniEngine_BINARYDIR 13 | 14 | set( _houdiniengine_lib HAPIL ) 15 | if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" ) 16 | set( _houdiniengine_hars_executable HARS ) 17 | elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) 18 | set( _houdiniengine_hars_executable HARS.exe ) 19 | elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) 20 | set( _houdiniengine_hars_executable HARS ) 21 | endif () 22 | 23 | #if ( NOT HoudiniEngine_FIND_VERSION ) 24 | # message( FATAL_ERROR "Houdini Engine version is not specified." ) 25 | #endif () 26 | 27 | ######################################## 28 | # Determine search directories 29 | ######################################## 30 | # HoudiniEngine_INCLUDEDIR 31 | if ( DEFINED HoudiniEngine_INCLUDEDIR ) 32 | list( APPEND _houdiniengine_include_search_dirs "${HoudiniEngine_INCLUDEDIR}" ) 33 | endif () 34 | 35 | # HoudiniEngine_LIBRARYDIR 36 | if ( DEFINED HoudiniEngine_LIBRARYDIR ) 37 | list( APPEND _houdiniengine_library_search_dirs "${HoudiniEngine_LIBRARYDIR}" ) 38 | endif () 39 | 40 | # HoudiniEngine_BINARYDIR 41 | if ( DEFINED HoudiniEngine_BINARYDIR ) 42 | list( APPEND _houdiniengine_binary_search_dirs "${HoudiniEngine_BINARYDIR}" ) 43 | endif () 44 | 45 | # HoudiniEngine_ROOT 46 | if ( DEFINED HoudiniEngine_ROOT ) 47 | set( _houdiniengine_root ${HoudiniEngine_ROOT} ) 48 | #else () 49 | # if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" ) 50 | # set( _houdiniengine_root "/opt/hfs${HoudiniEngine_FIND_VERSION}" ) 51 | # elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) 52 | # set( _houdiniengine_root "$ENV{ProgramFiles}/Side\ Effects\ Software/Houdini\ ${HoudiniEngine_FIND_VERSION}/bin" ) 53 | # elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) 54 | # set( _houdiniengine_root "/Applications/Houdini ${HoudiniEngine_FIND_VERSION}/Houdini.framework/Versions/Current/Resources" ) 55 | # endif () 56 | endif () 57 | if ( _houdiniengine_root ) 58 | if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" ) 59 | list( APPEND _houdiniengine_include_search_dirs "${_houdiniengine_root}/toolkit/include" ) 60 | list( APPEND _houdiniengine_library_search_dirs "${_houdiniengine_root}/dsolib" ) 61 | list( APPEND _houdiniengine_binary_search_dirs "${_houdiniengine_root}/bin" ) 62 | elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) 63 | list( APPEND _houdiniengine_include_search_dirs "${_houdiniengine_root}/toolkit/include" ) 64 | list( APPEND _houdiniengine_library_search_dirs "${_houdiniengine_root}/custom/houdini/dsolib" ) 65 | list( APPEND _houdiniengine_binary_search_dirs "${_houdiniengine_root}/bin" ) 66 | elseif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" ) 67 | list( APPEND _houdiniengine_include_search_dirs "${_houdiniengine_root}/toolkit/include" ) 68 | list( APPEND _houdiniengine_library_search_dirs "${_houdiniengine_root}/../Libraries" ) 69 | list( APPEND _houdiniengine_binary_search_dirs "${_houdiniengine_root}/bin" ) 70 | endif () 71 | endif () 72 | 73 | ######################################## 74 | # Find 75 | ######################################## 76 | find_path( 77 | HoudiniEngine_INCLUDE_DIRS 78 | HAPI/HAPI.h 79 | HINTS ${_houdiniengine_include_search_dirs} 80 | ) 81 | list( APPEND _houdiniengine_required_vars HoudiniEngine_INCLUDE_DIRS ) 82 | 83 | # Houdini libraries on Windows are lib.a or lib.lib 84 | if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) 85 | set(CMAKE_FIND_LIBRARY_PREFIXES "lib") 86 | if ( DEFINED HoudiniEngine_OLD_LIBRARY_EXTENSION ) 87 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") 88 | endif () 89 | endif () 90 | find_library( 91 | HoudiniEngine_${_houdiniengine_lib} 92 | NAMES ${_houdiniengine_lib} 93 | HINTS ${_houdiniengine_library_search_dirs} 94 | ) 95 | list( APPEND _houdiniengine_required_vars HoudiniEngine_${_houdiniengine_lib} ) 96 | list( APPEND HoudiniEngine_LIBRARIES ${HoudiniEngine_${_houdiniengine_lib}} ) 97 | get_filename_component( 98 | HoudiniEngine_LIBRARY_DIR 99 | ${HoudiniEngine_${_houdiniengine_lib}} 100 | DIRECTORY 101 | ) 102 | list( APPEND _houdiniengine_required_vars HoudiniEngine_LIBRARY_DIR ) 103 | 104 | find_path( 105 | HoudiniEngine_BINARY_DIR 106 | ${_houdiniengine_hars_executable} 107 | HINTS ${_houdiniengine_binary_search_dirs} 108 | ) 109 | list( APPEND _houdiniengine_required_vars HoudiniEngine_BINARY_DIR ) 110 | 111 | ######################################## 112 | # Create library target 113 | ######################################## 114 | add_library( HoudiniEngine SHARED IMPORTED ) 115 | 116 | if ( NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) 117 | set_target_properties( 118 | HoudiniEngine 119 | PROPERTIES 120 | IMPORTED_LOCATION ${HoudiniEngine_${_houdiniengine_lib}} 121 | ) 122 | else () 123 | # IMPORTED_IMPLIB is used on Windows. 124 | set_target_properties( 125 | HoudiniEngine 126 | PROPERTIES 127 | IMPORTED_IMPLIB ${HoudiniEngine_${_houdiniengine_lib}} 128 | ) 129 | endif () 130 | 131 | set_target_properties( 132 | HoudiniEngine 133 | PROPERTIES 134 | INTERFACE_INCLUDE_DIRECTORIES "${HoudiniEngine_INCLUDE_DIRS}" 135 | ) 136 | 137 | ######################################## 138 | # Handle standard arguments 139 | ######################################## 140 | include( FindPackageHandleStandardArgs ) 141 | find_package_handle_standard_args( 142 | HoudiniEngine 143 | DEFAULT_MSG 144 | ${_houdiniengine_required_vars} 145 | ) 146 | -------------------------------------------------------------------------------- /scripts/houdiniEngineAssetLibraryManager.mel: -------------------------------------------------------------------------------- 1 | global string $houdiniEngineAssetLibraryManager_assetLibraryFilePaths[]; 2 | 3 | global proc 4 | houdiniEngineAssetLibraryManager_addLoadedFile(string $assetLibraryFilePath) 5 | { 6 | global string $houdiniEngineAssetLibraryManager_assetLibraryFilePaths[]; 7 | 8 | // Maya 2012 doesn't have stringArrayFind 9 | int $index = -1; 10 | for($i = 0; $i < size($houdiniEngineAssetLibraryManager_assetLibraryFilePaths); $i++) 11 | { 12 | if($assetLibraryFilePath == $houdiniEngineAssetLibraryManager_assetLibraryFilePaths[$i]) 13 | { 14 | $index = $i; 15 | break; 16 | } 17 | } 18 | 19 | // does it already exist? 20 | if($index == -1) 21 | { 22 | $houdiniEngineAssetLibraryManager_assetLibraryFilePaths[size($houdiniEngineAssetLibraryManager_assetLibraryFilePaths)] = 23 | $assetLibraryFilePath; 24 | $houdiniEngineAssetLibraryManager_assetLibraryFilePaths = `sort $houdiniEngineAssetLibraryManager_assetLibraryFilePaths`; 25 | } 26 | 27 | } 28 | 29 | global proc 30 | houdiniEngineAssetLibraryManager_refreshAssetList() 31 | { 32 | global string $houdiniEngineAssetLibraryManager_assetLibraryFilePaths[]; 33 | 34 | if(!`window -exists houdiniEngineAssetLibraryManagerWindow`) 35 | { 36 | return; 37 | } 38 | 39 | treeView -edit -removeAll houdiniEngineAssetTreeView; 40 | 41 | for($assetLibraryFilePath in $houdiniEngineAssetLibraryManager_assetLibraryFilePaths) 42 | { 43 | treeView -e 44 | -addItem $assetLibraryFilePath "" 45 | houdiniEngineAssetTreeView; 46 | 47 | for($asset in `houdiniAsset -listAssets $assetLibraryFilePath`) 48 | { 49 | string $command = "houdiniAsset -loadAsset \"" + encodeString($assetLibraryFilePath) + "\"" 50 | + " \"" + $asset + "\""; 51 | 52 | string $itemName = ($assetLibraryFilePath + "," + $asset); 53 | treeView -e 54 | -addItem $itemName $assetLibraryFilePath 55 | houdiniEngineAssetTreeView; 56 | treeView -e 57 | -displayLabel $itemName $asset 58 | houdiniEngineAssetTreeView; 59 | } 60 | } 61 | } 62 | 63 | global proc 64 | houdiniEngineAssetLibraryManager_loadAsset(string $item) 65 | { 66 | // if the user double clicked on the OTL file, do nothing 67 | // check -itemExists because of treeView bug with displayLabel 68 | if(`treeView -q -itemExists $item houdiniEngineAssetTreeView` 69 | && !`treeView -q -isLeaf $item houdiniEngineAssetTreeView`) 70 | { 71 | return; 72 | } 73 | 74 | //treeView has a bug where the itemDblClickCommand is called with the 75 | //displayLabel instead of the item name, so we can't use comma as a 76 | //delimiter. 77 | 78 | //string $tokens[]; 79 | //tokenize $item "," $tokens; 80 | 81 | //string $assetLibraryFilePath = $tokens[0]; 82 | //string $asset = $tokens[1]; 83 | 84 | string $assetLibraryFilePath; 85 | string $asset; 86 | 87 | // workaround the treeView bug 88 | // check the selected item 89 | if(size($asset) == 0) 90 | { 91 | string $selectItem[] = `treeView -q -selectItem houdiniEngineAssetTreeView`; 92 | 93 | if(size($selectItem) == 1) 94 | { 95 | string $tokens[]; 96 | tokenize $selectItem[0] "," $tokens; 97 | 98 | if(size($tokens) == 2 && $tokens[1] == $item) 99 | { 100 | $assetLibraryFilePath = $tokens[0]; 101 | $asset = $tokens[1]; 102 | } 103 | } 104 | } 105 | 106 | // workaround the treeView bug 107 | // find it from the list of children 108 | if(size($asset) == 0) 109 | { 110 | string $child; 111 | for($child in `treeView -q -children "" houdiniEngineAssetTreeView`) 112 | { 113 | string $tokens[]; 114 | tokenize $child "," $tokens; 115 | 116 | if(size($tokens) == 2 && $tokens[1] == $item) 117 | { 118 | $assetLibraryFilePath = $tokens[0]; 119 | $asset = $tokens[1]; 120 | 121 | break; 122 | } 123 | } 124 | } 125 | 126 | if(size($asset) == 0) 127 | { 128 | error "Cannot determine clicked item from treeView due to Maya bug."; 129 | return; 130 | } 131 | 132 | houdiniAsset -loadAsset $assetLibraryFilePath $asset; 133 | } 134 | 135 | global proc 136 | houdiniEngineAssetLibraryManager() 137 | { 138 | if(`window -exists houdiniEngineAssetLibraryManagerWindow`) 139 | { 140 | showWindow houdiniEngineAssetLibraryManagerWindow; 141 | return; 142 | } 143 | 144 | window 145 | -title "Asset Library Manager" 146 | -menuBar true 147 | houdiniEngineAssetLibraryManagerWindow; 148 | 149 | // controls 150 | string $formLayout = `formLayout`; 151 | // Maya 2012 and 2013 don't support "-allowMultiSelection" 152 | string $treeView = `treeView houdiniEngineAssetTreeView`; 153 | 154 | formLayout -e 155 | -attachForm houdiniEngineAssetTreeView top 0 156 | -attachForm houdiniEngineAssetTreeView left 0 157 | -attachForm houdiniEngineAssetTreeView bottom 0 158 | -attachForm houdiniEngineAssetTreeView right 0 159 | $formLayout; 160 | 161 | treeView -e 162 | -itemDblClickCommand ("houdiniEngineAssetLibraryManager_loadAsset") 163 | houdiniEngineAssetTreeView; 164 | 165 | // menus 166 | menu -label "File"; 167 | menuItem 168 | -label "Close" 169 | -command ("evalDeferred \"deleteUI -window houdiniEngineAssetLibraryManagerWindow\""); 170 | 171 | // populate asset list 172 | houdiniEngineAssetLibraryManager_refreshAssetList; 173 | 174 | showWindow; 175 | } 176 | -------------------------------------------------------------------------------- /NodeOptions.h: -------------------------------------------------------------------------------- 1 | #ifndef __NodeOptions_h__ 2 | #define __NodeOptions_h__ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | template 12 | class NodeOptionDefinition 13 | { 14 | public: 15 | template 16 | class Option; 17 | 18 | public: 19 | void addAttributes() 20 | { 21 | for (auto &attribute : myAttributes) 22 | MPxNode::addAttribute(attribute); 23 | } 24 | 25 | std::array myAttributes; 26 | }; 27 | 28 | template 29 | template 30 | class NodeOptionDefinition::Option 31 | { 32 | public: 33 | Option(NodeOptionDefinition *base, 34 | const char *name, 35 | bool defaultValue) 36 | { 37 | MObject &attribute = base->myAttributes[Index]; 38 | 39 | MFnNumericAttribute nAttr; 40 | 41 | attribute = nAttr.create( 42 | name, name, MFnNumericData::kBoolean, defaultValue); 43 | nAttr.setInternal(true); 44 | } 45 | }; 46 | 47 | template 48 | struct DataSourceTrait 49 | { 50 | typedef T DataSource; 51 | }; 52 | 53 | template <> 54 | struct DataSourceTrait 55 | { 56 | typedef MFnDependencyNode DataSource; 57 | }; 58 | 59 | template 60 | class NodeOptionAccessor 61 | { 62 | private: 63 | template 64 | class OptionImpl; 65 | 66 | public: 67 | template 68 | class Option 69 | { 70 | public: 71 | // gcc 4.8 seems to need this to compile 72 | Option() {} 73 | Option(NodeOptionAccessor *base, 74 | const char *name, 75 | Type defaultValue) 76 | : myBase(base) 77 | { 78 | } 79 | 80 | Type operator()() const 81 | { 82 | return OptionImpl()(myBase); 83 | } 84 | 85 | private: 86 | NodeOptionAccessor *myBase; 87 | }; 88 | 89 | public: 90 | NodeOptionAccessor(const Definition &definition, DataSource &dataSource) 91 | : myDefinition(definition), myDataSource(dataSource) 92 | { 93 | } 94 | 95 | private: 96 | const Definition &myDefinition; 97 | DataSource &myDataSource; 98 | }; 99 | 100 | template 101 | template 102 | class NodeOptionAccessor::OptionImpl 104 | { 105 | public: 106 | bool operator()(NodeOptionAccessor *base) const 107 | { 108 | const MObject &attribute = base->myDefinition.myAttributes[Index]; 109 | 110 | return base->myDataSource.inputValue(attribute).asBool(); 111 | } 112 | }; 113 | 114 | template 115 | template 116 | class NodeOptionAccessor::OptionImpl 118 | { 119 | public: 120 | bool operator()( 121 | NodeOptionAccessor *base) const 122 | { 123 | const MObject &attribute = base->myDefinition.myAttributes[Index]; 124 | 125 | return base->myDataSource.findPlug(attribute, true).asBool(); 126 | } 127 | }; 128 | 129 | #define NODE_OPTIONS_BEGIN(NAME) \ 130 | namespace NAME \ 131 | { \ 132 | const int CounterStart = __COUNTER__ + 1; \ 133 | \ 134 | template \ 135 | class Interface : public Base \ 136 | { \ 137 | private: \ 138 | template \ 139 | using Option = typename Base::template Option; \ 140 | \ 141 | public: \ 142 | using Base::Base; 143 | 144 | #define NODE_OPTION(NAME, TYPE, DEFAULT_VALUE) \ 145 | Option<__COUNTER__ - CounterStart, TYPE> NAME = { \ 146 | this, #NAME, (DEFAULT_VALUE)}; 147 | 148 | #define NODE_OPTIONS_END() \ 149 | } \ 150 | ; \ 151 | \ 152 | typedef Interface> \ 153 | Definition; \ 154 | \ 155 | template \ 156 | using Accessor = Interface>; \ 157 | typedef Interface> \ 158 | AccessorDataBlock; \ 159 | typedef Interface> \ 160 | AccessorFn; \ 161 | } 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /InputTransformNode.C: -------------------------------------------------------------------------------- 1 | #include "InputTransformNode.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "MayaTypeID.h" 9 | #include "hapiutil.h" 10 | #include "util.h" 11 | 12 | MString InputTransformNode::typeName("houdiniInputTransform"); 13 | MTypeId InputTransformNode::typeId(MayaTypeID_HoudiniInputTransformNode); 14 | 15 | MObject InputTransformNode::inputTransform; 16 | MObject InputTransformNode::inputMatrix; 17 | MObject InputTransformNode::preserveScale; 18 | MObject InputTransformNode::outputNodeId; 19 | 20 | void * 21 | InputTransformNode::creator() 22 | { 23 | return new InputTransformNode(); 24 | } 25 | 26 | MStatus 27 | InputTransformNode::initialize() 28 | { 29 | MFnMatrixAttribute mAttr; 30 | MFnNumericAttribute nAttr; 31 | 32 | InputTransformNode::inputTransform = mAttr.create( 33 | "inputTransform", "inputTransform"); 34 | addAttribute(InputTransformNode::inputTransform); 35 | 36 | InputTransformNode::inputMatrix = mAttr.create( 37 | "inputMatrix", "inputMatrix"); 38 | mAttr.setArray(true); 39 | mAttr.setCached(false); 40 | mAttr.setStorable(false); 41 | mAttr.setDisconnectBehavior(MFnAttribute::kDelete); 42 | addAttribute(InputTransformNode::inputMatrix); 43 | 44 | InputTransformNode::preserveScale = nAttr.create( 45 | "preserveScale", "preserveScale", MFnNumericData::kBoolean, false); 46 | nAttr.setCached(false); 47 | addAttribute(InputTransformNode::preserveScale); 48 | 49 | InputTransformNode::outputNodeId = nAttr.create( 50 | "outputNodeId", "outputNodeId", MFnNumericData::kInt, -1); 51 | nAttr.setCached(false); 52 | nAttr.setStorable(false); 53 | addAttribute(InputTransformNode::outputNodeId); 54 | 55 | attributeAffects( 56 | InputTransformNode::inputTransform, InputTransformNode::outputNodeId); 57 | attributeAffects( 58 | InputTransformNode::inputMatrix, InputTransformNode::outputNodeId); 59 | attributeAffects( 60 | InputTransformNode::preserveScale, InputTransformNode::outputNodeId); 61 | 62 | return MStatus::kSuccess; 63 | } 64 | 65 | InputTransformNode::InputTransformNode() : myGeometryNodeId(-1) 66 | { 67 | Util::PythonInterpreterLock pythonInterpreterLock; 68 | 69 | CHECK_HAPI(HoudiniApi::CreateInputNode( 70 | Util::theHAPISession.get(), -1, &myGeometryNodeId, NULL)); 71 | if (!Util::statusCheckLoop()) 72 | { 73 | DISPLAY_ERROR(MString("Unexpected error when creating input transform node.")); 74 | } 75 | } 76 | 77 | InputTransformNode::~InputTransformNode() 78 | { 79 | if (!Util::theHAPISession.get()) 80 | return; 81 | CHECK_HAPI(HoudiniApi::DeleteNode(Util::theHAPISession.get(), myGeometryNodeId)); 82 | } 83 | 84 | MStatus 85 | InputTransformNode::compute(const MPlug &plug, MDataBlock &dataBlock) 86 | { 87 | if (plug == InputTransformNode::outputNodeId) 88 | { 89 | MPlug inputMatrixArrayPlug( 90 | thisMObject(), InputTransformNode::inputMatrix); 91 | 92 | const unsigned int pointCount = inputMatrixArrayPlug.numElements(); 93 | 94 | HAPI_PartInfo partInfo; 95 | HoudiniApi::PartInfo_Init(&partInfo); 96 | partInfo.id = 0; 97 | partInfo.faceCount = 0; 98 | partInfo.vertexCount = 0; 99 | partInfo.pointCount = pointCount; 100 | 101 | HoudiniApi::SetPartInfo( 102 | Util::theHAPISession.get(), myGeometryNodeId, 0, &partInfo); 103 | 104 | std::vector P(pointCount * 3); 105 | std::vector orient(pointCount * 4); 106 | std::vector scale(pointCount * 3); 107 | MStringArray name(pointCount, MString()); 108 | 109 | MPlug preserveScalePlug( 110 | thisMObject(), InputTransformNode::preserveScale); 111 | bool preserveScale = preserveScalePlug.asBool(); 112 | 113 | for (unsigned int i = 0; i < pointCount; i++) 114 | { 115 | MPlug inputMatrixPlug = 116 | inputMatrixArrayPlug.elementByPhysicalIndex(i); 117 | 118 | MPlug sourceNodePlug = Util::plugSource(inputMatrixPlug); 119 | name[i] = Util::getNodeName(sourceNodePlug.node()); 120 | 121 | MDataHandle inputMatrixHandle = 122 | dataBlock.inputValue(inputMatrixPlug); 123 | 124 | MTransformationMatrix transformation = inputMatrixHandle.asMatrix(); 125 | 126 | MVector t = transformation.getTranslation(MSpace::kWorld); 127 | 128 | if (preserveScale) 129 | t *= 0.01; 130 | 131 | P[i * 3 + 0] = t[0]; 132 | P[i * 3 + 1] = t[1]; 133 | P[i * 3 + 2] = t[2]; 134 | 135 | MQuaternion r = transformation.rotation(); 136 | orient[i * 4 + 0] = r[0]; 137 | orient[i * 4 + 1] = r[1]; 138 | orient[i * 4 + 2] = r[2]; 139 | orient[i * 4 + 3] = r[3]; 140 | 141 | double s[3]; 142 | transformation.getScale(s, MSpace::kWorld); 143 | scale[i * 3 + 0] = s[0]; 144 | scale[i * 3 + 1] = s[1]; 145 | scale[i * 3 + 2] = s[2]; 146 | } 147 | 148 | CHECK_HAPI(hapiSetPointAttribute(myGeometryNodeId, 0, 1, "name", name)); 149 | 150 | CHECK_HAPI(hapiSetPointAttribute(myGeometryNodeId, 0, 3, "P", P)); 151 | 152 | CHECK_HAPI( 153 | hapiSetPointAttribute(myGeometryNodeId, 0, 4, "orient", orient)); 154 | 155 | CHECK_HAPI( 156 | hapiSetPointAttribute(myGeometryNodeId, 0, 3, "scale", scale)); 157 | 158 | HoudiniApi::CommitGeo(Util::theHAPISession.get(), myGeometryNodeId); 159 | 160 | MDataHandle outputNodeIdHandle = 161 | dataBlock.outputValue(InputTransformNode::outputNodeId); 162 | 163 | outputNodeIdHandle.setInt(myGeometryNodeId); 164 | 165 | return MStatus::kSuccess; 166 | } 167 | 168 | return MPxNode::compute(plug, dataBlock); 169 | } 170 | 171 | -------------------------------------------------------------------------------- /scripts/houdiniEngineRemoveHistory.mel: -------------------------------------------------------------------------------- 1 | 2 | proc int 3 | only_geo_upstream(string $dstNode) { 4 | // is there non-trivial mesh history upstream, or just a copy of the mesh? 5 | // and set/group membership 6 | string $historyInput = ""; 7 | if(`nodeType $dstNode` == "mesh") { 8 | $historyInput = ($dstNode + ".inMesh"); 9 | } else if(`nodeType $dstNode` == "groupParts") { 10 | $historyInput = ($dstNode + ".inputGeometry"); 11 | } else { 12 | return 0; 13 | } 14 | string $srcNodes[] = `listConnections -sh true -d false $historyInput`; 15 | if(size($srcNodes) > 0) 16 | return(only_geo_upstream($srcNodes[0])); 17 | else 18 | return 1; 19 | } 20 | proc string 21 | skip_downstream_sets(string $node) 22 | { 23 | // this should return the next non-trivial downstream history node 24 | // in the unlikely event that the graph is bad 25 | // and the chain ends in a groupParts node, we return it anyway 26 | if(`nodeType $node` == "groupParts") { 27 | string $dstNodes[] = `listConnections -sh true -s false ($node + ".outputGeometry")`; 28 | if(size($dstNodes) > 0) 29 | return( skip_downstream_sets($dstNodes[0])); 30 | else 31 | return $node; 32 | } 33 | else 34 | return $node; 35 | } 36 | 37 | global proc int 38 | houdiniEngine_removeHistory(string $assetNode) 39 | { 40 | 41 | if(`getAttr ($assetNode + ".assetConnectType\n")` != 1) { 42 | print("Error: assetConnectType suggests that this is not a history asset\n"); 43 | return 1; 44 | } 45 | 46 | // find the outMesh connection on the asset and the inMesh connection on the inputGeometry 47 | // reconnect those plugs, discard the inputGeometry but leave the asset and any other inputs it has 48 | // in case you want to reuse it elsewhere 49 | // we assume one mesh history/mesh input and one historymesh output 50 | // but there could be secondary inputs that are not part of the history chain 51 | // 4 ways the asset to be removed could be connected (nodes to be removed in caps): 52 | // asset->ASSET->asset 53 | // asset->ASSET->mesh/hist 54 | // mesh/hist->inputGeo->ASSET->asset 55 | // mesh/hist->INPUTGEO->ASSET->mesh/hist 56 | // we do not expect a merge or curve or transform, since history nodes apply to a single mesh 57 | // does not yet collapse 2 contiguous shape: 58 | // previous non-history case (where we had shape->inputGeo->asset->shape) 59 | 60 | 61 | // the node that is downstream from the asset determines the upstream attr to connect on 62 | // and whether or not we need to delete the houdiniInputGeometry as well 63 | string $dstNode = ""; 64 | string $outPlug = $assetNode + ".outputObjects[0].outputGeos[0].outputParts[0].outputPartMeshData"; 65 | string $dstPlugs[] = `listConnections -s false -p true $outPlug`; 66 | if(size($dstPlugs) == 0) { 67 | $outPlug = $assetNode + ".outputObjects[0].outputObjectMetaData"; 68 | $dstPlugs = `listConnections -s false -p true $outPlug`; 69 | } 70 | if(size($dstPlugs) > 0) { 71 | $dstNode = plugNode($dstPlugs[0]); 72 | } else { 73 | print "Error, this asset's output is not connected as construction history\n"; 74 | return 1; 75 | } 76 | 77 | string $srcPlugs[]; 78 | string $inputGeos[] = `listConnections -d false -type "houdiniInputGeometry" $assetNode`; 79 | if(size($inputGeos) > 0) { 80 | // input is mesh 81 | if(`nodeType $dstNode` == "houdiniAsset") { 82 | //output is asset 83 | string $dstNodeIdPlugs[] = `listConnections -s false -p true ($inputGeos[0]+".outputNodeId")`; 84 | $srcPlugs[0] = ($inputGeos[0]+".outputNodeId"); 85 | disconnectAttr $srcPlugs[0] $dstNodeIdPlugs[0]; 86 | } else { 87 | // output is mesh 88 | $srcPlugs = `listConnections -s true -p true ($inputGeos[0]+".inputGeometry")`; 89 | string $srcXformPlugs[] = `listConnections -d false -p true ($inputGeos[0]+".inputTransform")`; 90 | disconnectAttr $srcPlugs[0] ($inputGeos[0]+".inputGeometry"); 91 | if(size($srcXformPlugs) > 0) 92 | disconnectAttr $srcXformPlugs[0] ($inputGeos[0]+".inputTransform"); 93 | delete $inputGeos[0]; 94 | } 95 | } else { 96 | string $srcDataPlugs[] = `listConnections -d false -p true -type "houdiniAsset" $assetNode`; 97 | if(size($srcDataPlugs) > 0) { 98 | // input is asset 99 | string $dstNodeIdPlugs[] = `listConnections -s false -p true $srcDataPlugs[0]`; 100 | if(`nodeType $dstNode` == "houdiniAsset") { 101 | // output is asset 102 | $srcPlugs[0] = $srcDataPlugs[0]; 103 | disconnectAttr $srcDataPlugs[0] $dstNodeIdPlugs[0]; 104 | 105 | } else { 106 | // output is mesh 107 | string $srcNode = plugNode($srcDataPlugs[0]); 108 | $srcPlugs[0] = $srcNode + ".outputObjects[0].outputGeos[0].outputParts[0].outputPartMeshData"; 109 | disconnectAttr $srcDataPlugs[0] $dstNodeIdPlugs[0]; 110 | } 111 | } else { 112 | print "Error, this asset's input is not connected as construction history\n"; 113 | return 1; 114 | } 115 | } 116 | disconnectAttr $outPlug $dstPlugs[0]; 117 | connectAttr $srcPlugs[0] $dstPlugs[0]; 118 | delete $assetNode; 119 | 120 | // if we removed the last Asset, and we are left with 2 shapes 121 | // with possibly some groupParts nodes in between for component set/group membership 122 | // we delete history, which will bake the set membership down on the remaining shape. 123 | // Set membership that existed downstream from the asset may no longer be valid 124 | // but leaving the set in place is consistent with regular maya history 125 | 126 | $dstNode = skip_downstream_sets($dstNode); 127 | if((`nodeType $dstNode` == "mesh") && only_geo_upstream($dstNode)) 128 | delete -ch $dstNode; 129 | 130 | return 0; 131 | } 132 | 133 | -------------------------------------------------------------------------------- /doc/Maya_Session.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | @page Maya_Session Session 4 | 5 | @tableofcontents 6 | 7 | In order to use Houdini Engine, a Houdini Engine session needs to be created. Creating the session will load the necessary Houdini libraries and plug-ins, and setup Houdini Engine according to various preferences, and environment variables. 8 | 9 | @image html Maya_Session.png 10 | 11 | @section Maya_Session_Socket Socket 12 | 13 | Socket session was added when thin client was introduced in Houdini Engine. In short, thin client allows the main Houdini Engine processing to happen in a separate process, outside of the Maya process. For socket sessions, TCP socket is used to communicate between the Maya process and the Houdini Engine process. 14 | 15 | In order to use socket sessions, the Houdini Engine server must be manually started with a known port on a known machine. Then, the host and port information is entered into the plug-in. 16 | 17 | @section Maya_Session_NamedPipe Named Pipe 18 | 19 | Named pipe session was added when thin client was introduced in Houdini Engine. In short, thin client allows the main Houdini Engine processing to happen in a separate process, outside of the Maya process. For named pipe sessions, named pipe is used to communicate between the Maya process and the Houdini Engine process. This is the recommended method when using thin client in the same local machine. 20 | 21 | The named pipe session supports auto-starting the Houdini Engine server. When the plug-in initializes, the Houdini Engine server will be automatically started, and a named pipe is automatically chosen. In some cases on Linux, we have found that there can be library conflicts if the LD_LIBRARY_PATH has been set, resulting in HARS failing to start. There is now a preference to unset LD_LIBRARY_PATH before starting the server (and restoring the value afterwards, of course). The preference is on by default, but if you need to pass LD_LIBRARY_PATH to engine, you can turn this preference off. Likewise, we have also found that conflicts may arise when PYTHONPATH is set. If you enable "Unset PYTHONPATH", the PYTHONPATH variable will be cleared before starting the Houdini Engine server, as well as when assets are viewed in Houdini via the "View Assets in Houdini" menu item. This preference is off by default for backwards compatibility, and is present for all operating systems. The timeout for connecting to HARS can also be set. This is useful if Houdini takes a long time to start. 22 | 23 | The Houdini Engine server can also be manually started with a known named pipe. Then, the named pipe information is entered into the plug-in. 24 | 25 | @section Maya_Session_InProcess Within Maya Process 26 | 27 | The Within Maya Process session type is no longer supported in Maya as of Houdini 17.0 28 | 29 | Before thin client was added to Houdini Engine, creating a Houdini Engine session within the Maya process was how Houdini Engine was initialized. All the Houdini libraries and necessary files were loaded directly within Maya's process. This required the libraries of Houdini, its plug-ins, and their library dependencies to be compatible with the libraries of Maya and its plug-ins. Otherwise, there could be dynamic library conflicts, library symbol conflicts, and crashes. Since the introduction of Houdini Engine, we have encountered additional issues and conflicts, and we are no longer able to support this session type. 30 | 31 | If your session type optionVar is set to Within Process, Named Pipe with auto-start will be used instead. On some Linux systems, there may be problems with auto-start, in which case you will need to manually start HARS in order to connect to it. 32 | 33 | @section Maya_Session_OptionVars OptionVars 34 | 35 | If loading the houdini plugin is not possible with the default settings due to library conflicts, the optionVars controlling the session type can be set manually before loading the plugin. 36 | 37 | For a socket session, with the default port and host: 38 | @verbatim 39 | optionVar -iv "houdiniEngineSessionType" 1; 40 | optionVar -iv "houdiniEngineThriftPort" 9090; 41 | optionVar -sv "houdiniEngineThriftServer" "localhost"; 42 | @endverbatim 43 | 44 | For a named pipe session with autostart: 45 | 46 | @verbatim 47 | optionVar -iv "houdiniEngineSessionType" 2; 48 | optionVar -sv "houdiniEngineThriftPipe" "hapi"; 49 | optionVar -iv "houdiniEngineSessionPipeCustom" 0; 50 | optionVar -iv "houdiniEngineTimeout" 10000; 51 | @endverbatim 52 | 53 | @section Maya_Session_Server Server 54 | 55 | In order to use a socket session on the client side, you need to start the server executable HARS, included in Houdini installation. HARS is a console application with simple command-line arguments: 56 | 57 | @verbatim 58 | $ HARS -h 59 | Allowed options: 60 | -h [ --help ] Produce this help message 61 | -s [ --socket-port ] arg The server port, if using a TCP socket session 62 | -n [ --named-pipe ] arg The name of the pipe, if using a named pipe session 63 | -a [ --auto-close ] Close the server automatically when all client 64 | connections are closed 65 | -r [ --ready-handle ] arg Event to signal when the server is ready to serve 66 | (for automated server startup) 67 | @endverbatim 68 | 69 | HARS links directly to libHAPI and to core Houdini libraries and their dependencies. Since Thrift IPC is cross-platform, the server process (HARS) may be run on different platforms. The HARS server may also be used for a named pipe session when autostart is not specified. The port or pipe should match the one specified in the Houdini Engine preferences. Please see the Session section in the Houdini Engine Documentation for more details. 70 | 71 | Please note that the HARS server currently only supports a single client connection. If a client is connected to the server and another client tries to connect, then the second client will block until the first connection is closed. 72 | 73 | @section Maya_Session_CleaningUpTheSession Cleaning Up the Session 74 | 75 | If the server was set to autostart, unloading the plugin will terminate the server, and the houdini engine license will be released. The server will be restarted when the plugin is reloaded, and the server will acquire a license when the first asset is loaded. 76 | 77 | If the HARS server is started manually, it will not be automatically killed unless the auto-close option was set (and assuming that no other client was connected to the server). 78 | 79 | If the HARS server has been running for a while, it may get cluttered up with input and merge nodes belonging to deleted maya nodes still on the undo queue. Flushing the undo queue from time to time will ensure that those nodes are deleted. 80 | 81 | */ 82 | -------------------------------------------------------------------------------- /OutputDeform.C: -------------------------------------------------------------------------------- 1 | #include "OutputDeform.h" 2 | #include "OutputObject.h" 3 | #include "OutputGeometryObject.h" 4 | #include "OutputGeometry.h" 5 | #include "OutputGeometryPart.h" 6 | #include "hapiutil.h" 7 | 8 | 9 | bool 10 | OutputDeform::getDelayedPointAttribute( 11 | const char *name, OutputDeformCache &cache, size_t n, 12 | float *buf) 13 | { 14 | const HAPI_Session *session = Util::theHAPISession.get(); 15 | return getPointAttribute(session,myDelayedNodeId,myDelayedPartId, name, cache, n, buf); 16 | } 17 | 18 | bool 19 | OutputDeform::getPointAttribute(const HAPI_Session *session, 20 | const HAPI_NodeId &nodeId, const HAPI_PartId &partId, 21 | const char *name, OutputDeformCache &cache, size_t n, 22 | float *buf 23 | ) 24 | { 25 | 26 | HAPI_Result hapiResult; 27 | if (!cache.myNeed) 28 | return false; 29 | 30 | HAPI_AttributeInfo attrInfo; 31 | hapiResult = HAPI_GetAttributeInfo(session, nodeId, partId, name, 32 | HAPI_ATTROWNER_POINT, &attrInfo); 33 | 34 | if (HAPI_FAIL(hapiResult)) 35 | return false; 36 | 37 | if (!attrInfo.exists) 38 | return false; 39 | 40 | const int stride = cache.myStride; 41 | int numComponents = 3; 42 | if (attrInfo.storage==HAPI_STORAGETYPE_FLOAT64) 43 | { 44 | cache.myIsDouble = true; 45 | cache.myData64.resize(n*numComponents); 46 | cache.myData32.resize(0); 47 | 48 | if (buf) 49 | { 50 | float *dst = buf; 51 | double *src = &(cache.myData64[0]); 52 | 53 | { 54 | for (size_t i=0; i(obj); 122 | if (!gobj) 123 | return false; 124 | 125 | gobj->update(); 126 | if (gobj->myGeos.size()<1) 127 | return false; 128 | 129 | OutputGeometry *geo = gobj->myGeos[0]; 130 | if (!geo) 131 | return false; 132 | 133 | if (geo->myParts.size()<1) 134 | return false; 135 | 136 | OutputGeometryPart *part = geo->myParts[0]; 137 | if (!part) 138 | return false; 139 | 140 | HAPI_NodeId nodeId = part->myNodeId; 141 | HAPI_PartId partId = part->myPartId; 142 | 143 | const HAPI_Session *session = Util::theHAPISession.get(); 144 | HAPI_Result hapiResult; 145 | 146 | HAPI_PartInfo partInfo; 147 | hapiResult = HAPI_GetPartInfo(session, nodeId, partId, &partInfo ); 148 | if (HAPI_FAIL(hapiResult)) 149 | return false; 150 | 151 | unsigned int pointCount = (unsigned int)partInfo.pointCount; 152 | 153 | // Clamp the point count in case the houdini geo has less points than the 154 | // maya one. 155 | if (n>pointCount || n==0) 156 | n = pointCount; 157 | 158 | if (!mySkipPointAttributes) 159 | { 160 | if (!getPointAttribute(session,nodeId,partId,"P",myPos, n)) 161 | { 162 | myPos.myDirty = true; 163 | myPos.myIsValid = false; 164 | 165 | myNormal.myDirty = true; 166 | myNormal.myIsValid = false; 167 | 168 | myTexture.myDirty = true; 169 | myTexture.myIsValid = false; 170 | return false; 171 | } 172 | 173 | // N is optionnal 174 | if ( !getPointAttribute(session,nodeId,partId,"N",myNormal, n) ) 175 | { 176 | myNormal.myDirty = true; 177 | myNormal.myIsValid = false; 178 | } 179 | 180 | // uv is optionnal 181 | if ( !getPointAttribute(session,nodeId,partId,"uv",myTexture, n) ) 182 | { 183 | myTexture.myDirty = true; 184 | myTexture.myIsValid = false; 185 | } 186 | } 187 | else 188 | { 189 | myDelayedPointCount = n; 190 | myDelayedNodeId = nodeId; 191 | myDelayedPartId = partId; 192 | 193 | myPos.myDirty = true; 194 | myPos.myIsValid = true; 195 | 196 | myNormal.myDirty = true; 197 | myNormal.myIsValid = true; 198 | 199 | myTexture.myDirty = true; 200 | myTexture.myIsValid = true; 201 | } 202 | 203 | if (myNeedTopo) 204 | { 205 | // Quick check for topology change 206 | if (!myTopoChanged) 207 | { 208 | if (myPartInfo.faceCount != partInfo.faceCount || 209 | myPartInfo.vertexCount != partInfo.vertexCount ) 210 | { 211 | myTopoChanged = true; 212 | } 213 | } 214 | 215 | if (myTopoChanged) 216 | { 217 | myTopoValid = false; 218 | myFaceCounts.resize(partInfo.faceCount); 219 | 220 | hapiResult = HAPI_GetFaceCounts(session, nodeId, partId, 221 | &myFaceCounts.front(), 0, partInfo.faceCount); 222 | if (HAPI_FAIL(hapiResult)) 223 | return false; 224 | 225 | myVertexList.resize(partInfo.vertexCount); 226 | hapiResult = HAPI_GetVertexList(session, nodeId, partId, 227 | &myVertexList.front(), 0, 228 | partInfo.vertexCount); 229 | if (HAPI_FAIL(hapiResult)) 230 | return false; 231 | 232 | Util::reverseWindingOrder(myVertexList, myFaceCounts); 233 | 234 | myTopoChanged = false; 235 | myTopoDirty = true; 236 | myTopoValid = true; 237 | } 238 | } 239 | 240 | 241 | // Keep the last part info 242 | memcpy(&myPartInfo,&partInfo,sizeof(HAPI_PartInfo)); 243 | 244 | return true; 245 | } 246 | 247 | --------------------------------------------------------------------------------