diff --git a/colorbleed/maya/menu.json b/colorbleed/maya/menu.json index 1918163f97..9c3cc40bba 100644 --- a/colorbleed/maya/menu.json +++ b/colorbleed/maya/menu.json @@ -1,61 +1,1696 @@ -{"Animation": [{"title" : "Script A", - "tooltip": "Script A", - "command": "$SCRIPTMENU/script_a.py", - "sourcetype": "file", - "tags": ["test", "script", "cluster"], - "icon": "$SCRIPTMENU/resources/script_a.png", - "label": "SCR A"}, - {"title" : "Script B", - "tooltip": "Run script B", - "command": "$SCRIPTMENU/script_b.py", - "sourcetype": "file", - "tags": ["test", "script", "curves"]}, - {"title" : "Script C", - "tooltip": "Run script C", - "command": "$SCRIPTMENU/script_c.py", - "sourcetype": "file", - "tags": ["test", "script", "joints"], - "icon": ""} - ], +{ + "order": [ + "Modeling", + "Rigging", + "Shading", + "Animation", + "Layout", + "Particles", + "Cleanup", + "Projects", + "Pyblish", + "Others" + ], + "Modeling": [ + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\duplicate_normalized.py", + "sourcetype": "file", + "tags": ["modeling", "duplicate", "normalized"], + "title": "Duplicate Normalized", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\transferUVs.py", + "sourcetype": "file", + "tags": ["modeling", "transfer", "uv"], + "title": "Transfer UVs", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\mirrorSymmetry.py", + "sourcetype": "file", + "tags": ["modeling", "mirror", "symmetry"], + "title": "Mirror Symmetry", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\selectOutlineUI", + "sourcetype": "file", + "tags": ["modeling", "select", "outline", "ui"], + "title": "Select Outline UI", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\polyDeleteOtherUVSets.py", + "sourcetype": "file", + "tags": ["modeling", "polygon", "uvset", "delete"], + "title": "polyDeleteOtherUVSets", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\polyCombineQuick.py", + "sourcetype": "file", + "tags": ["modeling", "combine", "polygon", "quick"], + "title": "Polygon Combine Quick", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "modeling", + "separateMeshPerShader" + ], + "title": "Separate Mesh Per Shader", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\polyDetachSeparate.py", + "sourcetype": "file", + "tags": [ + "modeling", + "polyDetachSeparate" + ], + "title": "Polygon Detach and Separate", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\polyRelaxVerts.py", + "sourcetype": "file", + "tags": ["modeling", "relax", "verts"], + "title": "Polygon Relax Vertices", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\polySelectEveryNthEdgeUI.py", + "sourcetype": "file", + "tags": [ + "modeling", + "polySelectEveryNthEdgeUI" + ], + "title": "Select Every Nth Edge" + }, + { + "command": "$COLORBLEED_SCRIPTS\\modeling\\djPFXUVs.py", + "sourcetype": "file", + "tags": [ + "modeling", + "djPFXUVs" + ], + "title": "dj PFX UVs", + "tooltip": "" + } + ], -"Modeling": [{"title" : "Script A", - "tooltip": "Run script A", - "command": "$SCRIPTMENU/script_a.py", - "sourcetype": "file", - "tags": ["test", "script", "model", "blendshapes"]}, - {"title" : "Script B", - "tooltip": "Run script B", - "command": "$SCRIPTMENU/script_b.py", - "sourcetype": "file", - "tags": ["test", "script", "normals", "model"]}, - {"title" : "Script C", - "tooltip": "Run script C", - "command": "$SCRIPTMENU/script_c.py", - "sourcetype": "file", - "tags": ["math", "power", "sum"]} - ], + "Animation": [ + { + "command": "", + "sourcetype": "", + "tags": ["animation", "attributes"], + "title": "Attributes", + "tooltip": "", + "items": [ + { + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyValues.py", + "sourcetype": "file", + "tags": ["animation", "copy", "attributes"], + "title": "Copy Values", + "tooltip": "Copy attribute values" + },{ + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyInConnections.py", + "sourcetype": "file", + "tags": ["animation", "copy", "attributes", "connections", "incoming"], + "title": "Copy In Connections", + "tooltip": "Copy incoming connections" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyOutConnections.py", + "sourcetype": "file", + "tags": ["animation", "copy", "attributes", "connections", "out"], + "title": "Copy Out Connections", + "tooltip": "Copy outcoming connections" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyTransformLocal.py", + "sourcetype": "file", + "tags": ["animation", "copy", "attributes", "transforms", "local"], + "title": "Copy Local Transfroms", + "tooltip": "Copy local transfroms" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyTransformMatrix.py", + "sourcetype": "file", + "tags": ["animation", "copy", "attributes", "transforms", "matrix"], + "title": "Copy Matrix Transfroms", + "tooltip": "Copy Matrix transfroms" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyTransformUI.py", + "sourcetype": "file", + "tags": ["animation", "copy", "attributes", "transforms", "UI"], + "title": "Copy Transforms UI", + "tooltip": "Open the Copy Transforms UI" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\simpleCopyUI.py", + "sourcetype": "file", + "tags": ["animation", "copy", "attributes", "transforms", "UI", "simple"], + "title": "Simple Copy UI", + "tooltip": "Open the simple Copy Transforms UI" + } + ] + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "animation", + "optimize" + ], + "title": "Optimize", + "tooltip": "Optimization scripts", + "items":[ + { + "command": "$COLORBLEED_SCRIPTS\\animation\\optimize\\toggleFreezeHierarchy.py", + "sourcetype": "file", + "tags": ["animation", "hierarchy", "toggle", "freeze"], + "title": "Toggle Freeze Hierarchy", + "tooltip": "Freeze and unfreeze hierarchy" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\optimize\\toggleParallelNucleus.py", + "sourcetype": "file", + "tags": ["animation", "nucleus", "toggle", "parallel"], + "title": "Toggle Parallel Nucleus", + "tooltip": "Toggle parallel nucleus" + } + ] + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\zvParentMaster.py", + "sourcetype": "file", + "tags": [ + "animation", + "zvParentMaster" + ], + "title": "ZV Parent Master", + "tooltip": "Open ZV Parent Master UI" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\pathAnimation.py", + "sourcetype": "file", + "tags": ["animation", "path"], + "title": "Path Animation", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\timeStepper.py", + "sourcetype": "file", + "tags": [ + "animation", + "timeStepper" + ], + "title": "TimeStepper", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\bakeSelectedToWorldSpace.py", + "sourcetype": "file", + "tags": ["animation", "world space", "bake"], + "title": "Bake To World Space", + "tooltip": "Select the item which need to be baked to World Space" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\simplePlayblastUI.py", + "sourcetype": "file", + "tags": ["animation", "gui", "simple", "capture"], + "title": "Capture GUI - simple", + "tooltip": "Simplified version of the Capture GUI" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\anim_scene_optimizer.py", + "sourcetype": "file", + "tags": ["animation", "optimizer", "scene"], + "title": "Animation Scene Optimizer", + "tooltip": "Optimize animation in a scene" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\poseLibrary.py", + "sourcetype": "file", + "tags": [ + "animation", + "poseLibrary" + ], + "title": "Pose Library", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\capture_ui.py", + "sourcetype": "file", + "tags": ["animation", "capture", "screenshot", "movie"], + "title": "Capture GUI", + "tooltip": "Render current camera to an image or movie" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\key_amplifier_ui.py", + "sourcetype": "file", + "tags": [ + "animation", + "key_amplifier_ui" + ], + "title": "Key Amplifier UI", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\selectAllAnimationCurves.py", + "sourcetype": "file", + "tags": ["animation", "curves", "scene"], + "title": "Select All Animation Curves", + "tooltip": "Select all animation curves in the scene" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\offsetSelectedObjectsUI.py", + "sourcetype": "file", + "tags": [ + "animation", + "offsetSelectedObjectsUI" + ], + "title": "Offset Selected Object UI", + "tooltip": "Offset selected objects" + }, + { + "command": "$COLORBLEED_SCRIPTS\\animation\\tweenMachineUI.py", + "sourcetype": "file", + "tags": [ + "animation", + "tweenMachineUI" + ], + "title": "Tween Machine UI", + "tooltip": "" + } + ], -"Rigging": [{"title" : "Script A", - "tooltip": "Run script A", - "command": "$SCRIPTMENU/script_a.py", - "sourcetype": "file", - "tags": ["test", "tool", "rig", "skeleton"]}, - {"title" : "Script B", - "tooltip": "Run script A", - "command": "$SCRIPTMENU/script_b.py", - "sourcetype": "file", - "tags": ["test", "script", "cloth", "rig", "setup"]}, - {"title" : "Script C", - "tooltip": "Run script C", - "command": "$SCRIPTMENU/script_c.py", - "sourcetype": "file", - "tags": ["test", "script", "approval"]} - ], + "Rigging": [ + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "removeRotationAxis" + ], + "title": "removeRotationAxis", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "mirrorCurveShape" + ], + "title": "mirrorCurveShape", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "selectSkinclusterJointsFromSelectedMesh" + ], + "title": "selectSkinclusterJointsFromSelectedMesh", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "channelBoxManagerUI" + ], + "title": "channelBoxManagerUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "setJointOrientationFromCurrentRotation" + ], + "title": "setJointOrientationFromCurrentRotation", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "regenerate_blendshape_targets" + ], + "title": "regenerate_blendshape_targets", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "freezeTransformToGroup" + ], + "title": "freezeTransformToGroup", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "selectSkinclusterJointsFromSelectedComponents" + ], + "title": "selectSkinclusterJointsFromSelectedComponents", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "superRelativeCluster" + ], + "title": "superRelativeCluster", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "special" + ], + "title": "special", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "averageSkinWeights" + ], + "title": "averageSkinWeights", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "connectUI" + ], + "title": "connectUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "ikHandlePoleVectorLocator" + ], + "title": "ikHandlePoleVectorLocator", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "toggleSegmentScaleCompensate" + ], + "title": "toggleSegmentScaleCompensate", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "addCurveBetween" + ], + "title": "addCurveBetween", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "resetBindSelectedMeshes" + ], + "title": "resetBindSelectedMeshes", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "rapidRig" + ], + "title": "rapidRig", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "paintItNowUI" + ], + "title": "paintItNowUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "toggleSkinclusterDeformNormals" + ], + "title": "toggleSkinclusterDeformNormals", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "setSelectedJointsOrientationZero" + ], + "title": "setSelectedJointsOrientationZero", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "groupSelected" + ], + "title": "groupSelected", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "simpleControllerOnSelection" + ], + "title": "simpleControllerOnSelection", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "resetBindSelectedSkinJoints" + ], + "title": "resetBindSelectedSkinJoints", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "quickSetWeightsUI" + ], + "title": "quickSetWeightsUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "simpleControllerOnSelectionHierarchy" + ], + "title": "simpleControllerOnSelectionHierarchy", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "setRotationOrderUI" + ], + "title": "setRotationOrderUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "jointOrientUI" + ], + "title": "jointOrientUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "copySkinWeightsLocal" + ], + "title": "copySkinWeightsLocal", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "tfSmoothSkinWeight" + ], + "title": "tfSmoothSkinWeight", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "characterAutorigger" + ], + "title": "characterAutorigger", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "jointsOnCurve" + ], + "title": "jointsOnCurve", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "parentScaleConstraint" + ], + "title": "parentScaleConstraint", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "rigRoss" + ], + "title": "rigRoss", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "createCenterLocator" + ], + "title": "createCenterLocator", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "setJointLabels" + ], + "title": "setJointLabels", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "cbSmoothSkinWeightUI" + ], + "title": "cbSmoothSkinWeightUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "rigging", + "toggleIntermediates" + ], + "title": "toggleIntermediates", + "tooltip": "" + } + ], -"MEL": [{"title" : "Create cube", - "tooltip": "Launch character rigging tool", - "command": "polyCube -w 1 -h 1 -d 1;", - "sourcetype": "mel", - "tags": ["test", "script", "mel"]} - ] -} \ No newline at end of file + "Shading": [ + { + "command": "", + "sourcetype": "file", + "tags": ["shading", "vray"], + "title": "VRay", + "tooltip": "", + "items": [ + { + "title": "Import Proxies", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayImportProxies.py", + "sourcetype": "file", + "tags": ["shading", "vray", "import","proxies"], + "tooltip": "" + }, + {"title": "separator"}, + { + "title": "Select All GES", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\selectAllGES.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "selectAllGES.py"] + }, + { + "title": "Select All GES Under Selection", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\selectAllGESUnderSelection.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "selection","all", "GES"] + }, + {"title": "separator"}, + { + "title": "Selection To VRay Mesh", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\selectionToVrayMesh.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "selection","vraymesh"] + }, + { + "title": "Add VRay Round Edges Attribute", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayRoundEdgesAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "round edges", "attribute"] + }, + { + "title": "Add Gamma", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayAddGamma.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "add gamma"] + }, + {"title": "separator"}, + { + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\select_vraymesh_materials_with_unconnected_shader_slots.py", + "sourcetype": "file", + "title": "Select Unconnected Shader Materials", + "tags": ["shading", "vray", "select", "vraymesh", "materials", "unconnected shader slots"], + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayMergeSimilarVRayMeshMaterials.py", + "sourcetype": "file", + "title": "Merge Similar VRay Mesh Materials", + "tags": ["shading", "vray", "Merge","VRayMesh", "Materials"], + "tooltip": "" + }, + { + "title": "Create Two Sided Material", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayCreate2SidedMtlForSelectedMtlRenamed.py", + "sourcetype": "file", + "tooltip": "Creates two sided material for selected material and renames it", + "tags": ["shading", "vray", "two sided", "material"] + }, + { + "title": "Create Two Sided Material For Selected", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayCreate2SidedMtlForSelectedMtl.py", + "sourcetype": "file", + "tooltip": "Select material to create a two sided version from it", + "tags": ["shading", "vray", "Create2SidedMtlForSelectedMtl.py"] + }, + {"title": "separator"}, + { + "title": "Add OpenSubdiv Attribute", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayOpenSubdivAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "add", "open subdiv", "attribute"] + }, + { + "title": "Remove OpenSubdiv Attribute", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\removeVrayOpenSubdivAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "remove","opensubdiv","attributee"] + }, + {"title": "separator"}, + { + "title": "Add Subdivision Attribute", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVraySubdivisionAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "addVraySubdivisionAttribute"] + }, + { + "title": "Remove Subdivision Attribute.py", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\removeVraySubdivisionAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "remove","subdivision", "attribute"] + }, + {"title": "separator"}, + { + "title": "Add Vray Object Ids", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayObjectIds.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "add", "object id"] + }, + { + "title": "Add Vray Material Ids", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayMaterialIds.py", + "sourcetype": "file", + "tooltip": "", + "tags": [ + "shading", + "vray", + "addVrayMaterialIds.py" + ] + }, + {"title": "separator"}, + { + "title": "Set Physical DOF Depth", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayPhysicalDOFSetDepth.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "physical","DOF ","Depth"] + }, + { + "title": "Magic Vray Proxy UI", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\magicVrayProxyUI.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", "vray", "magicVrayProxyUI"] + } + ] + }, + { + "command": "$COLORBLEED_SCRIPTS\\shading\\LightLinkUI.py", + "sourcetype": "file", + "tags": [ + "shading", + "LightLinkUI" + ], + "title": "Light Link UI", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\shading\\setTexturePreviewToCLRImage.py", + "sourcetype": "file", + "tags": ["shading", "CLRImage", "textures", "preview"], + "title": "Set Texture Preview To CLRImage", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\shading\\fixDefaultShaderSetBehavior", + "sourcetype": "file", + "tags": ["shading", "fix", "DefaultShaderSet", "Behavior"], + "title": "fixDefaultShaderSetBehavior", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\shading\\fixSelectedShapesReferenceAssignments.py", + "sourcetype": "file", + "tags": [ + "shading", + "fixSelectedShapesReferenceAssignments" + ], + "title": "Fix Shapes Reference Assignments", + "tooltip": "Select shapes to fix the reference assignments" + }, + { + "command": "$COLORBLEED_SCRIPTS\\shading\\selectLambert1Members.py", + "sourcetype": "file", + "tags": [ + "shading", + "selectLambert1Members" + ], + "title": "Select Lambert1 Members", + "tooltip": "Selects all objects which have the Lambert1 shader assigned" + }, + { + "command": "$COLORBLEED_SCRIPTS\\shading\\selectShapesWithoutShader.py", + "sourcetype": "file", + "tags": [ + "shading", + "selectShapesWithoutShader" + ], + "title": "Select Shapes Without Shader", + "tooltip": "" + }, + { + "command": "$COLORBLEED_SCRIPTS\\shading\\fixRenderLayerOutAdjustmentErrors.py", + "sourcetype": "file", + "tags": [ + "shading", + "fixRenderLayerOutAdjustmentErrors" + ], + "title": "Fix RenderLayerOut Adjustment Errors", + "tooltip": "" + } + ], + + "Layout": [ + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "alignDistributeUI" + ], + "title": "alignDistributeUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "alignSimpleUI" + ], + "title": "alignSimpleUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "center_locator" + ], + "title": "center_locator", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "average_locator" + ], + "title": "average_locator", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "selectWithinProximityUI" + ], + "title": "selectWithinProximityUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "dupCurveUI" + ], + "title": "dupCurveUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "randomDeselectUI" + ], + "title": "randomDeselectUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "multiReferencerUI" + ], + "title": "multiReferencerUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "duplicateOffsetUI" + ], + "title": "duplicateOffsetUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "spPaint3d" + ], + "title": "spPaint3d", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "randomizeUI" + ], + "title": "randomizeUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "layout", + "distributeWithinObjectUI" + ], + "title": "distributeWithinObjectUI", + "tooltip": "" + } + ], + + "Particles": [ + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "instancerToObjects" + ], + "title": "instancerToObjects", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "instancerToObjectsInstances" + ], + "title": "instancerToObjectsInstances", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "objectsToParticlesAndInstancerCleanSource" + ], + "title": "objectsToParticlesAndInstancerCleanSource", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "particleComponentsToLocators" + ], + "title": "particleComponentsToLocators", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "objectsToParticlesAndInstancer" + ], + "title": "objectsToParticlesAndInstancer", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "spawnParticlesOnMesh" + ], + "title": "spawnParticlesOnMesh", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "instancerToObjectsInstancesWithAnimation" + ], + "title": "instancerToObjectsInstancesWithAnimation", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "objectsToParticles" + ], + "title": "objectsToParticles", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "add_particle_cacheFile_attrs" + ], + "title": "add_particle_cacheFile_attrs", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "mergeParticleSystems" + ], + "title": "mergeParticleSystems", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "particlesToLocators" + ], + "title": "particlesToLocators", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "instancerToObjectsWithAnimation" + ], + "title": "instancerToObjectsWithAnimation", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "killSelectedParticles" + ], + "title": "killSelectedParticles", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "particles", + "clearInitialState" + ], + "title": "clearInitialState", + "tooltip": "" + } + ], + + "Cleanup": [ + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "selectIntermediateObjects" + ], + "title": "selectIntermediateObjects", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "resetViewportCache" + ], + "title": "resetViewportCache", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "selectNonUniqueNames" + ], + "title": "selectNonUniqueNames", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "uniqifyNodeNames" + ], + "title": "uniqifyNodeNames", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "selectByType" + ], + "title": "selectByType", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "removeNamespaces" + ], + "title": "removeNamespaces", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "autoRenameFileNodes" + ], + "title": "autoRenameFileNodes", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "remove_user_defined_attributes" + ], + "title": "remove_user_defined_attributes", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "removeUnknownNodes" + ], + "title": "removeUnknownNodes", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "colorbleedRename" + ], + "title": "colorbleedRename", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "removeUnloadedReferences" + ], + "title": "removeUnloadedReferences", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "referenceEditsUI" + ], + "title": "referenceEditsUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "renameShapesToTransform" + ], + "title": "renameShapesToTransform", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "removeReferencesFailedEdits" + ], + "title": "removeReferencesFailedEdits", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "reorderUI" + ], + "title": "reorderUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "pastedCleaner" + ], + "title": "pastedCleaner", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "cleanup", + "deleteGhostIntermediateObjects" + ], + "title": "deleteGhostIntermediateObjects", + "tooltip": "" + } + ], + + "Projects": [ + { + "command": "", + "sourcetype": "file", + "tags": [ + "projects", + "fifa" + ], + "title": "fifa", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "projects", + "beakbuds" + ], + "title": "beakbuds", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "projects", + "redeemer" + ], + "title": "redeemer", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "projects", + "bjorn" + ], + "title": "bjorn", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "projects", + "aldi" + ], + "title": "aldi", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "projects", + "eneco" + ], + "title": "eneco", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "projects", + "duurzame_verpakking" + ], + "title": "duurzame_verpakking", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "projects", + "bunch" + ], + "title": "bunch", + "tooltip": "" + } + ], + + "Pyblish": [ + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "layout" + ], + "title": "layout", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "look" + ], + "title": "look", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "submit_to_deadline" + ], + "title": "submit_to_deadline", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "instance_creator" + ], + "title": "instance_creator", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "utilities" + ], + "title": "utilities", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "loader" + ], + "title": "loader", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "animation" + ], + "title": "animation", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "lighting" + ], + "title": "lighting", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "inventory_ui" + ], + "title": "inventory_ui", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "pyblish", + "explorer" + ], + "title": "explorer", + "tooltip": "" + } + ], + + "Others": [ + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "instanceSmartTransform" + ], + "title": "instanceSmartTransform", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "archiveSceneUI" + ], + "title": "archiveSceneUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "getSimilarMeshes" + ], + "title": "getSimilarMeshes", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "createBoundingBoxEachSelected" + ], + "title": "createBoundingBoxEachSelected", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "curveFromPositionEveryFrame" + ], + "title": "curveFromPositionEveryFrame", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "hair" + ], + "title": "hair", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "selectSoftSelection" + ], + "title": "selectSoftSelection", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "instanceLeafSmartTransform" + ], + "title": "instanceLeafSmartTransform", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "randomizeUVShellsSelectedObjects" + ], + "title": "randomizeUVShellsSelectedObjects", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "centerPivotGroup" + ], + "title": "centerPivotGroup", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "locatorsOnSelectedFaces" + ], + "title": "locatorsOnSelectedFaces", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "display" + ], + "title": "display", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "copyDeformers" + ], + "title": "copyDeformers", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "locatorsOnEdgeSelectionPrompt" + ], + "title": "locatorsOnEdgeSelectionPrompt", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "yeti" + ], + "title": "yeti", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "selectInReferenceEditor" + ], + "title": "selectInReferenceEditor", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "selectConstrainingObject" + ], + "title": "selectConstrainingObject", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "deformerSetRelationsUI" + ], + "title": "deformerSetRelationsUI", + "tooltip": "" + }, + { + "command": "", + "sourcetype": "file", + "tags": [ + "others", + "recreateBaseNodesForAllLatticeNodes" + ], + "title": "recreateBaseNodesForAllLatticeNodes", + "tooltip": "" + } + ] +} diff --git a/colorbleed/maya/menu.py b/colorbleed/maya/menu.py index f72dc07ed9..5349284e63 100644 --- a/colorbleed/maya/menu.py +++ b/colorbleed/maya/menu.py @@ -1,90 +1,64 @@ import sys +import os +import logging +import site from avalon.vendor.Qt import QtWidgets, QtCore +import maya.cmds as cmds + self = sys.modules[__name__] self._menu = "colorbleed" -self._parent = {widget.objectName(): widget for widget in - QtWidgets.QApplication.topLevelWidgets()}.get("MayaWindow") + +# set colorbleed scripts path in environment keys +os.environ["COLORBLEED_SCRIPTS"] = r"P:\pipeline\dev\git\cbMayaScripts\cbMayaScripts" + +log = logging.getLogger(__name__) -def install(): - # from . import interactive +def deferred(): - uninstall() + # todo: replace path with server / library path + site.addsitedir("C:\Users\User\Documents\development\scriptsmenu\python") - def deferred(): + from scriptsmenu import launchformaya + import scriptsmenu.scriptsmenu as menu - import site - import os + log.info("Attempting to install ...") - # todo: replace path with server / library path - site.addsitedir("C:\Users\User\Documents\development\scriptsmenu\python") + # load configuration of custom menu + config_path = os.path.join(os.path.dirname(__file__), "menu.json") + config = menu.load_configuration(config_path) - from scriptsmenu import launchformaya - import scriptsmenu.scriptsmenu as menu + # hack? + parent = launchformaya._maya_main_menubar() + cb_menu = menu.ScriptsMenu(title=self._menu.title(), parent=parent) - # load configuration of custon menu - config_path = os.path.join(os.path.dirname(__file__), "menu.json") - config = menu.load_configuration(config_path) + # register modifiers + modifiers = QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier + cb_menu.register_callback(modifiers, launchformaya.to_shelf) - # create menu in appliction - cb_menu = launchformaya.main(title=self._menu, parent=self._parent) - - # apply configuration - menu.load_from_configuration(cb_menu, config) - - # cmds.menu(self._menu, - # label=self._menu.capitalize(), - # tearOff=True, - # parent="MayaWindow") - # - # # Modeling sub-menu - # cmds.menuItem("Modeling", - # label="Modeling", - # tearOff=True, - # subMenu=True, - # parent=self._menu) - # - # cmds.menuItem("Combine", command=interactive.combine) - # - # # Rigging sub-menu - # cmds.menuItem("Rigging", - # label="Rigging", - # tearOff=True, - # subMenu=True, - # parent=self._menu) - # - # cmds.menuItem("Auto Connect", command=interactive.auto_connect) - # cmds.menuItem("Clone (Local)", command=interactive.clone_localspace) - # cmds.menuItem("Clone (World)", command=interactive.clone_worldspace) - # cmds.menuItem("Clone (Special)", command=interactive.clone_special) - # cmds.menuItem("Create Follicle", command=interactive.follicle) - # - # # Animation sub-menu - # cmds.menuItem("Animation", - # label="Animation", - # tearOff=True, - # subMenu=True, - # parent=self._menu) - # - # cmds.menuItem("Set Defaults", command=interactive.set_defaults) - # - # cmds.setParent("..", menu=True) - # - # cmds.menuItem(divider=True) - # - # cmds.menuItem("Auto Connect", command=interactive.auto_connect_assets) - - # Allow time for uninstallation to finish. - QtCore.QTimer.singleShot(100, deferred) + # apply configuration + menu.load_from_configuration(cb_menu, config) def uninstall(): + + log.info("Attempting to uninstall ..") app = QtWidgets.QApplication.instance() widgets = dict((w.objectName(), w) for w in app.allWidgets()) menu = widgets.get(self._menu) if menu: - menu.deleteLater() - del(menu) + try: + menu.deleteLater() + del menu + except Exception as e: + log.error(e) + + +def install(): + + uninstall() + # Allow time for uninstallation to finish. + cmds.evalDeferred(deferred) diff --git a/colorbleed/plugins/maya/publish/__collect_instance_per_item.py b/colorbleed/plugins/maya/publish/__collect_instance_per_item.py deleted file mode 100644 index 7dd70d444a..0000000000 --- a/colorbleed/plugins/maya/publish/__collect_instance_per_item.py +++ /dev/null @@ -1,177 +0,0 @@ -from collections import defaultdict - -from maya import cmds - -import cbra.utils.maya.node_uuid as node_uuid -import cbra.lib - -import pyblish.api - - -class CollectInstancePerItem(pyblish.api.ContextPlugin): - """Collect instances from the Maya scene and breaks them down per item id - - An instance is identified by having an _INST suffix - and a .family user-defined attribute. - - All other user-defined attributes of the object set - is accessible within each instance's data. - - This collector breaks the instances down to each Item member it contains, - by using the IDs on the nodes in the instance it will split up the instance - into separate instances for each unique "item" id it finds. - - Note: - - Only breaks down based on children members and ignores parent members. - - Discards members without IDs. - - """ - - order = pyblish.api.CollectorOrder + 0.1 - hosts = ["maya"] - label = "Instance per Item" - - _include_families = ["colorbleed.look"] - - def process(self, context): - - invalid = list() - - for objset in cmds.ls("*_SET", - objectsOnly=True, - type='objectSet', - long=True, - recursive=True): # Include namespace - - try: - family = cmds.getAttr("{}.family".format(objset)) - except ValueError: - self.log.error("Found: %s found, but no family." % objset) - continue - - if family not in self._include_families: - continue - - # ignore referenced sets - if cmds.referenceQuery(objset, isNodeReferenced=True): - continue - - instances = self.build_instances(context, objset) - if not instances: - - # Log special error messages when objectSet is completely - # empty (has no members) to clarify to artists the root of - # their problem. - if not cmds.sets(objset, query=True): - self.log.error("Instance objectSet has no members: " - "{}".format(objset)) - - self.log.error("No instances retrieved from objectSet: " - "{}".format(objset)) - invalid.append(objset) - - if invalid: - raise RuntimeError("Invalid instances: {}".format(invalid)) - - # Sort context based on family - context[:] = sorted( - context, key=lambda instance: instance.data("family")) - - def build_instances(self, context, objset): - """Build the instances for a single instance objectSet - - Returns: - list: The constructed instances from the objectSet. - - """ - - self.log.info("Collecting: %s" % objset) - - short_name = objset.rsplit("|", 1)[-1].rsplit(":", 1)[-1] - - # Default data - default_data = {"name": short_name[:-5], - "subset": "default"} - - # Get user data from user defined attributes - user_data = dict() - for attr in cmds.listAttr(objset, userDefined=True): - try: - value = cmds.getAttr("{}.{}".format(objset, attr)) - user_data[attr] = value - except RuntimeError: - continue - - # Maintain nested object sets - members = cmds.sets(objset, query=True) - members = cmds.ls(members, long=True) - - children = cmds.listRelatives(members, - allDescendents=True, - fullPath=True) or [] - - # Exclude intermediate objects - children = cmds.ls(children, noIntermediate=True, long=True) - - nodes = members + children - nodes = list(set(nodes)) - - # Group nodes using ids to an Item - nodes_id = node_uuid.build_cache(nodes, include_without_ids=True) - - # Log warning for nodes without ids - if None in nodes_id: - self.log.warning("Skipping nodes without ids: " - "{}".format(nodes_id[None])) - - # ignore nodes without ids - context.data["instancePerItemNodesWithoutId"] = nodes_id.pop(None, - None) - - item_groups = defaultdict(list) - - for id, nodes in nodes_id.iteritems(): - item_id = id.rsplit(":", 1)[0] - item_groups[item_id].extend(nodes) - - instances = list() - for item_id, item_nodes in item_groups.iteritems(): - - ctx = node_uuid.parse_id("{}:fake_node_uuid".format(item_id)) - - # Use itemPath to parse full blown context using official lib - ctx = cbra.lib.parse_context(ctx['itemPath']) - - item = ctx.get('item', None) - if item is None: - self.log.info("Unparsed item id: {}".format(item_id)) - self.log.error("Item can't be parsed and seems to be " - "non-existent. Was an asset renamed? Or your" - "project set incorrectly?") - raise RuntimeError("Item not parsed. See log for description.") - - instance = context.create_instance(objset) - - # Set the related members - instance[:] = item_nodes - instance.data['setMembers'] = item_nodes - - # Set defaults and user data - instance.data.update(default_data.copy()) - instance.data.update(user_data.copy()) - - # Override the label to be clear - name = instance.data['name'] - instance.data['label'] = "{0} ({1})".format(name, item) - - # Store that the instance is collected per item - instance.data['_instancePerItem'] = True - instance.data['_itemContext'] = ctx - - assert "family" in instance.data, "No family data in instance" - assert "name" in instance.data, ("No objectSet name data " - "in instance") - - instances.append(instance) - - return instances diff --git a/colorbleed/plugins/maya/publish/__collect_yeti_caches.py b/colorbleed/plugins/maya/publish/__collect_yeti_caches.py deleted file mode 100644 index a236836cdf..0000000000 --- a/colorbleed/plugins/maya/publish/__collect_yeti_caches.py +++ /dev/null @@ -1,156 +0,0 @@ -import os -import re -import pyseq -import glob - -import pyblish.api - -from maya import cmds - - -class SeletYetiCachesAction(pyblish.api.Action): - """Select the nodes related to the collected file textures""" - - label = "Select yeti nodes" - on = "succeeded" # This action is only available on a failed plug-in - icon = "search" # Icon from Awesome Icon - - def process(self, context, plugin): - - self.log.info("Finding textures..") - - # Get the errored instances - instances = [] - for result in context.data["results"]: - instance = result["instance"] - if instance is None: - continue - - instances.append(instance) - - # Apply pyblish.logic to get the instances for the plug-in - instances = pyblish.api.instances_by_plugin(instances, plugin) - - # Get the texture nodes from the instances - nodes = [] - for instance in instances: - texture_nodes = instance.data['yetiCaches'].keys() - nodes.extend(texture_nodes) - - # Ensure unique - nodes = list(set(nodes)) - - if nodes: - self.log.info("Selecting nodes: %s" % ", ".join(nodes)) - cmds.select(nodes, r=True, noExpand=True) - else: - self.log.info("No nodes found.") - cmds.select(deselect=True) - - -def get_sequence(filename, pattern="%04d"): - """Get pyseq sequence from filename - - Supports negative frame ranges like (-001, 0000, 0001 and -0001, 0000, 0001). - - Arguments: - filename (str): The full path to filename containing the given pattern. - pattern (str): The pattern to swap with the variable frame number. - - Returns: - pyseq.Sequence: file sequence. - - """ - - glob_pattern = filename.replace(pattern, "*") - - escaped = re.escape(filename) - re_pattern = escaped.replace(pattern, "-?[0-9]+") - - files = glob.glob(glob_pattern) - files = [str(f) for f in files if re.match(re_pattern, f)] - - return pyseq.get_sequences(files) - - -class CollectYetiCaches(pyblish.api.InstancePlugin): - """Collect used yeti caches. - - Collects the file sequences from pgYetiMaya.cacheFileName - - """ - - order = pyblish.api.CollectorOrder + 0.495 - label = 'Yeti Caches' - families = ["colorbleed.groom"] - actions = [SeletYetiCachesAction] - - TYPES = {"pgYetiMaya": "cacheFileName"} - - def process(self, instance): - - # Get textures from sets - members = instance.data("setMembers") - members = cmds.ls(members, dag=True, shapes=True, type="pgYetiMaya", - noIntermediate=True, long=True) - if not members: - raise RuntimeError("Instance appears to be empty (no members)") - - # Collect only those cache frames that are required - # If handles are required it is assumed to already be included - # in the start frame and end frames. - # (e.g. using frame handle collector) - start_frame = instance.data("startFrame") - end_frame = instance.data("endFrame") - required = set(range(int(start_frame), int(end_frame) + 1)) - - history = cmds.listHistory(members) or [] - - resources = instance.data.get("resources", []) - yeti_caches = dict() - - for node_type, attr in self.TYPES.iteritems(): - for node in cmds.ls(history, type=node_type, long=True): - - attribute = "{0}.{1}".format(node, attr) - - # Source - source = cmds.getAttr(attribute) - if not source: - self.log.error("Node does not have a file set: " - "{0}".format(node)) - - # Collect the source as expanded path because that's also - # how the attribute must be 'set' for yeti nodes. - source = os.path.realpath(cmds.workspace(expandName=source)) - - # Collect the frames we need from the sequence - sequences = get_sequence(source) - files = list() - for sequence in sequences: - for index, frame in enumerate(sequence.frames()): - if frame not in required: - continue - - item = sequence[index] - files.append(item.path) - - # Define the resource - resource = {"tags": ["maya", "yeti", "attribute"], - "node": node, - "attribute": attribute, - "source": source, # required for resources - "files": files, # required for resources - "subfolder": "caches" # optional for resources - } - - resources.append(resource) - - # For validations - yeti_caches[node] = {"attribute": attribute, - "source": source, - "sequences": sequences} - - # Store data on instance - instance.data['yetiCaches'] = yeti_caches - instance.data['resources'] = resources diff --git a/colorbleed/plugins/maya/publish/__extract_layout.py b/colorbleed/plugins/maya/publish/__extract_layout.py deleted file mode 100644 index 823337b2e1..0000000000 --- a/colorbleed/plugins/maya/publish/__extract_layout.py +++ /dev/null @@ -1,81 +0,0 @@ -import json - -from maya import cmds - -import avalon.maya -import colorbleed.api - -import cb.utils.maya.context as context -import cbra.utils.maya.layout as layout - - -def get_upstream_hierarchy_fast(nodes): - """Passed in nodes must be long names!""" - - matched = set() - parents = [] - - for node in nodes: - hierarchy = node.split("|") - num = len(hierarchy) - for x in range(1, num-1): - parent = "|".join(hierarchy[:num-x]) - if parent in parents: - break - else: - parents.append(parent) - matched.add(parent) - - return parents - - -class ExtractLayout(colorbleed.api.Extractor): - """Extract Layout as both gpuCache and Alembic""" - - label = "Layout (gpuCache & alembic)" - hosts = ["maya"] - families = ["colorbleed.layout"] - - def process(self, instance): - - # Define extract output file path - dir_path = self.staging_dir(instance) - - start = instance.data.get("startFrame", 1) - end = instance.data.get("endFrame", 1) - step = instance.data.get("step", 1.0) - placeholder = instance.data.get("placeholder", False) - write_color_sets = instance.data.get("writeColorSets", False) - renderable_only = instance.data.get("renderableOnly", False) - visible_only = instance.data.get("visibleOnly", False) - - layers = instance.data.get("animLayersActive", None) - if layers: - layers = json.loads(layers) - self.log.info("Publishing with animLayers active: " - "{0}".format(layers)) - - # Perform extraction - self.log.info("Performing extraction..") - with avalon.maya.maintained_selection(): - - # Get children hierarchy - nodes = instance.data['setMembers'] - cmds.select(nodes, r=True, hierarchy=True) - hierarchy = cmds.ls(selection=True, long=True) - - with context.evaluation("off"): - with context.no_refresh(): - with context.active_anim_layers(layers): - layout.extract_layout(hierarchy, - dir_path, - start=start, - end=end, - step=step, - placeholder=placeholder, - write_color_sets=write_color_sets, - renderable_only=renderable_only, - visible_only=visible_only) - - self.log.info("Extracted instance '{0}' to: {1}".format( - instance.name, dir_path)) diff --git a/colorbleed/plugins/maya/publish/__validate_layout_nodes.py b/colorbleed/plugins/maya/publish/__validate_layout_nodes.py deleted file mode 100644 index fda17e4433..0000000000 --- a/colorbleed/plugins/maya/publish/__validate_layout_nodes.py +++ /dev/null @@ -1,91 +0,0 @@ -import os - -import maya.cmds as cmds - -import pyblish.api -import colorbleed.api - -import cbra.lib -from cb.utils.python.decorators import memorize - - -def isclose(a, b, rel_tol=1e-9, abs_tol=0.0): - return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) - - -@memorize -def is_published_path(path): - """Return whether path is from a published file""" - - # Quick check (optimization) without going through the folder - # structure - if cbra.lib.DIR_PUBLISH.lower() not in path.lower(): - return False - - try: - context = cbra.lib.parse_context(path) - except RuntimeError: - context = dict() - - return all([context.get("family", None), - context.get("subset", None), - context.get("version", None)]) - - -class ValidateLayoutNodes(pyblish.api.InstancePlugin): - """Validates that layout nodes behave to certain rules - - Gpu caches in a layout may not have sub-frame offsets, like offsets with a - value after the decimal point. (e.g. 1.45) - - Gpu caches loaded in a layout MUST come from a published source that has - family and version. - - """ - - order = colorbleed.api.ValidateContentsOrder - label = 'Layout Nodes' - families = ['colorbleed.layout'] - actions = [colorbleed.api.SelectInvalidAction] - - @classmethod - def get_invalid(cls, instance): - - caches = cmds.ls(instance, type="gpuCache", long=True) - - # Validate sub-frame offsets - invalid_offsets = list() - for cache in caches: - - offset = cmds.getAttr("{}.animOffset".format(cache)) - if not isclose(offset, round(offset)): - cls.log.warning("Invalid sub-frame offset on: %s" % cache) - invalid_offsets.append(cache) - - # Validate gpuCache paths are from published files - invalid_paths = list() - for cache in caches: - path = cmds.getAttr("{}.cacheFileName".format(cache)) - path = os.path.normpath(path) - if not is_published_path(path): - cls.log.warning("GpuCache path not from published file: " - "{0} -> {1}".format(cache, path)) - invalid_paths.append(cache) - - invalid = invalid_offsets + invalid_paths - - return invalid - - def process(self, instance): - - # Clear cache only once per publish. So we store a value on - # the context on the first instance so we clear only once. - name = self.__class__.__name__ - key = "_plugin_{0}_processed".format(name) - if not instance.context.data.get(key, False): - is_published_path.cache.clear() - instance.context.data[key] = True - - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError("Invalid nodes found: {0}".format(invalid)) diff --git a/colorbleed/plugins/maya/publish/__validate_related_node_ids.py b/colorbleed/plugins/maya/publish/__validate_related_node_ids.py deleted file mode 100644 index a79138168d..0000000000 --- a/colorbleed/plugins/maya/publish/__validate_related_node_ids.py +++ /dev/null @@ -1,83 +0,0 @@ -import pyblish.api -import colorbleed.api - -import cbra.utils.maya.node_uuid as id_utils -import cbra.lib - - -class ValidateRelatedNodeIds(pyblish.api.InstancePlugin): - """Validate nodes have related colorbleed ids. - - An ID is 'related' if its built in the current Item. - - Note that this doesn't ensure it's from the current Task. An ID created - from `lookdev` has the same relation to the Item as one coming from others, - like `rigging` or `modeling`. - - """ - - order = colorbleed.api.ValidatePipelineOrder - families = ['colorbleed.model'] - hosts = ['maya'] - label = 'Related Id Attributes' - actions = [colorbleed.api.SelectInvalidAction, - colorbleed.api.GenerateUUIDsOnInvalidAction] - - @classmethod - def get_invalid(cls, instance): - """Return the member nodes that are invalid""" - - context = instance.context - current_file = context.data.get('currentFile', None) - if not current_file: - raise RuntimeError("No current file information: " - "{0}".format(current_file)) - - try: - context = cbra.lib.parse_context(current_file) - except RuntimeError, e: - cls.log.error("Can't generate UUIDs because scene isn't " - "in new-style pipeline: ".format(current_file)) - raise e - - def to_item(id): - """Split the item id part from a node id""" - return id.rsplit(":", 1)[0] - - # Generate a fake id in the current context to retrieve the item - # id prefix that should match with ids on the nodes - fake_node = "__node__" - ids = id_utils.generate_ids(context, [fake_node]) - id = ids[fake_node] - item_prefix = to_item(id) - - # Take only the ids with more than one member - invalid = list() - invalid_items = set() - for member in instance: - member_id = id_utils.get_id(member) - - # skip nodes without ids - if not member_id: - continue - - if not member_id.startswith(item_prefix): - invalid.append(member) - invalid_items.add(to_item(member_id)) - - # Log invalid item ids - if invalid_items: - for item_id in sorted(invalid_items): - cls.log.warning("Found invalid item id: {0}".format(item_id)) - - return invalid - - def process(self, instance): - """Process all meshes""" - - # Ensure all nodes have a cbId - invalid = self.get_invalid(instance) - - if invalid: - raise RuntimeError("Nodes found with non-related " - "asset IDs: {0}".format(invalid)) diff --git a/colorbleed/plugins/maya/publish/__validate_unique_ids_in_item.py b/colorbleed/plugins/maya/publish/__validate_unique_ids_in_item.py deleted file mode 100644 index 55d3a14377..0000000000 --- a/colorbleed/plugins/maya/publish/__validate_unique_ids_in_item.py +++ /dev/null @@ -1,167 +0,0 @@ -import os -from collections import defaultdict - -import pyblish.api -import colorbleed.api - -import cbra.lib -from cbra.utils.maya.abc import get_alembic_ids -from cbra.utils.maya.node_uuid import get_id - - -def get_subset_path(context): - return os.path.join(context['itemPath'], - cbra.lib.DIR_PUBLISH, - context['family'], - context['subset']) - - -class ValidateUniqueIdsInItem(pyblish.api.InstancePlugin): - """Checks whether IDs are unique across other subsets - - This ensures a model to be published can't have ids - which are already present in another subset. For example - the "default" model can't have ids present in the "high" - subset. - - Note: - This will also invalidate the instance if it contains - nodes that are present in another instance in the scene. - So ensure the instance you're publishing actually has - the correct set members. - - """ - - order = colorbleed.api.ValidateMeshOrder - families = ['colorbleed.model'] - hosts = ['maya'] - label = 'Unique Ids in Item' - actions = [colorbleed.api.SelectInvalidAction] - optional = True - - @classmethod - def iter_invalid(cls, instance): - - verbose = instance.data.get("verbose", False) - - def _get_instance_ids(instance): - """Collect ids in an instance""" - nodes_per_id = defaultdict(list) - for node in instance: - node_id = get_id(node) - if node_id: - nodes_per_id[node_id].append(node) - return nodes_per_id - - nodes_per_id = _get_instance_ids(instance) - if not nodes_per_id: - return - - ids_lookup = set(nodes_per_id.keys()) - - instance_context = instance.data["instanceContext"] - instance_subset = instance.data['subset'] - - assert instance_context, "Instance must have 'instanceContext' data" - assert instance_subset, "Instance must have 'subset' data" - - subsets_checked = set() - subsets_checked.add(instance_subset) # we can skip this subset - - # Compare with all other *currently publishing instances* - # of family 'model' for this item - for other_instance in instance.context: - if other_instance is instance: - continue - - if other_instance.data['subset'] == instance_subset: - cls.log.error("Another instance has the same subset? " - "This should never happen.") - - if other_instance.data['family'] != "model": - continue - - if other_instance.data['instanceContext']['item'] != \ - instance_context['item']: - cls.log.error("Also publishing model for other item? " - "This should never happen.") - continue - other_ids = _get_instance_ids(other_instance).keys() - - # Perform comparison - intersection = ids_lookup.intersection(other_ids) - if intersection: - for node_id in intersection: - nodes = nodes_per_id[node_id] - for node in nodes: - yield node - - # Those that are invalid don't need to be checked again - ids_lookup.difference_update(other_ids) - - if not ids_lookup: - # Once we have no ids to check for anymore we can already - # return - return - - subsets_checked.add(other_instance.data['subset']) - - # Compare with all previously *published instances* - # of family 'model' for this item - ctx = instance_context.copy() - ctx['family'] = "model" - - published_subsets = cbra.lib.list_subsets(ctx) - published_subsets = set(x for x in published_subsets if - x != instance_subset) - - for published_subset in published_subsets: - ctx['subset'] = published_subset - ctx['subsetPath'] = get_subset_path(ctx) - - versions = cbra.lib.list_versions(ctx) - version = cbra.lib.find_highest_version(versions) - if not version: - cls.log.debug("No published version for " - "'model': {0}".format(published_subset)) - continue - - ctx['currentVersion'] = version - publish_abc = cbra.lib.get_filepath(ctx) + ".abc" - - if not os.path.exists(publish_abc): - cls.log.error("Published file to compare with does not exist: " - "{0}".format(publish_abc)) - continue - - if verbose: - cls.log.debug("Comparing with: {0}".format(publish_abc)) - - abc_ids = set(get_alembic_ids(publish_abc).values()) - - # Perform comparison - intersection = ids_lookup.intersection(abc_ids) - if intersection: - for node_id in intersection: - nodes = nodes_per_id[node_id] - for node in nodes: - yield node - - # Those that are invalid don't need to be checked again - ids_lookup.difference_update(abc_ids) - - if not ids_lookup: - # Once we have no ids to check for anymore we can already - # return - return - - return - - @classmethod - def get_invalid(cls, instance): - return list(cls.iter_invalid(instance)) - - def process(self, instance): - """Process all meshes""" - if any(self.iter_invalid(instance)): - raise RuntimeError("Invalid nodes found in {0}".format(instance)) diff --git a/colorbleed/plugins/maya/publish/_integrate_cb_asset.py b/colorbleed/plugins/maya/publish/_integrate_cb_asset.py deleted file mode 100644 index 61535e58db..0000000000 --- a/colorbleed/plugins/maya/publish/_integrate_cb_asset.py +++ /dev/null @@ -1,85 +0,0 @@ -import os -import shutil - -import pyblish_cb.lib -import colorbleed.api - - -class IntegrateColorbleedAssets(colorbleed.api.Integrator): - """Name and position instances on disk for instances. - - The files are transferred from the `extractDir` to the - computed `integrationDir` and are renamed as: - - "{item}_{family}_{subsetName}_{version}.{ext}" - - Assumptions: - - Each extracted instance is 1 file (no directories) - - """ - - label = "Asset" - families = ["colorbleed.model", "colorbleed.rig", "colorbleed.pointcache", - "colorbleed.proxy", "colorbleed.layout", "colorbleed.look", - "colorbleed.vrmeshReplace", "colorbleed.review", - "colorbleed.instancer", "colorbleed.camera", - "colorbleed.mayaAscii", - "colorbleed.furYeti"] - - def process(self, instance): - super(IntegrateColorbleedAssets, self).process(instance) - - self.log.info("Integrating {0}..".format(instance)) - - integration = pyblish_cb.lib.compute_integration(instance) - - # Store reference for upcoming plug-ins - instance.data["integrationDir"] = integration['path'] - instance.data["integrationVersion"] = integration['versionNum'] - - path = integration['path'] - data = integration.copy() - - try: - if not os.path.exists(path): - os.makedirs(path) - - self.log.info("Moving files to %s" % path) - - tmp = instance.data["extractDir"] - for src in (os.path.join(tmp, f) for f in os.listdir(tmp)): - - self.log.debug("Integrating %s" % src) - - # Source must be a file - if not os.path.isfile(src): - self.log.error("Source is not a file: {0}".format(src)) - continue - - # TODO(marcus): Consider files without extension - data["ext"] = src.split(".", 1)[-1] - dst = os.path.join(path, "{item}_" - "{family}_" - "{subsetName}_" - "{version}.{ext}".format( - **data)) - - # Copy - self.log.info("\"%s\" -> \"%s\"" % (src, dst)) - shutil.copyfile(src, dst) - - self.log.debug("Tagged %s with .Version" % path) - - try: - subset_path = os.path.dirname(path) - cquery.tag(subset_path, ".Subset") - self.log.debug("Tagged %s with .Subset" % subset_path) - except cquery.TagExists: - pass - - except OSError as e: - # If, for whatever reason, this instance did not get written. - instance.data.pop("integrationDir") - raise e - - except Exception as e: - raise Exception("An unknown error occured: %s" % e)