├── plug-ins ├── linux-maya2014 │ └── boneToMesh.so ├── linux-maya2015 │ └── boneToMesh.so ├── linux-maya2016 │ └── boneToMesh.so ├── linux-maya2017 │ └── boneToMesh.so ├── windows-maya2014 │ └── boneToMesh.mll ├── windows-maya2015 │ └── boneToMesh.mll ├── windows-maya2016 │ └── boneToMesh.mll └── windows-maya2017 │ └── boneToMesh.mll ├── readme.MD ├── .gitignore ├── CMakeLists.txt ├── LICENSE └── src ├── boneToMeshNode.h ├── boneToMeshCmd.h ├── pluginMain.cpp ├── boneToMesh.h ├── boneToMeshNode.cpp ├── boneToMesh.cpp └── boneToMeshCmd.cpp /plug-ins/linux-maya2014/boneToMesh.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yantor3d/boneToMesh/HEAD/plug-ins/linux-maya2014/boneToMesh.so -------------------------------------------------------------------------------- /plug-ins/linux-maya2015/boneToMesh.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yantor3d/boneToMesh/HEAD/plug-ins/linux-maya2015/boneToMesh.so -------------------------------------------------------------------------------- /plug-ins/linux-maya2016/boneToMesh.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yantor3d/boneToMesh/HEAD/plug-ins/linux-maya2016/boneToMesh.so -------------------------------------------------------------------------------- /plug-ins/linux-maya2017/boneToMesh.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yantor3d/boneToMesh/HEAD/plug-ins/linux-maya2017/boneToMesh.so -------------------------------------------------------------------------------- /plug-ins/windows-maya2014/boneToMesh.mll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yantor3d/boneToMesh/HEAD/plug-ins/windows-maya2014/boneToMesh.mll -------------------------------------------------------------------------------- /plug-ins/windows-maya2015/boneToMesh.mll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yantor3d/boneToMesh/HEAD/plug-ins/windows-maya2015/boneToMesh.mll -------------------------------------------------------------------------------- /plug-ins/windows-maya2016/boneToMesh.mll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yantor3d/boneToMesh/HEAD/plug-ins/windows-maya2016/boneToMesh.mll -------------------------------------------------------------------------------- /plug-ins/windows-maya2017/boneToMesh.mll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yantor3d/boneToMesh/HEAD/plug-ins/windows-maya2017/boneToMesh.mll -------------------------------------------------------------------------------- /readme.MD: -------------------------------------------------------------------------------- 1 | #### boneToMesh 2 | Maya plugin for easily creating proxy geometry to use in a rig. 3 | 4 | #### Description 5 | See the [Wiki](https://github.com/yantor3d/boneToMesh/wiki) for full details. 6 | 7 | ## Plugin Contents 8 | ### Commands 9 | - boneToMesh 10 | 11 | ### Nodes 12 | - boneToMesh -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # user defined 2 | [Bb]uild 3 | .vscode 4 | *.bat 5 | tests 6 | 7 | # Prerequisites 8 | *.d 9 | 10 | # Compiled Object files 11 | *.slo 12 | *.lo 13 | *.o 14 | *.obj 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Compiled Dynamic libraries 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | *.smod 27 | 28 | # Compiled Static libraries 29 | *.lai 30 | *.la 31 | *.a 32 | *.lib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | # Download Chad Vernon's cgcmake package (https://github.com/chadmv/cgcmake/) 4 | # and make sure your CMAKE_MODULES_PATH environment variable points at it. 5 | 6 | set(CMAKE_MODULE_PATH "$ENV{CMAKE_MODULE_PATH}") 7 | 8 | project(boneToMesh) 9 | file(GLOB SOURCE_FILES "src/*.cpp" "src/*.h") 10 | find_package(Maya REQUIRED) 11 | 12 | include_directories(${MAYA_INCLUDE_DIR}) 13 | link_directories(${MAYA_LIBRARY_DIR}) 14 | 15 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES}) 16 | target_link_libraries(${PROJECT_NAME} ${MAYA_LIBRARIES}) 17 | 18 | MAYA_PLUGIN(${PROJECT_NAME}) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ryan Porter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/boneToMeshNode.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2017 Ryan Porter 3 | You may use, distribute, or modify this code under the terms of the MIT license. 4 | */ 5 | 6 | #ifndef YANTOR_3D_BONE_TO_MESH_NODE_H 7 | #define YANTOR_3D_BONE_TO_MESH_NODE_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | class BoneToMeshNode : public MPxNode 18 | { 19 | public: 20 | static void* creator(); 21 | static MStatus initialize(); 22 | 23 | virtual MStatus compute(const MPlug &plug, MDataBlock &dataBlock); 24 | 25 | private: 26 | virtual MObject unpackComponentList(MObject &componentList); 27 | 28 | public: 29 | static MString NODE_NAME; 30 | static MTypeId NODE_ID; 31 | 32 | private: 33 | static MObject boneLength_attr; 34 | static MObject boneMatrix_attr; 35 | static MObject components_attr; 36 | static MObject direction_attr; 37 | static MObject directionMatrix_attr; 38 | static MObject fillPartialLoops_attr; 39 | static MObject inMesh_attr; 40 | static MObject maxDistance_attr; 41 | static MObject subdivisionsAxis_attr; 42 | static MObject subdivisionsHeight_attr; 43 | static MObject radius_attr; 44 | static MObject useMaxDistance_attr; 45 | 46 | static MObject outMesh_attr; 47 | }; 48 | 49 | #endif -------------------------------------------------------------------------------- /src/boneToMeshCmd.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2017 Ryan Porter 3 | You may use, distribute, or modify this code under the terms of the MIT license. 4 | */ 5 | 6 | #ifndef YANTOR_3D_BONE_TO_MESH_CMD_H 7 | #define YANTOR_3D_BONE_TO_MESH_CMD_H 8 | 9 | #include "boneToMesh.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | class BoneToMeshCommand : public MPxCommand 21 | { 22 | public: 23 | BoneToMeshCommand(); 24 | virtual ~BoneToMeshCommand(); 25 | 26 | static void* creator(); 27 | 28 | static MSyntax getSyntax(); 29 | virtual MStatus parseArguments(MArgDatabase &argsData); 30 | virtual MStatus validateArguments(); 31 | 32 | virtual MStatus doIt(const MArgList& argList); 33 | virtual MStatus redoIt(); 34 | virtual MStatus undoIt(); 35 | 36 | virtual bool isUndoable() const { return true; } 37 | virtual bool hasSyntax() const { return true; } 38 | 39 | private: 40 | virtual void help(); 41 | 42 | public: 43 | static MString COMMAND_NAME; 44 | 45 | private: 46 | MDagPath inMesh; 47 | MObject components; 48 | 49 | MString axis; 50 | MObject boneObj; 51 | 52 | BoneToMeshParams params; 53 | 54 | bool constructionHistory = false; 55 | bool showHelp = false; 56 | bool useMaxDistance = false; 57 | bool useWorldDirection = false; 58 | 59 | MObject undoCreatedMesh; 60 | MObject undoCreatedNode; 61 | }; 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/pluginMain.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2017 Ryan Porter 3 | You may use, distribute, or modify this code under the terms of the MIT license. 4 | */ 5 | 6 | #include "boneToMeshCmd.h" 7 | #include "boneToMeshNode.h" 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | const char* AUTHOR = "Ryan Porter"; 19 | const char* VERSION = "0.3.5"; 20 | const char* REQUIRED_API_VERSION = "Any"; 21 | 22 | 23 | MString BoneToMeshNode::NODE_NAME = "boneToMesh"; 24 | MTypeId BoneToMeshNode::NODE_ID = 0x00126b0f; 25 | 26 | MString BoneToMeshCommand::COMMAND_NAME = "boneToMesh"; 27 | 28 | 29 | MStatus initializePlugin(MObject obj) 30 | { 31 | MStatus status; 32 | MFnPlugin fnPlugin(obj, AUTHOR, VERSION, REQUIRED_API_VERSION); 33 | 34 | status = fnPlugin.registerNode( 35 | BoneToMeshNode::NODE_NAME, 36 | BoneToMeshNode::NODE_ID, 37 | BoneToMeshNode::creator, 38 | BoneToMeshNode::initialize, 39 | MPxNode::kDependNode 40 | ); 41 | 42 | CHECK_MSTATUS_AND_RETURN_IT(status); 43 | 44 | status = fnPlugin.registerCommand( 45 | BoneToMeshCommand::COMMAND_NAME, 46 | BoneToMeshCommand::creator, 47 | BoneToMeshCommand::getSyntax 48 | ); 49 | 50 | CHECK_MSTATUS_AND_RETURN_IT(status); 51 | 52 | return MS::kSuccess; 53 | } 54 | 55 | 56 | MStatus uninitializePlugin(MObject obj) 57 | { 58 | MStatus status; 59 | MFnPlugin fnPlugin(obj, AUTHOR, VERSION, REQUIRED_API_VERSION); 60 | 61 | status = fnPlugin.deregisterNode(BoneToMeshNode::NODE_ID); 62 | CHECK_MSTATUS_AND_RETURN_IT(status); 63 | 64 | status = fnPlugin.deregisterCommand(BoneToMeshCommand::COMMAND_NAME); 65 | CHECK_MSTATUS_AND_RETURN_IT(status); 66 | 67 | return MS::kSuccess; 68 | } -------------------------------------------------------------------------------- /src/boneToMesh.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2017 Ryan Porter 3 | You may use, distribute, or modify this code under the terms of the MIT license. 4 | */ 5 | 6 | #ifndef YANTOR_3D_BONE_TO_MESH_H 7 | #define YANTOR_3D_BONE_TO_MESH_H 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | struct BoneToMeshParams 22 | { 23 | double maxDistance = FLT_MAX; 24 | double boneLength = 1.0; 25 | uint subdivisionsX = 8; 26 | uint subdivisionsY = 4; 27 | int direction = 0; 28 | int fillPartialLoopsMethod = 0; 29 | double radius = 1.0; 30 | }; 31 | 32 | struct BoneToMeshProjection 33 | { 34 | MMatrix boneMatrix; 35 | MMatrix directionMatrix; 36 | MFloatVector directionVector; 37 | MFloatVector projectionVector; 38 | MFloatPoint startPoint; 39 | 40 | MVector::Axis longAxis; 41 | 42 | std::vector raySources; 43 | std::vector rayDirections; 44 | 45 | std::vector indices; 46 | std::vector points; 47 | 48 | int vertexIndex = 0; 49 | int maxVertices = 0; 50 | int maxPolygons = 0; 51 | }; 52 | 53 | MStatus boneToMesh( 54 | const MObject &inMesh, 55 | const MObject &components, 56 | const MMatrix &boneMatrix, 57 | const MMatrix &directionMatrix, 58 | BoneToMeshParams ¶ms, 59 | MObject &outMesh 60 | ); 61 | 62 | MStatus projectionVectors(BoneToMeshParams ¶ms, BoneToMeshProjection &proj); 63 | MStatus projectBoneToMesh(const MObject &inMesh, const MObject &components, BoneToMeshParams ¶ms, BoneToMeshProjection &proj); 64 | MStatus fillPartialLoops(BoneToMeshParams ¶ms, BoneToMeshProjection &proj); 65 | MStatus createMesh(BoneToMeshParams ¶ms, BoneToMeshProjection &proj, MObject &outMesh); 66 | 67 | #endif -------------------------------------------------------------------------------- /src/boneToMeshNode.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2017 Ryan Porter 3 | You may use, distribute, or modify this code under the terms of the MIT license. 4 | */ 5 | 6 | #define NOMINMAX 7 | 8 | #include "boneToMesh.h" 9 | #include "boneToMeshNode.h" 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | // Input attributes 37 | MObject BoneToMeshNode::boneLength_attr; 38 | MObject BoneToMeshNode::boneMatrix_attr; 39 | MObject BoneToMeshNode::components_attr; 40 | MObject BoneToMeshNode::direction_attr; 41 | MObject BoneToMeshNode::directionMatrix_attr; 42 | MObject BoneToMeshNode::fillPartialLoops_attr; 43 | MObject BoneToMeshNode::inMesh_attr; 44 | MObject BoneToMeshNode::maxDistance_attr; 45 | MObject BoneToMeshNode::subdivisionsAxis_attr; 46 | MObject BoneToMeshNode::subdivisionsHeight_attr; 47 | MObject BoneToMeshNode::radius_attr; 48 | MObject BoneToMeshNode::useMaxDistance_attr; 49 | 50 | // Output attributes 51 | MObject BoneToMeshNode::outMesh_attr; 52 | 53 | 54 | const short X_AXIS = 0; 55 | const short Y_AXIS = 1; 56 | const short Z_AXIS = 2; 57 | 58 | 59 | MStatus BoneToMeshNode::compute(const MPlug &plug, MDataBlock &dataBlock) 60 | { 61 | MStatus status; 62 | 63 | if (plug != outMesh_attr) { 64 | return MStatus::kUnknownParameter; 65 | } 66 | 67 | BoneToMeshParams params; 68 | 69 | MObject inMesh = dataBlock.inputValue(inMesh_attr).data(); 70 | MMatrix boneMatrix = MFnMatrixData(dataBlock.inputValue(boneMatrix_attr).data()).matrix(); 71 | MObject componentsList = dataBlock.inputValue(components_attr).data(); 72 | MMatrix directionMatrix = MFnMatrixData(dataBlock.inputValue(directionMatrix_attr).data()).matrix(); 73 | 74 | MObject components = this->unpackComponentList(componentsList); 75 | 76 | bool useMaxDistance = dataBlock.inputValue(useMaxDistance_attr).asBool(); 77 | 78 | params.boneLength = (float) dataBlock.inputValue(boneLength_attr).asDouble(); 79 | params.direction = dataBlock.inputValue(direction_attr).asShort(); 80 | params.fillPartialLoopsMethod = dataBlock.inputValue(fillPartialLoops_attr).asShort(); 81 | params.maxDistance = (float) (useMaxDistance ? (dataBlock.inputValue(maxDistance_attr).asDouble()) : DBL_MAX); 82 | params.radius = (float) dataBlock.inputValue(radius_attr).asDouble(); 83 | params.subdivisionsX = (uint) std::max(4, dataBlock.inputValue(subdivisionsAxis_attr).asLong()); 84 | params.subdivisionsY = (uint) std::max(2, dataBlock.inputValue(subdivisionsHeight_attr).asLong()); 85 | 86 | 87 | MDataHandle outMeshHandle = dataBlock.outputValue(outMesh_attr); 88 | 89 | MFnMeshData outMeshData; 90 | MObject outMesh = outMeshData.create(&status); 91 | CHECK_MSTATUS_AND_RETURN_IT(status); 92 | 93 | if (inMesh.isNull()) 94 | { 95 | return MStatus::kFailure; 96 | } else { 97 | status = boneToMesh( 98 | inMesh, 99 | components, 100 | boneMatrix, 101 | directionMatrix, 102 | params, 103 | outMesh 104 | ); 105 | CHECK_MSTATUS_AND_RETURN_IT(status); 106 | } 107 | 108 | if (outMesh.isNull()) 109 | { 110 | MGlobal::displayError("boneToMesh projection failed."); 111 | return MStatus::kFailure; 112 | } else { 113 | status = outMeshHandle.setMObject(outMesh); 114 | CHECK_MSTATUS_AND_RETURN_IT(status); 115 | } 116 | 117 | outMeshHandle.setClean(); 118 | 119 | return MStatus::kSuccess; 120 | } 121 | 122 | 123 | MObject BoneToMeshNode::unpackComponentList(MObject &componentList) 124 | { 125 | MObject components; 126 | 127 | if (!componentList.isNull()) 128 | { 129 | MFnComponentListData fnComponentList(componentList); 130 | MFnSingleIndexedComponent fnComponents; 131 | components = fnComponents.create(MFn::kMeshPolygonComponent); 132 | 133 | uint numComponents = fnComponentList.length(); 134 | 135 | for (uint i = 0; i < numComponents; i++) 136 | { 137 | MObject c = fnComponentList[i]; 138 | 139 | if (c.apiType() == MFn::kMeshPolygonComponent) 140 | { 141 | MFnSingleIndexedComponent fnComponent(c); 142 | int n = fnComponent.elementCount(); 143 | 144 | for (int j = 0; j < n; j++) 145 | { 146 | fnComponents.addElement(fnComponent.element(j)); 147 | } 148 | } 149 | } 150 | } 151 | 152 | return components; 153 | } 154 | 155 | 156 | MStatus BoneToMeshNode::initialize() 157 | { 158 | MStatus status; 159 | 160 | MFnEnumAttribute enumAttr; 161 | MFnNumericAttribute numAttr; 162 | MFnTypedAttribute typedAttr; 163 | 164 | inMesh_attr = typedAttr.create("inMesh", "im", MFnData::kMesh, MObject::kNullObj, &status); 165 | CHECK_MSTATUS_AND_RETURN_IT(status); 166 | 167 | components_attr = typedAttr.create("components", "c", MFnData::kComponentList, MObject::kNullObj, &status); 168 | CHECK_MSTATUS_AND_RETURN_IT(status); 169 | 170 | boneMatrix_attr = typedAttr.create("boneMatrix", "bm", MFnData::kMatrix, MObject::kNullObj, &status); 171 | CHECK_MSTATUS_AND_RETURN_IT(status); 172 | 173 | directionMatrix_attr = typedAttr.create("directionMatrix", "dm", MFnData::kMatrix, MObject::kNullObj, &status); 174 | CHECK_MSTATUS_AND_RETURN_IT(status); 175 | 176 | boneLength_attr = numAttr.create("boneLength", "len", MFnNumericData::kDouble, 1.0, &status); 177 | CHECK_MSTATUS_AND_RETURN_IT(status); 178 | numAttr.setKeyable(true); 179 | numAttr.setKeyable(true); 180 | 181 | direction_attr = enumAttr.create("direction", "d", X_AXIS, &status); 182 | CHECK_MSTATUS_AND_RETURN_IT(status); 183 | enumAttr.setKeyable(true); 184 | enumAttr.addField("X", X_AXIS); 185 | enumAttr.addField("Y", Y_AXIS); 186 | enumAttr.addField("Z", Z_AXIS); 187 | 188 | fillPartialLoops_attr = enumAttr.create("fillPartialLoops", "fp", 3, &status); 189 | CHECK_MSTATUS_AND_RETURN_IT(status); 190 | enumAttr.addField("No Fill", 0), 191 | enumAttr.addField("Shortest", 1); 192 | enumAttr.addField("Longest", 2); 193 | enumAttr.addField("Average", 3); 194 | enumAttr.addField("Radius", 4); 195 | enumAttr.setKeyable(true); 196 | 197 | radius_attr = numAttr.create("radius", "r", MFnNumericData::kDouble, 1.0, &status); 198 | CHECK_MSTATUS_AND_RETURN_IT(status); 199 | numAttr.setMin(0.0); 200 | numAttr.setKeyable(true); 201 | 202 | subdivisionsAxis_attr = numAttr.create("subdivisionsAxis", "sa", MFnNumericData::kLong, 0, &status); 203 | CHECK_MSTATUS_AND_RETURN_IT(status); 204 | numAttr.setDefault(8); 205 | numAttr.setMin(3); 206 | numAttr.setKeyable(true); 207 | 208 | subdivisionsHeight_attr = numAttr.create("subdivisionsHeight", "sh", MFnNumericData::kLong, 0, &status); 209 | CHECK_MSTATUS_AND_RETURN_IT(status); 210 | numAttr.setDefault(4); 211 | numAttr.setMin(1); 212 | numAttr.setKeyable(true); 213 | 214 | maxDistance_attr = numAttr.create("maxDistance", "md", MFnNumericData::kDouble, 1.0, &status); 215 | CHECK_MSTATUS_AND_RETURN_IT(status); 216 | numAttr.setMin(0.0); 217 | numAttr.setKeyable(true); 218 | 219 | useMaxDistance_attr = numAttr.create("useMaxDistance", "umd", MFnNumericData::kBoolean, false, &status); 220 | CHECK_MSTATUS_AND_RETURN_IT(status); 221 | numAttr.setKeyable(true); 222 | 223 | outMesh_attr = typedAttr.create("outMesh", "om", MFnData::kMesh, MObject::kNullObj, &status); 224 | CHECK_MSTATUS_AND_RETURN_IT(status); 225 | typedAttr.setStorable(false); 226 | 227 | addAttribute(boneLength_attr); 228 | addAttribute(boneMatrix_attr); 229 | addAttribute(components_attr); 230 | addAttribute(direction_attr); 231 | addAttribute(directionMatrix_attr); 232 | addAttribute(fillPartialLoops_attr); 233 | addAttribute(inMesh_attr); 234 | addAttribute(maxDistance_attr); 235 | addAttribute(radius_attr); 236 | addAttribute(subdivisionsAxis_attr); 237 | addAttribute(subdivisionsHeight_attr); 238 | addAttribute(useMaxDistance_attr); 239 | addAttribute(outMesh_attr); 240 | 241 | attributeAffects(inMesh_attr, outMesh_attr); 242 | attributeAffects(boneMatrix_attr, outMesh_attr); 243 | attributeAffects(boneLength_attr, outMesh_attr); 244 | attributeAffects(components_attr, outMesh_attr); 245 | attributeAffects(fillPartialLoops_attr, outMesh_attr); 246 | attributeAffects(direction_attr, outMesh_attr); 247 | attributeAffects(directionMatrix_attr, outMesh_attr); 248 | attributeAffects(radius_attr, outMesh_attr); 249 | attributeAffects(subdivisionsAxis_attr, outMesh_attr); 250 | attributeAffects(subdivisionsHeight_attr, outMesh_attr); 251 | attributeAffects(maxDistance_attr, outMesh_attr); 252 | attributeAffects(useMaxDistance_attr, outMesh_attr); 253 | 254 | return MStatus::kSuccess; 255 | } 256 | 257 | 258 | void* BoneToMeshNode::creator() 259 | { 260 | return new BoneToMeshNode(); 261 | } 262 | -------------------------------------------------------------------------------- /src/boneToMesh.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2017 Ryan Porter 3 | You may use, distribute, or modify this code under the terms of the MIT license. 4 | */ 5 | 6 | #define NOMINMAX 7 | 8 | #include "boneToMesh.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | const short FILL_NONE = 0; 33 | const short FILL_SHORTEST = 1; 34 | const short FILL_LONGEST = 2; 35 | const short FILL_AVERAGE = 3; 36 | const short FILL_RADIUS = 4; 37 | 38 | MStatus boneToMesh( 39 | const MObject &inMesh, 40 | const MObject &components, 41 | const MMatrix &boneMatrix, 42 | const MMatrix &directionMatrix, 43 | BoneToMeshParams ¶ms, 44 | MObject &outMesh 45 | ) { 46 | MStatus status; 47 | 48 | BoneToMeshProjection proj; 49 | 50 | proj.boneMatrix = boneMatrix; 51 | proj.directionMatrix = directionMatrix; 52 | 53 | switch (params.direction) 54 | { 55 | case 0: // X axis 56 | proj.directionVector = MVector::xAxis; 57 | proj.projectionVector = MVector::yAxis; 58 | proj.longAxis = MVector::kXaxis; 59 | break; 60 | 61 | case 1: // Y axis 62 | proj.directionVector = MVector::yAxis; 63 | proj.projectionVector = MVector::zAxis; 64 | proj.longAxis = MVector::kYaxis; 65 | break; 66 | 67 | case 2: // Z axis 68 | proj.directionVector = MVector::zAxis; 69 | proj.projectionVector = MVector::xAxis; 70 | proj.longAxis = MVector::kZaxis; 71 | break; 72 | } 73 | 74 | MVector dVec(proj.directionVector); 75 | dVec *= params.boneLength; 76 | dVec *= proj.boneMatrix; 77 | 78 | proj.directionVector = MFloatVector(dVec); 79 | proj.startPoint = MFloatPoint(MPoint::origin * proj.boneMatrix); 80 | proj.maxVertices = params.subdivisionsY * params.subdivisionsX; 81 | 82 | projectionVectors(params, proj); 83 | projectBoneToMesh(inMesh, components, params, proj); 84 | fillPartialLoops(params, proj); 85 | createMesh(params, proj, outMesh); 86 | 87 | return MStatus::kSuccess; 88 | } 89 | 90 | MStatus projectionVectors(BoneToMeshParams ¶ms, BoneToMeshProjection &proj) 91 | { 92 | MStatus status; 93 | 94 | proj.raySources.resize(params.subdivisionsY); 95 | proj.rayDirections.resize(proj.maxVertices); 96 | 97 | for (uint sh = 0; sh < params.subdivisionsY; sh++) 98 | { 99 | float t = float(sh) / float(params.subdivisionsY - 1); 100 | MFloatPoint raySource(proj.startPoint + (proj.directionVector * t)); 101 | 102 | proj.raySources[sh] = raySource; 103 | 104 | MTransformationMatrix dxMatrix(proj.directionMatrix); 105 | dxMatrix.setTranslation(MVector(raySource), MSpace::kWorld); 106 | 107 | MMatrix dMatrix = dxMatrix.asMatrix(); 108 | 109 | for (uint sa = 0; sa < params.subdivisionsX; sa++) 110 | { 111 | double a = (2.0 * M_PI) * (float(sa) / float(params.subdivisionsX)); 112 | 113 | uint idx = (sh * params.subdivisionsX) + sa; 114 | MVector ray(proj.projectionVector); 115 | ray = ray.rotateBy(proj.longAxis, a); 116 | ray *= dMatrix; 117 | 118 | proj.rayDirections[idx] = MFloatVector(ray); 119 | } 120 | } 121 | 122 | return MStatus::kSuccess; 123 | } 124 | 125 | 126 | MStatus projectBoneToMesh( 127 | const MObject &inMesh, 128 | const MObject &components, 129 | BoneToMeshParams ¶ms, 130 | BoneToMeshProjection &proj 131 | ) { 132 | MStatus status; 133 | 134 | MFnMesh inMeshFn(inMesh); 135 | 136 | MMeshIsectAccelParams accelParams = inMeshFn.autoUniformGridParams(); 137 | 138 | bool useComponents = !components.isNull(); 139 | 140 | MIntArray faceIds; 141 | MIntArray* faceIds_ptr = NULL; 142 | 143 | if (useComponents) 144 | { 145 | MFnSingleIndexedComponent fnComponents(components); 146 | 147 | int numComponents = fnComponents.elementCount(); 148 | faceIds.setLength(numComponents); 149 | 150 | for (int i = 0; i < numComponents; i++) 151 | { 152 | faceIds[i] = fnComponents.element(i); 153 | } 154 | 155 | faceIds_ptr = &faceIds; 156 | } 157 | 158 | float tolerance = (float) 1e-6; 159 | float maxParams = (float) params.maxDistance; 160 | 161 | proj.indices.resize(proj.maxVertices, -1); 162 | proj.points.resize(proj.maxVertices); 163 | 164 | for (uint sh = 0; sh < params.subdivisionsY; sh++) 165 | { 166 | for (uint sa = 0; sa < params.subdivisionsX; sa++) 167 | { 168 | uint idx = (sh * params.subdivisionsX) + sa; 169 | MFloatPointArray hitPoints; 170 | 171 | bool hits = inMeshFn.allIntersections( 172 | proj.raySources[sh], 173 | proj.rayDirections[idx], 174 | faceIds_ptr, 175 | NULL, // tri Ids 176 | true, // sort ids 177 | MSpace::kObject,// space 178 | maxParams, 179 | false, // test both directions 180 | &accelParams, // acceleration parameters 181 | true, // sort hits 182 | hitPoints, 183 | NULL, // hit ray params 184 | NULL, // hit faces 185 | NULL, // hit triangles 186 | NULL, // hit barycentric coordinates 187 | NULL, // hit barycentric coordinates 188 | tolerance, 189 | &status 190 | ); 191 | 192 | if (hits) 193 | { 194 | proj.indices[idx] = proj.vertexIndex++; 195 | proj.points[idx] = MFloatPoint(hitPoints[0]); 196 | } 197 | 198 | CHECK_MSTATUS(status); 199 | } 200 | } 201 | 202 | return MStatus::kSuccess; 203 | } 204 | 205 | MStatus fillPartialLoops(BoneToMeshParams ¶ms, BoneToMeshProjection &proj) 206 | { 207 | MStatus status; 208 | 209 | if (params.fillPartialLoopsMethod != FILL_NONE) 210 | { 211 | // Fill in missing points. 212 | for (uint sh = 0; sh < params.subdivisionsY; sh++) 213 | { 214 | int numHits = 0; 215 | 216 | float rayLength = params.fillPartialLoopsMethod == FILL_SHORTEST ? FLT_MAX : 0.0f; 217 | 218 | for (uint sa = 0; sa < params.subdivisionsX; sa++) 219 | { 220 | uint idx = (sh * params.subdivisionsX) + sa; 221 | 222 | if (proj.indices[idx] != -1) 223 | { 224 | switch (params.fillPartialLoopsMethod) 225 | { 226 | case FILL_SHORTEST: 227 | rayLength = std::min(rayLength, (proj.points[idx] - proj.raySources[sh]).length()); 228 | break; 229 | case FILL_LONGEST: 230 | rayLength = std::max(rayLength, (proj.points[idx] - proj.raySources[sh]).length()); 231 | break; 232 | case FILL_AVERAGE: 233 | rayLength += (proj.points[idx] - proj.raySources[sh]).length(); 234 | break; 235 | } 236 | 237 | numHits++; 238 | } 239 | } 240 | 241 | if (numHits == 0) 242 | { 243 | continue; 244 | } 245 | 246 | switch(params.fillPartialLoopsMethod) 247 | { 248 | case FILL_AVERAGE: rayLength /= float(numHits); break; 249 | case FILL_RADIUS: rayLength = (float) params.radius; break; 250 | } 251 | 252 | for (uint sa = 0; sa < params.subdivisionsX; sa++) 253 | { 254 | uint idx = (sh * params.subdivisionsX) + sa; 255 | 256 | if (proj.indices[idx] == -1) 257 | { 258 | proj.indices[idx] = proj.vertexIndex++; 259 | proj.points[idx] = proj.raySources[sh] + (proj.rayDirections[idx] * rayLength); 260 | } 261 | } 262 | } 263 | 264 | // Reset the vertex indices 265 | proj.vertexIndex = 0; 266 | 267 | for (uint sh = 0; sh < params.subdivisionsY; sh++) 268 | { 269 | for (uint sa = 0; sa < params.subdivisionsX; sa++) 270 | { 271 | uint idx = (sh * params.subdivisionsX) + sa; 272 | 273 | if (proj.indices[idx] != -1) 274 | { 275 | proj.indices[idx] = proj.vertexIndex++; 276 | } 277 | } 278 | } 279 | } 280 | 281 | return MStatus::kSuccess; 282 | } 283 | 284 | 285 | MStatus createMesh(BoneToMeshParams ¶ms, BoneToMeshProjection &proj, MObject &outMesh) 286 | { 287 | MStatus status; 288 | 289 | MFloatPointArray vertexArray(proj.maxVertices); 290 | MIntArray polygonCounts(proj.maxVertices, 4); 291 | MIntArray polygonConnects(proj.maxVertices * 4, -1); 292 | 293 | int numVertices = 0; 294 | int numPolygons = 0; 295 | 296 | // Face order - clockwise vs counter-clockwise 297 | int cw = (int) params.boneLength >= 0; 298 | int cc = (int) params.boneLength < 0; 299 | 300 | for (uint sh = 0; sh < params.subdivisionsY; sh++) 301 | { 302 | for (uint sa = 0; sa < params.subdivisionsX; sa++) 303 | { 304 | uint idx = (sh * params.subdivisionsX) + sa; 305 | 306 | if (proj.indices[idx] != -1) 307 | { 308 | vertexArray.set(proj.points[idx], (uint) numVertices); 309 | numVertices++; 310 | } 311 | } 312 | } 313 | 314 | for (uint sh = 0; sh < params.subdivisionsY - 1; sh++) 315 | { 316 | for (uint sa = 0; sa < params.subdivisionsX; sa++) 317 | { 318 | uint na = (sa + 1) % (params.subdivisionsX); 319 | 320 | uint idx0 = (sh * params.subdivisionsX) + sa; 321 | uint idx1 = (sh * params.subdivisionsX) + na; 322 | uint idx2 = ((sh + 1) * params.subdivisionsX) + sa; 323 | uint idx3 = ((sh + 1) * params.subdivisionsX) + na; 324 | 325 | int vtx0 = proj.indices[idx0]; 326 | int vtx1 = proj.indices[idx1]; 327 | int vtx2 = proj.indices[idx2]; 328 | int vtx3 = proj.indices[idx3]; 329 | 330 | if (vtx0 == -1 || vtx1 == -1 || vtx2 == -1 || vtx3 == -1) { continue; } 331 | 332 | // (vtx# * clockwise) + (vtx# * counter-clockwise) 333 | // to avoid the if branch 334 | polygonConnects[(numPolygons * 4) + 0] = (vtx0 * cw) + (vtx0 * cc); 335 | polygonConnects[(numPolygons * 4) + 1] = (vtx1 * cw) + (vtx2 * cc); 336 | polygonConnects[(numPolygons * 4) + 2] = (vtx3 * cw) + (vtx3 * cc); 337 | polygonConnects[(numPolygons * 4) + 3] = (vtx2 * cw) + (vtx1 * cc); 338 | 339 | numPolygons++; 340 | } 341 | } 342 | 343 | vertexArray.setLength(numVertices); 344 | polygonCounts.setLength(numPolygons); 345 | polygonConnects.setLength(numPolygons * 4); 346 | 347 | MFnMesh outMeshFn; 348 | 349 | outMeshFn.create( 350 | numVertices, 351 | numPolygons, 352 | vertexArray, 353 | polygonCounts, 354 | polygonConnects, 355 | outMesh, 356 | &status 357 | ); 358 | 359 | CHECK_MSTATUS_AND_RETURN_IT(status); 360 | 361 | return MStatus::kSuccess; 362 | } 363 | -------------------------------------------------------------------------------- /src/boneToMeshCmd.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2017 Ryan Porter 3 | You may use, distribute, or modify this code under the terms of the MIT license. 4 | */ 5 | 6 | #include "boneToMesh.h" 7 | #include "boneToMeshCmd.h" 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | #define RETURN_IF_ERROR(s) if (!s) { return s; } 33 | 34 | const char* AXIS_FLAG = "-a"; 35 | const char* AXIS_LONG = "-axis"; 36 | 37 | const char* BONE_FLAG = "-b"; 38 | const char* BONE_LONG = "-bone"; 39 | 40 | const char* CONSTRUCTION_HISTORY_FLAG = "-ch"; 41 | const char* CONSTRUCTION_HISTORY_LONG = "-constructionHistory"; 42 | 43 | const char* FILL_PARTIAL_LOOPS_FLAG = "-fp"; 44 | const char* FILL_PARTIAL_LOOPS_LONG = "-fillPartialLoops"; 45 | 46 | const char* HELP_FLAG = "-h"; 47 | const char* HELP_LONG = "-help"; 48 | 49 | const char* LENGTH_FLAG = "-l"; 50 | const char* LENGTH_LONG = "-length"; 51 | 52 | const char* MAX_DISTANCE_FLAG = "-md"; 53 | const char* MAX_DISTANCE_LONG = "-maxDistance"; 54 | 55 | const char* RADIUS_FLAG = "-r"; 56 | const char* RADIUS_LONG = "-radius"; 57 | 58 | const char* SUBDIVISIONS_X_FLAG = "-sx"; 59 | const char* SUBDIVISIONS_X_LONG = "-subdivisionsX"; 60 | 61 | const char* SUBDIVISIONS_Y_FLAG = "-sy"; 62 | const char* SUBDIVISIONS_Y_LONG = "-subdivisionsY"; 63 | 64 | const char* WORLD_SPACE_FLAG = "-w"; 65 | const char* WORLD_SPACE_LONG = "-world"; 66 | 67 | 68 | BoneToMeshCommand::BoneToMeshCommand() 69 | { 70 | 71 | } 72 | 73 | 74 | BoneToMeshCommand::~BoneToMeshCommand() 75 | { 76 | 77 | } 78 | 79 | 80 | void* BoneToMeshCommand::creator() 81 | { 82 | return new BoneToMeshCommand(); 83 | } 84 | 85 | 86 | void BoneToMeshCommand::help() 87 | { 88 | MString helpMessage( 89 | "\nboneToMesh\n" 90 | "\n" 91 | "Creates a cylindrical mesh around the specified bone and projects it outward onto the selected mesh.\n" 92 | "\n" 93 | "FLAGS\n" 94 | "Long Name Short Name Argument Type(s) Description\n" 95 | "-axis -a string Long axis of the bone. Accepted values are \"x\", \"y\", or \"z\".\n" 96 | "-bone -b string Transform at the base of the \"bone\".\n" 97 | "-constructionHistory -ch boolean Toggles construction history on/off.\n" 98 | "-fillPartialLoops -fp string Method by which partial loops have their missing points filled\n" 99 | " Accepted values are 0 - \"none\", 1 - \"shortest\", 2 - \"longest\", 3 - \"average\", or 4 - \"radius\".\n" 100 | "-length -l double Length of the bone.\n" 101 | "-maxDistance -md double Maximum distance from the bone an intersection with the mesh may occur.\n" 102 | "-radius -r double Distance from the bone of filled in points if -fillPartialLoops is set to \"radius\".\n" 103 | "-subdivisionsX -sx int Specifies the number of subdivisions around the bone.\n" 104 | "-subdivisionsY -sy int Specifies the number of subdivisions along the bone.\n" 105 | "-world -w boolean Toggles the axis between world and local.\n" 106 | ); 107 | 108 | MGlobal::displayInfo(helpMessage); 109 | } 110 | 111 | 112 | MStatus BoneToMeshCommand::parseArguments(MArgDatabase &argsData) 113 | { 114 | MStatus status; 115 | 116 | // -help flag 117 | if (argsData.isFlagSet(HELP_FLAG)) 118 | { 119 | this->showHelp = true; 120 | return MStatus::kSuccess; 121 | } else { 122 | this->showHelp = false; 123 | } 124 | 125 | // selected mesh 126 | { 127 | MSelectionList selection; 128 | argsData.getObjects(selection); 129 | 130 | if (selection.isEmpty()) 131 | { 132 | MGlobal::displayError("Must select a mesh."); 133 | return MStatus::kFailure; 134 | } 135 | 136 | selection.getDagPath(0, this->inMesh, this->components); 137 | } 138 | 139 | // -axis flag 140 | if (argsData.isFlagSet(AXIS_FLAG)) 141 | { 142 | status = argsData.getFlagArgument(AXIS_FLAG, 0, this->axis); 143 | CHECK_MSTATUS_AND_RETURN_IT(status); 144 | } else { 145 | this->axis = "x"; 146 | } 147 | 148 | // -bone flag 149 | if (argsData.isFlagSet(BONE_FLAG)) 150 | { 151 | MSelectionList selection; 152 | MString objectName; 153 | 154 | status = argsData.getFlagArgument(BONE_FLAG, 0, objectName); 155 | CHECK_MSTATUS_AND_RETURN_IT(status); 156 | 157 | status = selection.add(objectName); 158 | 159 | if (status) 160 | { 161 | status = selection.getDependNode(0, this->boneObj); 162 | RETURN_IF_ERROR(status); 163 | } else { 164 | MString errorMsg("Object '^1s does not exist."); 165 | errorMsg.format(errorMsg, objectName); 166 | MGlobal::displayError(errorMsg); 167 | return status; 168 | } 169 | } else { 170 | MGlobal::displayError("The -bone/-b flag is required."); 171 | return MStatus::kFailure; 172 | } 173 | 174 | // -constructionHistory flag 175 | if (argsData.isFlagSet(CONSTRUCTION_HISTORY_FLAG)) 176 | { 177 | status = argsData.getFlagArgument(CONSTRUCTION_HISTORY_FLAG, 0, this->constructionHistory); 178 | CHECK_MSTATUS_AND_RETURN_IT(status); 179 | } 180 | 181 | // -fillPartialLoops flag 182 | if (argsData.isFlagSet(FILL_PARTIAL_LOOPS_FLAG)) 183 | { 184 | status = argsData.getFlagArgument(FILL_PARTIAL_LOOPS_FLAG, 0, params.fillPartialLoopsMethod); 185 | CHECK_MSTATUS_AND_RETURN_IT(status); 186 | 187 | if (params.fillPartialLoopsMethod < 0) { params.fillPartialLoopsMethod = 0; } 188 | if (params.fillPartialLoopsMethod > 4) { params.fillPartialLoopsMethod = 4; } 189 | } 190 | 191 | // -length flag 192 | if (argsData.isFlagSet(LENGTH_FLAG)) 193 | { 194 | status = argsData.getFlagArgument(LENGTH_FLAG, 0, params.boneLength); 195 | CHECK_MSTATUS_AND_RETURN_IT(status); 196 | } 197 | 198 | // -maxDistance flag 199 | if (argsData.isFlagSet(MAX_DISTANCE_FLAG)) 200 | { 201 | this->useMaxDistance = true; 202 | 203 | status = argsData.getFlagArgument(MAX_DISTANCE_FLAG, 0, params.maxDistance); 204 | CHECK_MSTATUS_AND_RETURN_IT(status); 205 | } 206 | 207 | // -radius flag 208 | if (argsData.isFlagSet(RADIUS_FLAG)) 209 | { 210 | status = argsData.getFlagArgument(RADIUS_FLAG, 0, params.radius); 211 | CHECK_MSTATUS_AND_RETURN_IT(status); 212 | } 213 | 214 | // -subdivisionsX (axis) flag 215 | if (argsData.isFlagSet(SUBDIVISIONS_X_FLAG)) 216 | { 217 | status = argsData.getFlagArgument(SUBDIVISIONS_X_FLAG, 0, params.subdivisionsX); 218 | CHECK_MSTATUS_AND_RETURN_IT(status); 219 | } 220 | 221 | // -subdivisionsY (height) flag 222 | if (argsData.isFlagSet(SUBDIVISIONS_Y_FLAG)) 223 | { 224 | status = argsData.getFlagArgument(SUBDIVISIONS_Y_FLAG, 0, params.subdivisionsY); 225 | CHECK_MSTATUS_AND_RETURN_IT(status); 226 | } 227 | 228 | // -world flag 229 | if (argsData.isFlagSet(WORLD_SPACE_FLAG)) 230 | { 231 | status = argsData.getFlagArgument(WORLD_SPACE_FLAG, 0, this->useWorldDirection); 232 | CHECK_MSTATUS_AND_RETURN_IT(status); 233 | } else { 234 | this->useWorldDirection = false; 235 | } 236 | 237 | 238 | return MStatus::kSuccess; 239 | } 240 | 241 | 242 | MStatus BoneToMeshCommand::validateArguments() 243 | { 244 | MStatus status; 245 | 246 | if ( 247 | this->axis != "x" && 248 | this->axis != "y" && 249 | this->axis != "z" 250 | ) { 251 | MGlobal::displayError("-axis/-a flag must be set to \"x\", \"y\", or \"z\"."); 252 | return MStatus::kFailure; 253 | } 254 | 255 | if (this->inMesh.hasFn(MFn::kMesh)) 256 | { 257 | if (this->inMesh.node().hasFn(MFn::kTransform)) 258 | { 259 | this->inMesh.extendToShapeDirectlyBelow(0); 260 | } 261 | } else { 262 | MGlobal::displayError("Must select a mesh."); 263 | return MStatus::kFailure; 264 | } 265 | 266 | if (!this->boneObj.hasFn(MFn::kTransform)) { 267 | MGlobal::displayError("The -bone/-b flag expects a transform."); 268 | return MStatus::kFailure; 269 | } 270 | 271 | if (params.subdivisionsX < 3) { 272 | MGlobal::displayError("The -subdivisionsX/-sx flag must be at least 3."); 273 | return MStatus::kFailure; 274 | } 275 | 276 | if (params.subdivisionsY < 1) { 277 | MGlobal::displayError("The -subdivisionsY/-sy flag must be at least 1."); 278 | return MStatus::kFailure; 279 | } 280 | 281 | return MStatus::kSuccess; 282 | } 283 | 284 | 285 | MSyntax BoneToMeshCommand::getSyntax() 286 | { 287 | MSyntax syntax; 288 | 289 | syntax.addFlag(AXIS_FLAG, AXIS_LONG, MSyntax::kString); 290 | syntax.addFlag(BONE_FLAG, BONE_LONG, MSyntax::kString); 291 | syntax.addFlag(CONSTRUCTION_HISTORY_FLAG, CONSTRUCTION_HISTORY_LONG, MSyntax::kBoolean); 292 | syntax.addFlag(FILL_PARTIAL_LOOPS_FLAG, FILL_PARTIAL_LOOPS_LONG, MSyntax::kLong); 293 | syntax.addFlag(HELP_FLAG, HELP_LONG, MSyntax::kBoolean); 294 | syntax.addFlag(LENGTH_FLAG, LENGTH_LONG, MSyntax::kDouble); 295 | syntax.addFlag(MAX_DISTANCE_FLAG, MAX_DISTANCE_LONG, MSyntax::kDouble); 296 | syntax.addFlag(RADIUS_FLAG, RADIUS_LONG, MSyntax::kDouble); 297 | syntax.addFlag(SUBDIVISIONS_X_FLAG, SUBDIVISIONS_X_LONG, MSyntax::kLong); 298 | syntax.addFlag(SUBDIVISIONS_Y_FLAG, SUBDIVISIONS_Y_LONG, MSyntax::kLong); 299 | syntax.addFlag(WORLD_SPACE_FLAG, WORLD_SPACE_LONG, MSyntax::kBoolean); 300 | 301 | syntax.useSelectionAsDefault(true); 302 | syntax.setObjectType(MSyntax::kSelectionList, 1, 1); 303 | 304 | syntax.enableQuery(false); 305 | syntax.enableEdit(false); 306 | 307 | return syntax; 308 | } 309 | 310 | 311 | MStatus BoneToMeshCommand::doIt(const MArgList& argList) 312 | { 313 | MStatus status; 314 | 315 | MArgDatabase argsData(syntax(), argList, &status); 316 | 317 | status = this->parseArguments(argsData); 318 | RETURN_IF_ERROR(status); 319 | 320 | if (this->showHelp) 321 | { 322 | help(); 323 | return MStatus::kSuccess; 324 | } 325 | 326 | status = this->validateArguments(); 327 | RETURN_IF_ERROR(status); 328 | 329 | status = this->redoIt(); 330 | 331 | return status; 332 | } 333 | 334 | 335 | MStatus BoneToMeshCommand::redoIt() 336 | { 337 | MStatus status; 338 | 339 | if (this->axis == "x") { params.direction = 0; } 340 | else if (this->axis == "y") { params.direction = 1; } 341 | else if (this->axis == "z") { params.direction = 2; } 342 | 343 | MFnTransform fnXform(this->boneObj, &status); 344 | CHECK_MSTATUS_AND_RETURN_IT(status); 345 | 346 | MMatrix boneMatrix = fnXform.transformation().asMatrix(); 347 | MMatrix directionMatrix = this->useWorldDirection ? MMatrix::identity : MMatrix(boneMatrix); 348 | 349 | MDagModifier dagMod; 350 | 351 | MObject newMeshParent; 352 | MObject newMesh = dagMod.createNode("transform", MObject::kNullObj, &status); 353 | CHECK_MSTATUS_AND_RETURN_IT(status); 354 | 355 | status = dagMod.doIt(); 356 | CHECK_MSTATUS_AND_RETURN_IT(status); 357 | 358 | CHECK_MSTATUS_AND_RETURN_IT(status); 359 | 360 | status = dagMod.doIt(); 361 | CHECK_MSTATUS_AND_RETURN_IT(status); 362 | 363 | MDagPath parentTransform; 364 | MDagPath::getAPathTo(newMesh, parentTransform); 365 | 366 | MObject inMeshObj = this->inMesh.node(); 367 | 368 | status = boneToMesh( 369 | inMeshObj, 370 | this->components, 371 | boneMatrix, 372 | directionMatrix, 373 | params, 374 | newMesh 375 | ); 376 | 377 | RETURN_IF_ERROR(status); 378 | 379 | MGlobal::executeCommand("sets -e -forceElement initialShadingGroup " + parentTransform.partialPathName()); 380 | 381 | newMeshParent = MObject(parentTransform.node()); 382 | parentTransform.extendToShape(); 383 | newMesh = MObject(parentTransform.node()); 384 | parentTransform.pop(); 385 | 386 | MString boneName = MFnDagNode(this->boneObj).name(); 387 | 388 | dagMod.renameNode(newMeshParent, boneName + "_Mesh"); 389 | dagMod.renameNode(newMesh, boneName + "_MeshShape"); 390 | status = dagMod.doIt(); 391 | CHECK_MSTATUS_AND_RETURN_IT(status); 392 | 393 | this->appendToResult(parentTransform.partialPathName()); 394 | 395 | if (this->constructionHistory) 396 | { 397 | MDGModifier dgMod; 398 | 399 | MObject newNode = dgMod.createNode("boneToMesh", &status); 400 | CHECK_MSTATUS_AND_RETURN_IT(status); 401 | 402 | status = dgMod.doIt(); 403 | CHECK_MSTATUS_AND_RETURN_IT(status); 404 | 405 | MFnDagNode fnInMesh(this->inMesh); 406 | MFnDependencyNode fnBone(this->boneObj); 407 | MFnDependencyNode fnNode(newNode); 408 | MFnDependencyNode fnNewMesh(newMesh); 409 | 410 | if (!this->components.isNull()) 411 | { 412 | MFnComponentListData fnComponentList; 413 | MObject componentList = fnComponentList.create(); 414 | 415 | MItMeshPolygon itPoly(this->inMesh, this->components); 416 | 417 | while (!itPoly.isDone()) 418 | { 419 | MObject c = itPoly.currentItem(); 420 | status = fnComponentList.add(c); 421 | CHECK_MSTATUS_AND_RETURN_IT(status); 422 | 423 | itPoly.next(); 424 | } 425 | 426 | MPlug node_componentsPlug = fnNode.findPlug("components", false); 427 | status = node_componentsPlug.setMObject(componentList); 428 | CHECK_MSTATUS_AND_RETURN_IT(status); 429 | } 430 | 431 | MPlug inMesh_worldMeshPlug = fnInMesh.findPlug("worldMesh", false, &status).elementByLogicalIndex(0); 432 | MPlug bone_worldMatrixPlug = fnBone.findPlug("worldMatrix", false, &status).elementByLogicalIndex(0); 433 | 434 | MPlug node_boneLengthPlug = fnNode.findPlug("boneLength", false); 435 | MPlug node_boneMatrixPlug = fnNode.findPlug("boneMatrix", false); 436 | MPlug node_directionPlug = fnNode.findPlug("direction", false); 437 | MPlug node_directionMatrixPlug = fnNode.findPlug("directionMatrix", false); 438 | MPlug node_inMeshPlug = fnNode.findPlug("inMesh", false); 439 | MPlug node_maxDistancePlug = fnNode.findPlug("maxDistance", false); 440 | MPlug node_outMeshPlug = fnNode.findPlug("outMesh", false); 441 | MPlug node_subdivisionsXPlug = fnNode.findPlug("subdivisionsAxis", false); 442 | MPlug node_subdivisionsYPlug = fnNode.findPlug("subdivisionsHeight", false); 443 | MPlug node_useMaxDistancePlug = fnNode.findPlug("useMaxDistance", false); 444 | 445 | MPlug newMesh_inMeshPlug = fnNewMesh.findPlug("inMesh", false, &status); 446 | 447 | if (this->useMaxDistance) 448 | { 449 | status = node_useMaxDistancePlug.setBool(true); 450 | CHECK_MSTATUS_AND_RETURN_IT(status); 451 | status = node_maxDistancePlug.setDouble(params.maxDistance); 452 | CHECK_MSTATUS_AND_RETURN_IT(status); 453 | } 454 | 455 | dgMod.connect(inMesh_worldMeshPlug, node_inMeshPlug); 456 | dgMod.connect(bone_worldMatrixPlug, node_boneMatrixPlug); 457 | 458 | status = dgMod.doIt(); 459 | CHECK_MSTATUS_AND_RETURN_IT(status); 460 | 461 | MFnMatrixData fnMatrixData; 462 | MObject directionMatrixData = fnMatrixData.create(directionMatrix); 463 | node_directionMatrixPlug.setMObject(directionMatrixData); 464 | 465 | status = node_boneLengthPlug.setDouble(params.boneLength); 466 | CHECK_MSTATUS_AND_RETURN_IT(status); 467 | status = node_subdivisionsXPlug.setInt(params.subdivisionsX); 468 | status = node_subdivisionsYPlug.setInt(params.subdivisionsY); 469 | status = node_directionPlug.setShort(params.direction); 470 | CHECK_MSTATUS_AND_RETURN_IT(status); 471 | 472 | status = dgMod.connect(node_outMeshPlug, newMesh_inMeshPlug); 473 | CHECK_MSTATUS_AND_RETURN_IT(status); 474 | 475 | status = dgMod.doIt(); 476 | CHECK_MSTATUS_AND_RETURN_IT(status); 477 | 478 | this->appendToResult(fnNode.name()); 479 | 480 | this->undoCreatedNode = newNode; 481 | } 482 | 483 | this->undoCreatedMesh = newMeshParent; 484 | 485 | return MStatus::kSuccess; 486 | } 487 | 488 | 489 | MStatus BoneToMeshCommand::undoIt() 490 | { 491 | MStatus status; 492 | 493 | bool createdMesh = !undoCreatedMesh.isNull(); 494 | bool createdNode = !undoCreatedNode.isNull(); 495 | 496 | if (createdNode && createdMesh) 497 | { 498 | MString deleteCmd("delete ^1s ^2s"); 499 | deleteCmd.format( 500 | deleteCmd, 501 | MFnDependencyNode(undoCreatedNode).name(), 502 | MFnDependencyNode(undoCreatedMesh).name() 503 | ); 504 | 505 | status = MGlobal::executeCommand(deleteCmd); 506 | CHECK_MSTATUS_AND_RETURN_IT(status); 507 | } else if (createdNode) { 508 | CHECK_MSTATUS_AND_RETURN_IT(status); 509 | 510 | MString deleteCmd("delete ^1s"); 511 | deleteCmd.format( 512 | deleteCmd, 513 | MFnDependencyNode(undoCreatedNode).name() 514 | ); 515 | 516 | status = MGlobal::executeCommand(deleteCmd); 517 | CHECK_MSTATUS_AND_RETURN_IT(status); 518 | } else if (createdMesh) { 519 | MString deleteCmd("delete ^1s"); 520 | deleteCmd.format( 521 | deleteCmd, 522 | MFnDependencyNode(undoCreatedMesh).name() 523 | ); 524 | 525 | status = MGlobal::executeCommand(deleteCmd); 526 | CHECK_MSTATUS_AND_RETURN_IT(status); 527 | } 528 | 529 | return MStatus::kSuccess; 530 | } 531 | --------------------------------------------------------------------------------