[
  {
    "path": ".gitignore",
    "content": "\n#ignore thumbnails created by windows\nThumbs.db\n#Ignore files build by Visual Studio\n*.obj\n*.exe\n*.pdb\n*.user\n*.aps\n*.pch\n*.vspscc\n*_i.c\n*_p.c\n*.ncb\n*.suo\n*.tlb\n*.tlh\n*.bak\n*.cache\n*.ilk\n*.log\n[Bb]in\n[Dd]ebug*/\n*.lib\n*.sbr\nobj/\n[Rr]elease*/\n_ReSharper*/\n[Bb]uild*/\n[Tt]est[Rr]esult*\n.idea\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"cgcmake\"]\n\tpath = cgcmake\n\turl = git@github.com:chadmv/cgcmake.git\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.6)\nproject(cvwrap)\n\nset(PROJECT_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT})\nset(CMAKE_INSTALL_PREFIX ${PROJECT_PATH})\nset(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cgcmake/modules)\n\nadd_subdirectory(src)\n\nconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/module.txt\" \"${PROJECT_PATH}/${PROJECT_NAME}.txt\")\n\ninstall(DIRECTORY scripts DESTINATION .)\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Chad Vernon\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# cvwrap\nA Maya wrap deformer that is faster than Maya's wrap deformer, can be rebounded, has a GPU implementation, and supports inverted front of chain blend shapes.\n\nYou can purchase a video series documenting the development of this plug-in from scratch at [CGCircuit](http://www.cgcircuit.com/course/creating-a-gpu-driven-wrap-deformer?affid=df9a2a33e2f653182abfd4cfc9b7159752671dde4280a13fc724d0c42b62d143c055f01d504ecc4d176537d5ce1994d0010d204a200f178091bf59c85380dbdc).\n\n```python\nsphere = cmds.polySphere(sx=10, sy=10)[0]\ncube = cmds.polyCube(w=2.3, h=2.3, d=2.3, sx=5, sy=5, sz=5)[0]\n\n# Create a new wrap\nwrap_node = cmds.cvWrap(sphere, cube, name='wrapnode', radius=0.1)\n\n# Rebind a vertex\ncmds.select(['{0}.vtx[75]'.format(sphere)])\ncmds.select(['{0}.{1}'.format(cube, faces) for faces in ['f[110:111]', 'f[115:116]']], add=True)\ncmds.cvWrap(rb=wrap_node)\n\nfile_path = r'E:\\My Documents\\maya\\projects\\default\\data\\binding.wrap'\n# Export the binding\ncmds.cvWrap(wrap_node, ex=file_path)\n\n# Recreate the wrap node with the adjusted binding\ncmds.delete(wrap_node)\nwrap_node = cmds.cvWrap(sphere, cube, name=wrap_node, b=file_path)\n\n# Import the binding again\ncmds.cvWrap(wrap_node, im=file_path)\n```\n"
  },
  {
    "path": "build.bat",
    "content": "@echo off\nFOR %%G IN (2019, 2020, 2022, 2023) DO (call :subroutine \"%%G\")\nGOTO :eof\n\n:subroutine\nset builddir=build.%1\nif not exist %builddir% goto BUILDENV\ndel %builddir% /S /Q\n:BUILDENV\nmkdir %builddir%\ncd %builddir%\nif %1 LSS \"2020\" (\n    cmake -A x64 -T v140 -DMAYA_VERSION=%1 ../\n) ELSE (\n    cmake -A x64 -T v141 -DMAYA_VERSION=%1 ../\n)\ncmake --build . --target install --config Release\ncd ..\ngoto :eof\n"
  },
  {
    "path": "module.txt",
    "content": "+ ${PROJECT_NAME} 1.0.0 ${PROJECT_PATH}\n"
  },
  {
    "path": "scripts/AEcvWrapTemplate.mel",
    "content": "global proc AEcvWrapTemplate(string $nodeName) {\n    editorTemplate -beginScrollLayout;\n        editorTemplate -beginLayout \"cvWrap Attributes\" -collapse false;\n            editorTemplate -beginNoOptimize;\n            editorTemplate -addControl \"scale\";\n            editorTemplate -endNoOptimize;\t\t\t\t\n\n            editorTemplate -suppress \"driver\";\n            editorTemplate -suppress \"bindMesh\";\n            editorTemplate -suppress \"bindData\";\n            editorTemplate -suppress \"numTasks\";\n        editorTemplate -endLayout;\n\n    AEweightGeometryFilterTemplate $nodeName;\n\n    editorTemplate -addExtraControls;\n    editorTemplate -endScrollLayout;\n}\n"
  },
  {
    "path": "scripts/cvwrap/__init__.py",
    "content": ""
  },
  {
    "path": "scripts/cvwrap/bindui.py",
    "content": "import maya.cmds as cmds\nif cmds.about(api=True) >= 201700:\n    from PySide2 import QtWidgets as QtGui\nelse:\n    from PySide import QtGui\nfrom functools import partial\nfrom maya.app.general.mayaMixin import MayaQWidgetBaseMixin\n\n_win = None\ndef show():\n    global _win\n    if _win == None:\n        _win = BindingDialog()\n    _win.show()\n\n\nclass BindingDialog(MayaQWidgetBaseMixin, QtGui.QDialog):\n\n    def __init__(self, parent=None):\n        super(BindingDialog, self).__init__(parent)\n        self.resize(600, 200)\n        self.setWindowTitle('cvWrap Rebind')\n        vbox = QtGui.QVBoxLayout(self)\n\n        label_width = 130\n\n        hbox = QtGui.QHBoxLayout()\n        vbox.addLayout(hbox)\n        label = QtGui.QLabel('Components to rebind:')\n        label.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)\n        label.setMinimumWidth(label_width)\n        label.setMaximumWidth(label_width)\n        hbox.addWidget(label)\n        self.components_to_rebind = QtGui.QLineEdit()\n        self.components_to_rebind.textChanged.connect(self.populate_cvwrap_dropdown)\n        hbox.addWidget(self.components_to_rebind)\n        button = QtGui.QPushButton('Set Components')\n        button.released.connect(partial(self.set_selected_text, widget=self.components_to_rebind))\n        hbox.addWidget(button)\n\n        hbox = QtGui.QHBoxLayout()\n        vbox.addLayout(hbox)\n        label = QtGui.QLabel('Faces to rebind to:')\n        label.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)\n        label.setMinimumWidth(label_width)\n        label.setMaximumWidth(label_width)\n        hbox.addWidget(label)\n        self.target_faces = QtGui.QLineEdit()\n        hbox.addWidget(self.target_faces)\n        button = QtGui.QPushButton('Set Faces')\n        button.released.connect(partial(self.set_selected_text, widget=self.target_faces))\n        hbox.addWidget(button)\n\n        hbox = QtGui.QHBoxLayout()\n        vbox.addLayout(hbox)\n        label = QtGui.QLabel('cvWrap node:')\n        label.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)\n        label.setMinimumWidth(label_width)\n        label.setMaximumWidth(label_width)\n        hbox.addWidget(label)\n        self.cvwrap_combo = QtGui.QComboBox()\n        hbox.addWidget(self.cvwrap_combo)\n\n        hbox = QtGui.QHBoxLayout()\n        vbox.addLayout(hbox)\n        label = QtGui.QLabel('Sample radius:')\n        label.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)\n        label.setMinimumWidth(label_width)\n        label.setMaximumWidth(label_width)\n        hbox.addWidget(label)\n        self.sample_radius = QtGui.QDoubleSpinBox()\n        self.sample_radius.setValue(0.1)\n        self.sample_radius.setRange(0, 100)\n        self.sample_radius.setDecimals(2)\n        self.sample_radius.setSingleStep(.1)\n        hbox.addWidget(self.sample_radius)\n\n        vbox.addStretch()\n\n        hbox = QtGui.QHBoxLayout()\n        vbox.addLayout(hbox)\n        button = QtGui.QPushButton('Rebind')\n        button.released.connect(self.rebind)\n        hbox.addWidget(button)\n\n    def set_selected_text(self, widget):\n        sel = cmds.ls(sl=True)\n        text = ' '.join(sel)\n        widget.setText(text)\n\n    def populate_cvwrap_dropdown(self, text):\n        node = text.split()\n        if not node:\n            return\n        node = node[0].split('.')\n        if not node:\n            return\n        node = node[0]\n        wrap_nodes = [x for x in cmds.listHistory(node, pdo=True) or []\n                      if cmds.nodeType(x) == 'cvWrap']\n        self.cvwrap_combo.clear()\n        self.cvwrap_combo.addItems(wrap_nodes)\n\n    def rebind(self):\n        components = self.components_to_rebind.text().split()\n        faces = self.target_faces.text().split()\n        wrap_node = self.cvwrap_combo.currentText()\n        radius = self.sample_radius.value()\n        # Make sure the faces are actual faces.  If they are not, convert to faces.\n        cmds.select(faces)\n        cmds.ConvertSelectionToFaces()\n        faces = cmds.ls(sl=True)\n\n        cmds.select(components)\n        cmds.ConvertSelectionToVertices()\n        cmds.select(faces, add=True)\n        cmds.cvWrap(rb=wrap_node, radius=radius)\n        print('Rebounded vertices')\n\n"
  },
  {
    "path": "scripts/cvwrap/menu.py",
    "content": "import maya.cmds as cmds\nimport maya.mel as mel\nimport maya.OpenMayaUI as OpenMayaUI\nimport os\nif cmds.about(api=True) >= 201700:\n    from PySide2 import QtGui\nelse:\n    from PySide import QtGui\nimport cvwrap.bindui\n\nNAME_WIDGET = 'cvwrap_name'\nRADIUS_WIDGET = 'cvwrap_radius'\nNEW_BIND_MESH_WIDGET = 'cvwrap_newbindmesh'\nBIND_FILE_WIDGET = 'cvwrap_bindfile'\nMENU_ITEMS = []\n\n\ndef create_menuitems():\n    global MENU_ITEMS\n    if MENU_ITEMS:\n        # Already created\n        return\n    if cmds.about(api=True) < 201600:\n        cmds.warning('cvWrap menus only available in Maya 2016 and higher.')\n        return\n    for menu in ['mainDeformMenu', 'mainRigDeformationsMenu']:\n        # Make sure the menu widgets exist first.\n        mel.eval('ChaDeformationsMenu MayaWindow|{0};'.format(menu))\n        items = cmds.menu(menu, q=True, ia=True)\n        for item in items:\n            if cmds.menuItem(item, q=True, divider=True):\n                section = cmds.menuItem(item, q=True, label=True)\n            menu_label = cmds.menuItem(item, q=True, label=True)\n            if menu_label == 'Wrap':\n                if section == 'Create':\n                    cvwrap_item = cmds.menuItem(label=\"cvWrap\", command=create_cvwrap,\n                                                sourceType='python', insertAfter=item, parent=menu)\n                    cvwrap_options = cmds.menuItem(command=display_cvwrap_options,\n                                                   insertAfter=cvwrap_item, parent=menu,\n                                                   optionBox=True)\n                    MENU_ITEMS.append(cvwrap_item)\n                    MENU_ITEMS.append(cvwrap_options)\n                elif section == 'Edit':\n                    submenu = cmds.menuItem(label=\"cvWrap\", subMenu=True, insertAfter=item,\n                                            parent=menu)\n                    MENU_ITEMS.append(submenu)\n                    item = cmds.menuItem(label=\"Edit Binding\", command=edit_binding,\n                                         sourceType='python', parent=submenu)\n                    MENU_ITEMS.append(item)\n                    item = cmds.menuItem(label=\"Import Binding\", command=import_binding,\n                                         sourceType='python', parent=submenu)\n                    MENU_ITEMS.append(item)\n                    item = cmds.menuItem(label=\"Export Binding\", command=export_binding,\n                                         sourceType='python', parent=submenu)\n                    MENU_ITEMS.append(item)\n            elif menu_label == 'Cluster' and section == 'Paint Weights':\n                    item = cmds.menuItem(label=\"cvWrap\", command=paint_cvwrap_weights,\n                                         sourceType='python', insertAfter=item, parent=menu)\n                    MENU_ITEMS.append(item)\n\n\ndef create_cvwrap(*args, **kwargs):\n    cmds.loadPlugin('cvwrap', qt=True)\n    sel = cmds.ls(sl=True)\n    if len(sel) >= 2:\n        kwargs = get_create_command_kwargs()\n        result = cmds.cvWrap(**kwargs)\n        print(result)\n    else:\n        raise RuntimeError(\"Select at least one surface and one influence object.\")\n\n\ndef get_create_command_kwargs():\n    \"\"\"Gets the cvWrap command arguments either from the option box widgets or the saved\n    option vars.  If the widgets exist, their values will be saved to the option vars.\n    @return A dictionary of the kwargs to the cvWrap command.\"\"\"\n    args = {}\n    if cmds.textFieldGrp(NAME_WIDGET, exists=True):\n        args['name'] = cmds.textFieldGrp(NAME_WIDGET, q=True, text=True)\n        cmds.optionVar(sv=(NAME_WIDGET, args['name']))\n    else:\n        args['name'] = cmds.optionVar(q=NAME_WIDGET) or 'cvWrap#'\n    if cmds.floatSliderGrp(RADIUS_WIDGET, exists=True):\n        args['radius'] = cmds.floatSliderGrp(RADIUS_WIDGET, q=True, value=True)\n        cmds.optionVar(fv=(RADIUS_WIDGET, args['radius']))\n    else:\n        args['radius'] = cmds.optionVar(q=RADIUS_WIDGET)\n\n    if cmds.checkBoxGrp(NEW_BIND_MESH_WIDGET, exists=True):\n        if cmds.checkBoxGrp(NEW_BIND_MESH_WIDGET, q=True, v1=True):\n            args['newBindMesh'] = True\n            cmds.optionVar(iv=(NEW_BIND_MESH_WIDGET, 1))\n        else:\n            cmds.optionVar(iv=(NEW_BIND_MESH_WIDGET, 0))\n    else:\n        value = cmds.optionVar(q=NEW_BIND_MESH_WIDGET)\n        if value:\n            args['newBindMesh'] = True\n\n    if cmds.textFieldButtonGrp(BIND_FILE_WIDGET, exists=True):\n        bind_file = cmds.textFieldButtonGrp(BIND_FILE_WIDGET, q=True, text=True)\n        bind_file = os.path.expandvars(bind_file.strip())\n        if bind_file:\n            if os.path.exists(bind_file):\n                args['binding'] = bind_file\n            else:\n                cmds.warning('{0} does not exist.'.format(bind_file))\n\n    return args\n\n\ndef display_cvwrap_options(*args, **kwargs):\n    cmds.loadPlugin('cvwrap', qt=True)\n    layout = mel.eval('getOptionBox')\n    cmds.setParent(layout)\n    cmds.columnLayout(adj=True)\n\n    for widget in [NAME_WIDGET, RADIUS_WIDGET, BIND_FILE_WIDGET, NEW_BIND_MESH_WIDGET]:\n        # Delete the widgets so we don't create multiple controls with the same name\n        try:\n            cmds.deleteUI(widget, control=True)\n        except:\n            pass\n\n    cmds.textFieldGrp(NAME_WIDGET, label='Node name', text='cvWrap#')\n    radius = cmds.optionVar(q=RADIUS_WIDGET)\n    cmds.floatSliderGrp(RADIUS_WIDGET, label='Sample radius', field=True, minValue=0.0,\n                        maxValue=100.0, fieldMinValue=0.0, fieldMaxValue=100.0, value=radius,\n                        step=0.01, precision=2)\n    cmds.textFieldButtonGrp(BIND_FILE_WIDGET, label='Binding file ', text='', buttonLabel='Browse',\n                            bc=display_bind_file_dialog)\n    use_new_bind_mesh = cmds.optionVar(q=NEW_BIND_MESH_WIDGET)\n    cmds.checkBoxGrp(NEW_BIND_MESH_WIDGET, numberOfCheckBoxes=1, label='Create new bind mesh',\n                     v1=use_new_bind_mesh)\n    mel.eval('setOptionBoxTitle(\"cvWrap Options\");')\n    mel.eval('setOptionBoxCommandName(\"cvWrap\");')\n    apply_close_button = mel.eval('getOptionBoxApplyAndCloseBtn;')\n    cmds.button(apply_close_button, e=True, command=apply_and_close)\n    apply_button = mel.eval('getOptionBoxApplyBtn;')\n    cmds.button(apply_button, e=True, command=create_cvwrap)\n    reset_button = mel.eval('getOptionBoxResetBtn;')\n    # For some reason, the buttons in the menu only accept MEL.\n    cmds.button(reset_button, e=True,\n                command='python(\"import cvwrap.menu; cvwrap.menu.reset_to_defaults()\");')\n    close_button = mel.eval('getOptionBoxCloseBtn;')\n    cmds.button(close_button, e=True, command=close_option_box)\n    save_button = mel.eval('getOptionBoxSaveBtn;')\n    cmds.button(save_button, e=True,\n                command='python(\"import cvwrap.menu; cvwrap.menu.get_create_command_kwargs()\");')\n    mel.eval('showOptionBox')\n\n\ndef apply_and_close(*args, **kwargs):\n    \"\"\"Create the cvWrap deformer and close the option box.\"\"\"\n    create_cvwrap()\n    mel.eval('saveOptionBoxSize')\n    close_option_box()\n\n\ndef close_option_box(*args, **kwargs):\n    mel.eval('hideOptionBox')\n\n\ndef display_bind_file_dialog(*args, **kwargs):\n    \"\"\"Displays the dialog to choose the binding file with which to create the cvWrap deformer.\"\"\"\n    root_dir = cmds.workspace(q=True, rootDirectory=True)\n    start_directory = os.path.join(root_dir, 'data')\n    file_path = cmds.fileDialog2(fileFilter='*.wrap', dialogStyle=2, fileMode=1,\n                                 startingDirectory=start_directory)\n    if file_path:\n        cmds.textFieldButtonGrp(BIND_FILE_WIDGET, e=True, text=file_path[0])\n\n\ndef reset_to_defaults(*args, **kwargs):\n    \"\"\"Reset the cvWrap option box widgets to their defaults.\"\"\"\n    cmds.textFieldGrp(NAME_WIDGET, e=True, text='cvWrap#')\n    cmds.floatSliderGrp(RADIUS_WIDGET, e=True, value=0)\n    cmds.textFieldButtonGrp(BIND_FILE_WIDGET, e=True, text='')\n    cmds.checkBoxGrp(NEW_BIND_MESH_WIDGET, e=True, v1=False)\n\n\ndef edit_binding(*args, **kwargs):\n    cvwrap.bindui.show()\n\n\ndef export_binding(*args, **kwargs):\n    \"\"\"Export a wrap binding from the selected wrap node or mesh.\"\"\"\n    cmds.loadPlugin('cvwrap', qt=True)\n    wrap_node = get_wrap_node_from_selected()\n    if wrap_node:\n        data_dir = os.path.join(cmds.workspace(q=True, rd=True), 'data')\n        file_path = cmds.fileDialog2(fileFilter='*.wrap', dialogStyle=2, cap='Export Binding',\n                                     startingDirectory=data_dir, fm=0)\n        if file_path:\n            cmds.cvWrap(wrap_node, ex=file_path[0])\n\n\ndef import_binding(*args, **kwargs):\n    \"\"\"Import a wrap binding onto the selected wrap node or mesh.\"\"\"\n    cmds.loadPlugin('cvwrap', qt=True)\n    wrap_node = get_wrap_node_from_selected()\n    if wrap_node:\n        data_dir = os.path.join(cmds.workspace(q=True, rd=True), 'data')\n        file_path = cmds.fileDialog2(fileFilter='*.wrap', dialogStyle=2, cap='Import Binding',\n                                     startingDirectory=data_dir, fm=1)\n        if file_path:\n            cmds.cvWrap(wrap_node, im=file_path[0])\n\n\ndef get_wrap_node_from_selected():\n    \"\"\"Get a wrap node from the selected geometry.\"\"\"\n    sel = cmds.ls(sl=True) or []\n    if not sel:\n        raise RuntimeError('No cvWrap found on selected.')\n    if cmds.nodeType(sel[0]) == 'cvWrap':\n        return sel[0]\n    history = cmds.listHistory(sel[0], pdo=0) or []\n    wrap_nodes = [node for node in history if cmds.nodeType(node) == 'cvWrap']\n    if not wrap_nodes:\n        raise RuntimeError('No cvWrap node found on {0}.'.format(sel[0]))\n    if len(wrap_nodes) == 1:\n        return wrap_nodes[0]\n    else:\n        # Multiple wrap nodes are deforming the mesh.  Let the user choose which one\n        # to use.\n        return QtGui.QInputDialog.getItem(None, 'Select cvWrap node', 'cvWrap node:', wrap_nodes)\n\n\ndef destroy_menuitems():\n    \"\"\"Remove the cvWrap items from the menus.\"\"\"\n    global MENU_ITEMS\n    for item in MENU_ITEMS:\n        cmds.deleteUI(item, menuItem=True)\n    MENU_ITEMS = []\n\n\ndef paint_cvwrap_weights(*args, **kwargs):\n    \"\"\"Activates the paint cvWrap weights context.\"\"\"\n    sel = cmds.ls(sl=True)\n    if sel:\n        wrap_node = get_wrap_node_from_selected()\n        if wrap_node:\n            mel.eval('artSetToolAndSelectAttr(\"artAttrCtx\", \"cvWrap.{0}.weights\");'.format(\n                     wrap_node))\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "set(SOURCE_FILES\n    \"pluginMain.cpp\"\n    \"cvWrapCmd.cpp\"\n    \"cvWrapCmd.h\"\n    \"cvWrapDeformer.cpp\"\n    \"cvWrapDeformer.h\"\n    \"bindingio.cpp\"\n    \"bindingio.h\"\n    \"common.cpp\"\n    \"common.h\"\n    \"cvwrap.cl\"\n)\n\nif (WIN32)\n    set(COMPILE_FLAGS \"/arch:AVX\")\nelse()\n    set(COMPILE_FLAGS \"-mavx\")\nendif()\n\nfind_package(Maya REQUIRED)\n\nadd_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})\ntarget_link_libraries(${PROJECT_NAME} PRIVATE Maya::Maya)\ntarget_include_directories(${PROJECT_NAME} PRIVATE Maya::Maya)\ntarget_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_FLAGS})\nMAYA_PLUGIN(${PROJECT_NAME})\n\ninstall(TARGETS ${PROJECT_NAME} ${MAYA_TARGET_TYPE} DESTINATION plug-ins)\ninstall(FILES \"cvwrap.cl\" DESTINATION plug-ins)\n\n"
  },
  {
    "path": "src/bindingio.cpp",
    "content": "#include \"bindingio.h\"\n#include \"cvWrapDeformer.h\"\n\n#include <maya/MGlobal.h>\n#include <maya/MObjectArray.h>\n#include <maya/MFnDoubleArrayData.h>\n#include <maya/MFnIntArrayData.h>\n#include <maya/MFnMatrixData.h>\n#include <maya/MFnWeightGeometryFilter.h>\n\nconst float BindingIO::kWrapFileVersion = 1.0f;\n\ntemplate <>\nvoid WriteAttribute<double, MMatrix>(std::ofstream &out, const MMatrix& attribute) {\n  double values[16];\n  for (int i = 0; i < 4; i++) {\n    for (int j = 0; j < 4; j++) {\n      values[i*4 + j] = attribute[i][j];\n    }\n  }\n  out.write((char *)values, 16 * sizeof(double));\n}\n\ntemplate <>\nvoid ReadAttribute<double, MMatrix>(std::ifstream &in, MMatrix &matrix) {\n  double values[16];\n  in.read((char *)values, 16 * sizeof(double));\n  for (int i = 0; i < 4; i++) {\n    for (int j = 0; j < 4; j++) {\n      matrix[i][j] = values[(i * 4) + j];\n    }\n  }\n}\n\nMStatus BindingIO::ExportBinding(std::ofstream& out, MObject& oWrapNode) {\n  MStatus status;\n  MFnWeightGeometryFilter fnWrapNode(oWrapNode, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  if (fnWrapNode.typeId() != CVWrap::id) {\n    MGlobal::displayError(fnWrapNode.name() + \" is not a cvWrap node.\");\n    CHECK_MSTATUS_AND_RETURN_IT(MS::kFailure);\n  }\n\n  out.write((char *)&kWrapFileVersion, sizeof(float));\n\n  MPlug plugBindData = fnWrapNode.findPlug(CVWrap::aBindData, false, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  // Get the input geometry so we can get the geometry indices\n  MObjectArray outputGeometry;\n  status = fnWrapNode.getOutputGeometry(outputGeometry);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  // Write the number of geometry\n  unsigned int geometryCount = outputGeometry.length();\n  out.write((char *)(&geometryCount), sizeof(geometryCount));\n\n  MIntArray triangleVerts(3);  /**< Storage for the triangle vertex ids. */\n  MFloatArray baryCoords(3);  /**< Storage for the barycentric weights. */\n  for (unsigned int i = 0; i < outputGeometry.length(); ++i) {\n    unsigned int geomIndex = fnWrapNode.indexForOutputShape(outputGeometry[i], &status);\n    // Get the plugs to the binding attributes for this geometry\n    MPlug plugBind = plugBindData.elementByLogicalIndex(geomIndex, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugSampleWeights = plugBind.child(CVWrap::aSampleWeights, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugSampleVerts = plugBind.child(CVWrap::aSampleComponents, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugSampleBindMatrix = plugBind.child(CVWrap::aBindMatrix, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugTriangleVerts = plugBind.child(CVWrap::aTriangleVerts, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugBarycentricWeights = plugBind.child(CVWrap::aBarycentricWeights, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n\n    unsigned int numElements = plugSampleWeights.numElements();\n    out.write((char *)(&numElements), sizeof(numElements));\n  \n    for (unsigned int j = 0; j < numElements; ++j) {\n      // Write the logical index\n      MPlug plugSampleVertElement = plugSampleVerts.elementByPhysicalIndex(j, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      unsigned int logicalIndex = plugSampleVertElement.logicalIndex();\n      out.write((char *)(&logicalIndex), sizeof(logicalIndex));\n\n      // Export sample vertex ids\n      MObject oSampleIds = plugSampleVertElement.asMObject();\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MFnIntArrayData fnIntData(oSampleIds, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MIntArray sampleIds = fnIntData.array();\n      WriteAttribute<int, MIntArray>(out, sampleIds);\n\n      // Export sample weights\n      MObject oWeightData = plugSampleWeights.elementByPhysicalIndex(j, &status).asMObject();\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MFnDoubleArrayData fnDoubleData(oWeightData);\n      MDoubleArray weights = fnDoubleData.array();\n      WriteAttribute<double, MDoubleArray>(out, weights);\n\n      // Export bind matrix\n      MObject oBindMatrix = plugSampleBindMatrix.elementByPhysicalIndex(j, &status).asMObject();\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MFnMatrixData fnMatrixData(oBindMatrix, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      WriteAttribute<double, MMatrix>(out, fnMatrixData.matrix());\n\n      // Export triangle vertices\n      MObject oTriangleVerts = plugTriangleVerts.elementByPhysicalIndex(j, &status).asMObject();\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MFnNumericData fnNumericData(oTriangleVerts, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      fnNumericData.getData3Int(triangleVerts[0], triangleVerts[1], triangleVerts[2]);\n      WriteAttribute<int, MIntArray>(out, triangleVerts);\n\n      // Export the barycentric weights\n      MObject oBaryWeights = plugBarycentricWeights.elementByPhysicalIndex(j, &status).asMObject();\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MFnNumericData fnBaryData(oBaryWeights, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      fnBaryData.getData3Float(baryCoords[0], baryCoords[1], baryCoords[2]);\n      WriteAttribute<float, MFloatArray>(out, baryCoords);\n    }\n  }\n\n  MGlobal::displayInfo(\"Wrap binding exported.\");\n\n  return status;\n}\n\n\n\nMStatus BindingIO::ImportBinding(std::ifstream& in, MObject& oWrapNode) {\n  MStatus status;\n\n  MFnWeightGeometryFilter fnWrapNode(oWrapNode, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MPlug plugBindData = fnWrapNode.findPlug(CVWrap::aBindData, false, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  float version;\n  in.read((char *)(&version), sizeof(float));\n\n  unsigned int geometryCount = 0;\n  in.read((char *)(&geometryCount), sizeof(geometryCount));\n\n  MFnMatrixData fnMatrixData;\n  MFnIntArrayData fnIntData;\n  MFnDoubleArrayData fnDoubleData;\n  MFnNumericData fnNumericData;\n  // We are assuming that the geometryIndices are compact and continuous.  It is possible\n  // that the indices could be sparse, but we will ignore that corner case.\n  for (unsigned int geomIndex = 0; geomIndex < geometryCount; ++geomIndex) {\n    // Get the plugs to the binding attributes for this geometry\n    MPlug plugBind = plugBindData.elementByLogicalIndex(geomIndex, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugSampleWeights = plugBind.child(CVWrap::aSampleWeights, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugSampleVerts = plugBind.child(CVWrap::aSampleComponents, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugSampleBindMatrix = plugBind.child(CVWrap::aBindMatrix, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugTriangleVerts = plugBind.child(CVWrap::aTriangleVerts, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugBarycentricWeights = plugBind.child(CVWrap::aBarycentricWeights, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n\n    unsigned int numElements = plugSampleWeights.numElements();\n    in.read((char *)(&numElements), sizeof(numElements));\n    for (unsigned int i = 0; i < numElements; ++i) {\n      unsigned int logicalIndex = 0;\n      in.read((char *)(&logicalIndex), sizeof(logicalIndex));\n\n      // Sample vert ids.\n      MIntArray sampleIds;\n      ReadAttribute<int, MIntArray>(in, sampleIds);\n      MObject oIntData = fnIntData.create(sampleIds, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      plugSampleVerts.elementByLogicalIndex(logicalIndex, &status).setMObject(oIntData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n\n      // Sample weights\n      MDoubleArray weights;\n      ReadAttribute<double, MDoubleArray>(in, weights);\n      MObject oDoubleData = fnDoubleData.create(weights, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      plugSampleWeights.elementByLogicalIndex(logicalIndex, &status).setMObject(oDoubleData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n\n      // Bind matrix\n      MMatrix bindMatrix;\n      ReadAttribute<double, MMatrix>(in, bindMatrix);\n      MObject oMatrixData = fnMatrixData.create(bindMatrix, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      plugSampleBindMatrix.elementByLogicalIndex(logicalIndex, &status).setMObject(oMatrixData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n\n      // Triangle vertices\n      MIntArray triangleVertices;\n      ReadAttribute<int, MIntArray>(in, triangleVertices);\n      MObject oNumericData = fnNumericData.create(MFnNumericData::k3Int, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      status = fnNumericData.setData3Int(triangleVertices[0], triangleVertices[1],\n                                         triangleVertices[2]);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      plugTriangleVerts.elementByLogicalIndex(logicalIndex, &status).setMObject(oNumericData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n\n      // Barycentric coordinates\n      MFloatArray coords;\n      ReadAttribute<float, MFloatArray>(in, coords);\n      oNumericData = fnNumericData.create(MFnNumericData::k3Float, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      status = fnNumericData.setData3Float(coords[0], coords[1], coords[2]);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      plugBarycentricWeights.elementByLogicalIndex(logicalIndex, &status).setMObject(oNumericData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n    }\n  }\n  MGlobal::displayInfo(\"Wrap binding imported.\");\n\n  return MS::kSuccess;\n}\n"
  },
  {
    "path": "src/bindingio.h",
    "content": "#ifndef CVWRAP_BindingIO_H\n#define CVWRAP_BindingIO_H\n\n#include <maya/MMatrix.h>\n#include <maya/MObject.h>\n#include <maya/MString.h>\n#include <fstream>\n\n/**\n  The BindingIO is used to import and export binding information from a wrap node.\n*/\nclass BindingIO {\n public:\n  /**\n    Exports the binding information to disk.\n  */\n  MStatus ExportBinding(std::ofstream& out, MObject& oWrapNode);\n\n  /**\n    Imports the binding information from disk.\n  */\n  MStatus ImportBinding(std::ifstream& in, MObject& oWrapNode);\n\n  const static float kWrapFileVersion;\n};\n\n/**\n  Convenience function to write a Maya array type to a binary stream.\n  @param[in] out Output stream.\n  @param[in] attribute A Maya array type.\n*/\ntemplate <typename dataType, typename container>\nvoid WriteAttribute(std::ofstream &out, const container& attribute) {\n  unsigned int length = attribute.length();\n  out.write((char *)(&length), sizeof(length));\n  if (length > 0) {\n    dataType * pAttr = new dataType[length];\n    attribute.get(pAttr);\n    out.write((char *)pAttr, length * sizeof(dataType));\n    delete [] pAttr;\n  }\n}\n\n/**\n  Template specialization for MMatrix because they need to be read differently from\n  normal array types.\n*/\ntemplate <>\nvoid WriteAttribute<double, MMatrix>(std::ofstream &out, const MMatrix& attribute);\n\n/**\n  Convenience function to read a Maya array type from a binary stream.\n  @param[in] in Input stream.\n  @param[out] attribute A Maya array type.\n*/\ntemplate <typename dataType, typename container>\nvoid ReadAttribute(std::ifstream &in, container &attribute) {\n  attribute.clear();\n  unsigned int length;\n  in.read((char *)(&length), sizeof(length));\n  if (length > 0) {\n    attribute.setLength(length);\n    dataType* pValues = new dataType[length];\n    in.read((char *)pValues, length * sizeof(dataType));\n    for (unsigned int i = 0; i < length; i++) {\n      attribute[i] = pValues[i];\n    }\n    delete [] pValues;\n  }\n}\n\ntemplate <>\nvoid ReadAttribute<double, MMatrix>(std::ifstream &in, MMatrix &matrix);\n\n\n#endif"
  },
  {
    "path": "src/common.cpp",
    "content": "#include \"common.h\"\n\n#include <maya/MGlobal.h>\n#include <maya/MFnDagNode.h>\n#include <maya/MFnMesh.h>\n#include <maya/MItMeshVertex.h>\n#include <maya/MSelectionList.h>\n#include <algorithm>\n#include <cassert>\n#include <complex>\n#include <set>\n#include <queue>\n#include <utility>\n\n#define NORMALIZATION_INDEX -1\n\nvoid StartProgress(const MString& title, unsigned int count) {\n  if (MGlobal::mayaState() == MGlobal::kInteractive) {\n    MString message = \"progressBar -e -bp -ii true -st \\\"\";\n    message += title;\n    message += \"\\\" -max \";\n    message += count;\n    message += \" $gMainProgressBar;\";\n    MGlobal::executeCommand(message);\n  }\n}\n\n\nvoid StepProgress(int step) {\n  if (MGlobal::mayaState() == MGlobal::kInteractive) {\n    MString message = \"progressBar -e -s \";\n    message += step;\n    message += \" $gMainProgressBar;\";\n    MGlobal::executeCommand(message);\n  }\n}\n\n\nbool ProgressCancelled() {\n  if (MGlobal::mayaState() == MGlobal::kInteractive) {\n    int cmdResult = 0;\n    MGlobal::executeCommand(\"progressBar -query -isCancelled $gMainProgressBar\", cmdResult);\n    return cmdResult != 0;\n  }\n  return false;\n}\n\n\nvoid EndProgress() {\n  if (MGlobal::mayaState() == MGlobal::kInteractive) {\n    MGlobal::executeCommand(\"progressBar -e -ep $gMainProgressBar;\");\n  }\n}\n\n\nbool IsShapeNode(MDagPath& path) {\n  return path.node().hasFn(MFn::kMesh) ||\n         path.node().hasFn(MFn::kNurbsCurve) ||\n         path.node().hasFn(MFn::kNurbsSurface);\n}\n\n\nMStatus GetShapeNode(MDagPath& path, bool intermediate) {\n  MStatus status;\n\n  if (IsShapeNode(path)) {\n    // Start at the transform so we can honor the intermediate flag.\n    path.pop();\n  }\n\n  if (path.hasFn(MFn::kTransform)) {\n    unsigned int shapeCount = path.childCount();\n\n    for (unsigned int i = 0; i < shapeCount; ++i) {\n      status = path.push(path.child(i));\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      if (!IsShapeNode(path)) {\n        path.pop();\n        continue;\n      }\n\n      MFnDagNode fnNode(path, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      if ((!fnNode.isIntermediateObject() && !intermediate) ||\n          (fnNode.isIntermediateObject() && intermediate)) {\n        return MS::kSuccess;\n      }\n      // Go to the next shape\n      path.pop();\n    }\n  }\n\n  // No valid shape node found.\n  return MS::kFailure;\n}\n\n\nMStatus GetDagPath(MString& name, MDagPath& path) {\n  MStatus status;\n  MSelectionList list;\n  status = MGlobal::getSelectionListByName(name, list);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = list.getDagPath(0, path);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  return MS::kSuccess;\n}\n\nMStatus DeleteIntermediateObjects(MDagPath& path) {\n  MStatus status;\n  MDagPath pathMesh(path);\n  while (GetShapeNode(pathMesh, true) == MS::kSuccess) {\n    status = MGlobal::executeCommand(\"delete \" + pathMesh.partialPathName());\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    pathMesh = MDagPath(path);\n  }\n  return MS::kSuccess;\n}\n\nvoid GetBarycentricCoordinates(const MPoint& P, const MPoint& A, const MPoint& B, const MPoint& C,\n                               BaryCoords& coords) {\n  // Compute the normal of the triangle\n  MVector N = (B - A) ^ (C - A);\n  MVector unitN = N.normal();\n\n  // Compute twice area of triangle ABC\n  double areaABC = unitN * N;\n\n  if (areaABC == 0.0) {\n    // If the triangle is degenerate, just use one of the points.\n    coords[0] = 1.0f;\n    coords[1] = 0.0f;\n    coords[2] = 0.0f;\n    return;\n  }\n\n  // Compute a\n  double areaPBC = unitN * ((B - P) ^ (C - P));\n  coords[0] = (float)(areaPBC / areaABC);\n\n  // Compute b\n  double areaPCA = unitN * ((C - P) ^ (A - P));\n  coords[1] = (float)(areaPCA / areaABC);\n\n  // Compute c\n  coords[2] = 1.0f - coords[0] - coords[1];\n}\n\n\nMStatus GetAdjacency(MDagPath& pathMesh, std::vector<std::set<int> >& adjacency) {\n  MStatus status;\n  // Get mesh adjacency.  The adjacency will be all vertex ids on the connected faces.\n  MItMeshVertex itVert(pathMesh, MObject::kNullObj, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MFnMesh fnMesh(pathMesh, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  adjacency.resize(itVert.count());\n  for (; !itVert.isDone(); itVert.next()) {\n    MIntArray faces;\n    status = itVert.getConnectedFaces(faces);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    adjacency[itVert.index()].clear();\n    // Put the vertex ids in a set to avoid duplicates\n    for (unsigned int j = 0; j < faces.length(); ++j) {\n      MIntArray vertices;\n      fnMesh.getPolygonVertices(faces[j], vertices);\n      for (unsigned int k = 0; k < vertices.length(); ++k) {\n        if (vertices[k] != itVert.index()) {\n          adjacency[itVert.index()].insert(vertices[k]);\n        }\n      }\n    }\n  }\n  return MS::kSuccess;\n}\n\n\n/**\n  Used in the CrawlSurface function to keep track of where we are crawling.\n*/\nstruct CrawlData {\n  MPoint sourcePosition;  /**< Where the crawl iteration came from. */\n  double crawlDistance;  /**< How far this crawl iteration has traveled. */\n  int nextVertex;  /**< Where this crawl iteration should go next. */\n};\n\n\nMStatus CrawlSurface(const MPoint& startPoint, const MIntArray& vertexIndices, MPointArray& points, double maxDistance,\n                     std::vector<std::set<int> >& adjacency, std::map<int, double>& distances) {\n  MStatus status;\n  distances[NORMALIZATION_INDEX] = 0.0; // -1 will represent our hit point.\n  double minStartDistance = 999999.0;\n  unsigned int minStartIndex = 0;\n\n  // Instead of a recursive function, which can get pretty slow, we'll use a queue to keep\n  // track of where we are going and where we are coming from.\n  std::queue<CrawlData> verticesToVisit;\n  // Add the initial crawl paths to the queue.\n  for (unsigned int i = 0; i < vertexIndices.length(); ++i) {\n    double distance = startPoint.distanceTo(points[vertexIndices[i]]);\n    // Only crawl to the starting vertices if they are within the radius.\n    if (distance <= maxDistance) {\n      CrawlData root = {startPoint, distance, vertexIndices[i]};\n      verticesToVisit.push(root);\n    }\n    // Track the minimum start distance in case we need to add the closest vertex below.\n    // The minimum must be greater than 0 to make sure we do not use the vertex that is the\n    // same as the startPoint which would create an invalid up vector.\n    if (distance < minStartDistance && distance > 0.000001) {\n      minStartDistance = distance;\n      minStartIndex = vertexIndices[i];\n    }\n  }\n  // If we didn't even reach a vertex in the hit face, or the startPoint is equal to a vertex\n  // on the face, add the closest vertex so we can calculate a proper up vector\n  if (verticesToVisit.size() <= 1) {\n    CrawlData root = {startPoint, maxDistance - 0.001, (int)minStartIndex};\n    verticesToVisit.push(root);\n    distances[minStartIndex] = maxDistance - 0.001;\n  }\n  while (verticesToVisit.size()) {\n    CrawlData next = verticesToVisit.front();\n    verticesToVisit.pop();\n\n    // Extract the data out of the crawl struct\n    int idx = next.nextVertex;\n    MPoint& pt = points[idx];\n    MPoint sourcePoint = next.sourcePosition;\n    double currentCrawlDistance = next.crawlDistance;\n\n    currentCrawlDistance += sourcePoint.distanceTo(pt);\n    if (currentCrawlDistance >= maxDistance) {\n      // If this vertex is outside the radius, no need to crawl anymore from that vertex.\n      continue;\n    }\n    double& savedDistance = distances[idx];\n    if (currentCrawlDistance <= savedDistance || savedDistance == 0.0) {\n      // If this current crawl distance is less then the distance we have saved for this\n      // vertex, use this new crawl distance instead.\n      savedDistance = currentCrawlDistance;\n    } else {\n      // A smaller distance is already stored so we don't want to crawl\n      // from this vertex any further.\n      continue;\n    }\n    // Crawl the adjacent vertices\n    std::set<int>::iterator iter;\n    for (iter = adjacency[idx].begin(); iter != adjacency[idx].end(); ++iter) {\n      CrawlData data = {pt, currentCrawlDistance, *iter};\n      verticesToVisit.push(data);\n    }\n  }\n  assert(distances.size() > 0);\n  \n  return MS::kSuccess;\n}\n\nbool SampleSort(std::pair<int, double> lhs, std::pair<int, double> rhs) {\n  // Ensure that the normalization sample comes last.\n  return (lhs.second < rhs.second) || rhs.first == NORMALIZATION_INDEX; \n}\n\nvoid CalculateSampleWeights(const std::map<int, double>& distances, double radius,\n                            MIntArray& vertexIds, MDoubleArray& weights) {\n  \n  std::map<int, double>::const_iterator itDistance;\n  std::vector<std::pair<int, double> > samples;\n  for (itDistance = distances.begin();\n        itDistance != distances.end();\n        itDistance++) {\n    double x = itDistance->second;\n    double w = 1.0 - (x/radius);\n    samples.push_back(std::pair<int, double>(itDistance->first, w));\n  }\n\n  // Make the samples a multiple of 4 so we can use fast intrinsics!\n  int remainder = 4 - ((samples.size()-1) % 4);\n  if (remainder != 4) {\n    for (int i = 0; i < remainder; ++i) {\n      samples.push_back(std::pair<int, double>(0, 0.0));\n    }\n  }\n\n  unsigned int length = (unsigned int)samples.size();\n  weights.setLength(length);\n  vertexIds.setLength(length);\n  std::sort(samples.begin(), samples.end(), SampleSort);\n  std::vector<std::pair<int, double> >::iterator iter;\n  int ii = 0;\n  double sum = 0.0;\n  for (iter = samples.begin(); iter != samples.end(); ++iter, ++ii) {\n    vertexIds[ii] = (*iter).first;\n    weights[ii] = (*iter).second;\n    sum += (*iter).second;\n  }\n  assert(sum > 0.0);\n  // Normalize the weights\n  for (unsigned int i = 0; i < weights.length(); ++i) {\n    weights[i] /= sum;\n  }\n}\n\n\nvoid CreateMatrix(const MPoint& origin, const MVector& normal, const MVector& up,\n                  MMatrix& matrix) {\n  const MPoint& t = origin;\n  const MVector& y = normal;\n  MVector x = y ^ up;\n  MVector z = x ^ y;\n  // Renormalize vectors\n  x.normalize();\n  z.normalize();\n  matrix[0][0] = x.x; matrix[0][1] = x.y; matrix[0][2] = x.z; matrix[0][3] = 0.0;\n  matrix[1][0] = y.x; matrix[1][1] = y.y; matrix[1][2] = y.z; matrix[1][3] = 0.0;\n  matrix[2][0] = z.x; matrix[2][1] = z.y; matrix[2][2] = z.z; matrix[2][3] = 0.0;\n  matrix[3][0] = t.x; matrix[3][1] = t.y; matrix[3][2] = t.z; matrix[3][3] = 1.0;\n}\n\n\nvoid CalculateBasisComponents(const MDoubleArray& weights, const BaryCoords& coords,\n                              const MIntArray& triangleVertices, const MPointArray& points,\n                              const MFloatVectorArray& normals, const MIntArray& sampleIds,\n                              double* alignedStorage,\n                              MPoint& origin, MVector& up, MVector& normal) {\n  // Start with the recreated point and normal using the barycentric coordinates of the hit point.\n  unsigned int hitIndex = weights.length()-1;\n#ifdef __AVX__\n  __m256d originV = Dot4<MPoint>(coords[0], coords[1], coords[2], 0.0,\n                                points[triangleVertices[0]], points[triangleVertices[1]],\n                                points[triangleVertices[2]], MPoint::origin);\n  __m256d hitNormalV = Dot4<MVector>(coords[0], coords[1], coords[2], 0.0,\n                                normals[triangleVertices[0]], normals[triangleVertices[1]],\n                                normals[triangleVertices[2]], MVector::zero);\n  __m256d hitWeightV = _mm256_set1_pd(weights[hitIndex]);\n  // Create the barycentric point and normal.\n  __m256d normalV = _mm256_mul_pd(hitNormalV, hitWeightV);\n  // Then use the weighted adjacent data.\n  for (unsigned int j = 0; j < hitIndex; j += 4) {\n    __m256d tempNormal = Dot4<MVector>(weights[j], weights[j+1], weights[j+2], weights[j+3],\n                                       normals[sampleIds[j]], normals[sampleIds[j+1]],\n                                       normals[sampleIds[j+2]], normals[sampleIds[j+3]]);\n    normalV = _mm256_add_pd(tempNormal, normalV);\n  }\n\n  _mm256_store_pd(alignedStorage, originV);\n  origin.x = alignedStorage[0];\n  origin.y = alignedStorage[1];\n  origin.z = alignedStorage[2];\n  _mm256_store_pd(alignedStorage, normalV);\n  normal.x = alignedStorage[0];\n  normal.y = alignedStorage[1];\n  normal.z = alignedStorage[2];\n\n  // Calculate the up vector\n  const MPoint& pt1 = points[triangleVertices[0]];\n  const MPoint& pt2 = points[triangleVertices[1]];\n  __m256d p1 = _mm256_set_pd(pt1.w, pt1.z, pt1.y, pt1.x);\n  __m256d p2 = _mm256_set_pd(pt2.w, pt2.z, pt2.y, pt2.x);\n  p1 = _mm256_add_pd(p1, p2);\n  __m256d half = _mm256_set_pd(0.5, 0.5, 0.5, 0.5);\n  p1 = _mm256_mul_pd(p1, half);\n  __m256d upV = _mm256_sub_pd(p1, originV);\n  _mm256_store_pd(alignedStorage, upV);\n  up.x = alignedStorage[0];\n  up.y = alignedStorage[1];\n  up.z = alignedStorage[2];\n#else\n  MVector hitNormal;\n  // Create the barycentric point and normal.\n  for (int i = 0; i < 3; ++i) {\n    origin += points[triangleVertices[i]] * coords[i];\n    hitNormal += MVector(normals[triangleVertices[i]]) * coords[i];\n  }\n  // Use crawl data to calculate normal\n  normal = hitNormal * weights[hitIndex];\n  for (unsigned int j = 0; j < hitIndex; j++) {\n    normal += MVector(normals[sampleIds[j]]) * weights[j];\n  }\n\n  // Calculate the up vector\n  // The triangle vertices are sorted by decreasing barycentric coordinates so the first two are\n  // the two closest vertices in the triangle.\n  up = ((points[triangleVertices[0]] + points[triangleVertices[1]]) * 0.5) - origin;\n#endif\n  normal.normalize();\n  GetValidUp(weights, points, sampleIds, origin, normal, up);\n}\n\n\nvoid GetValidUp(const MDoubleArray& weights, const MPointArray& points,\n                const MIntArray& sampleIds, const MPoint& origin, const MVector& normal,\n                MVector& up) {\n  MVector unitUp = up.normal();\n  // Adjust up if it's parallel to normal or if it's zero length\n  if (std::abs((unitUp * normal) - 1.0) < 0.001 || up.length() < 0.0001) {\n    for (unsigned int j = 0; j < weights.length()-1; ++j) {\n      up -= (points[sampleIds[j]] - origin) * weights[j];\n      unitUp = up.normal();\n      if (std::abs((unitUp * normal) - 1.0) > 0.001 && up.length() > 0.0001) {\n        // If the up and normal vectors are no longer parallel and the up vector has a length,\n        // then we are good to go.\n        break;\n      }\n    }\n    up.normalize();\n  } else {\n    up = unitUp;\n  }\n}\n"
  },
  {
    "path": "src/common.h",
    "content": "/**\n  Contains various helper functions.\n*/\n\n#ifndef CVWRAP_COMMON_H\n#define CVWRAP_COMMON_H\n\n#include <maya/MDagPath.h>\n#include <maya/MDoubleArray.h>\n#include <maya/MFloatVectorArray.h>\n#include <maya/MIntArray.h>\n#include <maya/MMatrix.h>\n#include <maya/MPoint.h>\n#include <maya/MPointArray.h>\n#include <maya/MString.h>\n#include <map>\n#include <vector>\n#include <set>\n\n#ifdef __AVX__\n#include <xmmintrin.h>\n#include <immintrin.h>\n#endif\n\n/**\n  Helper function to start a new progress bar.\n  @param[in] title Status title.\n  @param[in] count Progress bar maximum count.\n*/\nvoid StartProgress(const MString& title, unsigned int count);\n\n\n/**\n  Helper function to increase the progress bar by the specified amount.\n  @param[in] step Step amount.\n*/\nvoid StepProgress(int step);\n\n\n/**\n  Check if the progress has been cancelled.\n  @return true if the progress has been cancelled.\n*/\nbool ProgressCancelled();\n\n\n/**\n  Ends any running progress bar.\n*/\nvoid EndProgress();\n\n\n/**\n  Checks if the path points to a shape node.\n  @param[in] path A dag path.\n  @return true if the path points to a shape node.\n */\nbool IsShapeNode(MDagPath& path);\n\n\n/**\n  Ensures that the given dag path points to a non-intermediate shape node.\n  @param[in,out] path Path to a dag node that could be a transform or a shape.\n  On return, the path will be to a shape node if one exists.\n  @param[in] intermediate true to get the intermediate shape.\n  @return MStatus.\n */\nMStatus GetShapeNode(MDagPath& path, bool intermediate=false);\n\n\n/**\n  Get the MDagPath of an object.\n  @param[in] name Name of a dag node.\n  @param[out] path Storage for the dag path.\n */\nMStatus GetDagPath(MString& name, MDagPath& path);\n\n\n/**\n  Delete all intermediate shapes of the given dag path.\n  @param[in] path MDagPath.\n */\nMStatus DeleteIntermediateObjects(MDagPath& path);\n\n\n/**\n  Helper struct to hold the 3 barycentric coordinates.\n*/\nstruct BaryCoords {\n  float coords[3];\n  float operator[](int index) const { return coords[index]; }\n  float& operator[](int index) { return coords[index]; }\n};\n\n\n/**\n  Get the barycentric coordinates of point P in the triangle specified by points A,B,C.\n  @param[in] P The sample point.\n  @param[in] A Triangle point.\n  @param[in] B Triangle point.\n  @param[in] C Triangle point.\n  @param[out] coords Barycentric coordinates storage.\n*/\nvoid GetBarycentricCoordinates(const MPoint& P, const MPoint& A, const MPoint& B, const MPoint& C,\n                               BaryCoords& coords);\n\n\n/**\n  Get the vertex adjacency of the specified mesh.  The vertex adjacency are the vertex ids\n  of the connected faces of each vertex.\n  @param[in] pathMesh Path to a mesh.\n  @param[out] adjacency Ajdancency storage of the adjancency per vertex id.\n */\nMStatus GetAdjacency(MDagPath& pathMesh, std::vector<std::set<int> >& adjacency);\n\n\n/**\n  Crawls the surface to find all the points within the sampleradius.\n  @param[in] startPoint The position from which to start the crawl.\n  @param[in] vertexIndices The starting vertex indices we want to crawl to.\n  @param[in] points The array of all the mesh points.\n  @param[in] maxDistance The maximum crawl distance.\n  @param[in] adjacency Vertex adjacency data from the GetAdjacency function.\n  @param[out] distances Storage for the distances to the crawled points.\n  @return MStatus\n */\nMStatus CrawlSurface(const MPoint& startPoint, const MIntArray& vertexIndices, MPointArray& points, double maxDistance,\n                     std::vector<std::set<int> >& adjacency, std::map<int, double>& distances);\n\n\n/**\n  Calculates a weight for each vertex within the crawl sample radius.  Vertices that are further\n  away from the origin should have a lesser effect than vertices closer to the origin.\n  @param[in] distances Crawl distances calculated from CrawlSurface.\n  @param[in] radius Sample radius.\n  @param[out] vertexIds Storage for the vertex ids sampled during the crawl.\n  @param[out] weights Storage for the calculated weights of each sampled vertex.\n*/\nvoid CalculateSampleWeights(const std::map<int, double>& distances, double radius,\n                            MIntArray& vertexIds, MDoubleArray& weights);\n\n /**\n   Creates an orthonormal basis using the given point and two axes.\n   @param[in] origin Position.\n   @param[in] normal Normal vector.\n   @param[in] up Up vector.\n   @param[out] matrix Generated matrix.\n */\nvoid CreateMatrix(const MPoint& origin, const MVector& normal, const MVector& up,\n                  MMatrix& matrix);\n\n/**\n  Calculates the components necessary to create a wrap basis matrix.\n  @param[in] weights The sample weights array from the wrap binding.\n  @param[in] coords The barycentric coordinates of the closest point.\n  @param[in] triangleVertices The vertex ids forming the triangle of the closest point.\n  @param[in] points The driver point array.\n  @param[in] normals The driver per-vertex normal array.\n  @param[in] sampleIds The vertex ids on the driver of the current sample.\n  @param[in] alignedStorage double array that is 32 byte aligned for AVX.\n  @param[out] origin The origin of the coordinate system.\n  @param[out] up The up vector of the coordinate system.\n  @param[out] normal The normal vector of the coordinate system.\n*/\nvoid CalculateBasisComponents(const MDoubleArray& weights, const BaryCoords& coords,\n                              const MIntArray& triangleVertices, const MPointArray& points,\n                              const MFloatVectorArray& normals, const MIntArray& sampleIds,\n                              double* alignedStorage,\n                              MPoint& origin, MVector& up, MVector& normal);\n\n/**\n  Ensures that the up and normal vectors are perpendicular to each other.\n  @param[in] weights The sample weights array from the wrap binding.\n  @param[in] points The driver point array.\n  @param[in] sampleIds The vertex ids on the driver of the current sample.\n  @param[in] origin The origin of the coordinate system.\n  @param[in] up The up vector of the coordinate system.\n  @param[out] normal The normal vector of the coordinate system.\n*/\nvoid GetValidUp(const MDoubleArray& weights, const MPointArray& points,\n                const MIntArray& sampleIds, const MPoint& origin, const MVector& normal,\n                MVector& up);\n\n\ntemplate <typename T>\nstruct ThreadData {\n  unsigned int start;\n  unsigned int end;\n  unsigned int numTasks;\n  double* alignedStorage;\n  T* pData;\n\n#ifdef __AVX__\n  ThreadData() {\n    alignedStorage = (double*) _mm_malloc(4*sizeof(double), 256);\n  }\n  ~ThreadData() {\n    _mm_free(alignedStorage);\n  }\n#endif\n};\n\n\n/**\n  Creates the data stuctures that will be sent to each thread.  Divides the vertices into\n  discrete chunks to be evaluated in the threads.\n  @param[in] taskCount The number of individual tasks we want to divide the calculation into.\n  @param[in] elementCount The number of vertices or elements to be divided up.\n  @param[in] taskData The TaskData or BindData object.\n  @param[out] threadData The array of ThreadData objects.  It is assumed the array is of size taskCount.\n*/\ntemplate <typename T>\nvoid CreateThreadData(int taskCount, unsigned int elementCount, T* taskData, ThreadData<T>* threadData) {\n  unsigned int taskLength = (elementCount + taskCount - 1) / taskCount;\n  unsigned int start = 0;\n  unsigned int end = taskLength;\n  int lastTask = taskCount - 1;\n  for(int i = 0; i < taskCount; i++) {\n    if (i == lastTask) {\n      end = elementCount;\n    }\n    threadData[i].start = start;\n    threadData[i].end = end;\n    threadData[i].numTasks = taskCount;\n    threadData[i].pData = taskData;\n\n    start += taskLength;\n    end += taskLength;\n  }\n}\n\n#ifdef __AVX__\n/**\n  Calculates 4 dot products at once.\n  @param[in] w1 Weight vector x element.\n  @param[in] w2 Weight vector y element.\n  @param[in] w3 Weight vector z element.\n  @param[in] w4 Weight vector w element.\n  @param[in] p1 First vector.\n  @param[in] p2 Second vector.\n  @param[in] p3 Third vector.\n  @param[in] p4 Fourth vector.\n  @return A __m256d vector where each element is the corresponding p vector dot product with w.\n */\ntemplate <typename T>\n__m256d Dot4(double w1, double w2, double w3, double w4,\n             const T& p1, const T& p2, const T& p3, const T& p4) {\n  __m256d xxx = _mm256_set_pd(p1.x, p2.x, p3.x, p4.x);\n  __m256d yyy = _mm256_set_pd(p1.y, p2.y, p3.y, p4.y);\n  __m256d zzz = _mm256_set_pd(p1.z, p2.z, p3.z, p4.z);\n  __m256d www = _mm256_set_pd(w1, w2, w3, w4);\n  __m256d xw = _mm256_mul_pd(xxx, www);\n  __m256d yw = _mm256_mul_pd(yyy, www);\n  __m256d zw = _mm256_mul_pd(zzz, www);\n  __m256d ww = _mm256_mul_pd(www, www); // Dummy\n  // low to high: xw0+xw1 yw0+yw1 xw2+xw3 yw2+yw3\n  __m256d temp01 = _mm256_hadd_pd(xw, yw);   \n  // low to high: zw0+zw1 ww0+ww1 zw2+zw3 ww2+ww3\n  __m256d temp23 = _mm256_hadd_pd(zw, ww);\n  // low to high: xw2+xw3 yw2+yw3 zw0+zw1 ww0+ww1\n  __m256d swapped = _mm256_permute2f128_pd(temp01, temp23, 0x21);\n  // low to high: xw0+xw1 yw0+yw1 zw2+zw3 ww2+ww3\n  __m256d blended = _mm256_blend_pd(temp01, temp23, 0xC);\n  // low to high: xw0+xw1+xw2+xw3 yw0+yw1+yw2+yw3 zw0+zw1+zw2+zw3 ww0+ww1+ww2+ww3\n  __m256d dotproduct = _mm256_add_pd(swapped, blended);\n  return dotproduct;\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/cvWrapCmd.cpp",
    "content": "#include \"cvWrapCmd.h\"\n#include \"cvWrapDeformer.h\"\n#include \"bindingio.h\"\n\n#include <maya/MArgDatabase.h>\n#include <maya/MFnDoubleArrayData.h>\n#include <maya/MFnIntArrayData.h>\n#include <maya/MFnMatrixData.h>\n#include <maya/MFnMesh.h>\n#include <maya/MGlobal.h>\n#include <maya/MItDependencyGraph.h>\n#include <maya/MItGeometry.h>\n#include <maya/MItSelectionList.h>\n#include <maya/MMeshIntersector.h>\n#include <maya/MFnSingleIndexedComponent.h>\n#include <maya/MFnWeightGeometryFilter.h>\n#include <maya/MSyntax.h>\n#include <algorithm>\n#include <cassert>\n#include <utility>\n\n#define PROGRESS_STEP 100\n#define TASK_COUNT 32\n\n/**\n  A version number used to support future updates to the binary wrap binding file.\n*/\nconst float kWrapFileVersion = 1.0f;\n\nconst char* CVWrapCmd::kName = \"cvWrap\";\nconst char* CVWrapCmd::kNameFlagShort = \"-n\";\nconst char* CVWrapCmd::kNameFlagLong = \"-name\";\nconst char* CVWrapCmd::kRadiusFlagShort = \"-r\";\nconst char* CVWrapCmd::kRadiusFlagLong = \"-radius\";\nconst char* CVWrapCmd::kNewBindMeshFlagShort = \"-nbm\";\nconst char* CVWrapCmd::kNewBindMeshFlagLong = \"-newBindMesh\";\nconst char* CVWrapCmd::kExportFlagShort = \"-ex\";\nconst char* CVWrapCmd::kExportFlagLong = \"-export\";\nconst char* CVWrapCmd::kImportFlagShort = \"-im\";\nconst char* CVWrapCmd::kImportFlagLong = \"-import\";\nconst char* CVWrapCmd::kBindingFlagShort = \"-b\";\nconst char* CVWrapCmd::kBindingFlagLong = \"-binding\";\nconst char* CVWrapCmd::kRebindFlagShort = \"-rb\";\nconst char* CVWrapCmd::kRebindFlagLong = \"-rebind\";\nconst char* CVWrapCmd::kHelpFlagShort = \"-h\";\nconst char* CVWrapCmd::kHelpFlagLong = \"-help\";\n\n/**\n  Displays command instructions.\n*/\nvoid DisplayHelp() {\n  MString help;\n  help += \"Flags:\\n\"; \n  help += \"-name (-n):          String     Name of the wrap node to create.\\n\"; \n  help += \"-radius (-r):        Double     Sample radius.  Default is 0.1.  The greater the radius,\\n\"; \n  help += \"                                the smoother the deformation but slower performance.\\n\";\n  help += \"-newBindMesh (-nbm)  N/A        Creates a new bind mesh, otherwise the existing bind mesh will be used.\\n\";\n  help += \"-export (-ex):       String     Path to a file to export the binding to.\\n\"; \n  help += \"-import (-im):       String     Path to a file to import the binding from.\\n\"; \n  help += \"-binding (-b):       String     Path to a file to import the binding from on creation.\\n\"; \n  help += \"-rebind (-rb):       String     The name of the wrap node we are rebinding.\\n\"; \n  help += \"-help (-h)           N/A        Display this text.\\n\";\n  MGlobal::displayInfo(help);\n}\n\n\nCVWrapCmd::CVWrapCmd()\n    : radius_(0.1),\n      name_(\"cvWrap#\"),\n      command_(kCommandCreate),\n      useBinding_(false),\n      newBindMesh_(false) {\n}\n\n\nMSyntax CVWrapCmd::newSyntax() {\n  MSyntax syntax;\n  syntax.addFlag(kNameFlagShort, kNameFlagLong, MSyntax::kString);\n  syntax.addFlag(kRadiusFlagShort, kRadiusFlagLong, MSyntax::kDouble);\n  syntax.addFlag(kNewBindMeshFlagShort, kNewBindMeshFlagLong);\n  syntax.addFlag(kExportFlagShort, kExportFlagLong, MSyntax::kString);\n  syntax.addFlag(kImportFlagShort, kImportFlagLong, MSyntax::kString);\n  syntax.addFlag(kBindingFlagShort, kBindingFlagLong, MSyntax::kString);\n  syntax.addFlag(kRebindFlagShort, kRebindFlagLong, MSyntax::kString);\n  syntax.addFlag(kHelpFlagShort, kHelpFlagLong);\n  syntax.setObjectType(MSyntax::kSelectionList, 0, 255);\n  syntax.useSelectionAsDefault(true);\n  return syntax;\n}\n\n\nvoid* CVWrapCmd::creator() {                                \n  return new CVWrapCmd;                    \n}    \n\n\nbool CVWrapCmd::isUndoable() const {\n  return command_ == kCommandCreate;  // Only creation will be undoable\n}\n\n\nMStatus CVWrapCmd::doIt(const MArgList& args) {\n  MStatus status;\n    \n  status = GatherCommandArguments(args);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  if (command_ == kCommandImport || command_ == kCommandExport) {\n    // In import/export mode, get the selected wrap deformer node so we can read/write\n    // data from it.\n    status = selectionList_.getDependNode(0, oWrapNode_);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MFnDependencyNode fnNode(oWrapNode_);\n    if (fnNode.typeId() != CVWrap::id) {\n      MGlobal::displayError(\"No wrap node specified.\");\n      return MS::kFailure;\n    }\n  } else if (command_ == kCommandRebind) {\n    status = GetGeometryPaths();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    status = Rebind();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  } else {\n    // Otherwise get the driver and driven geometry paths.\n    status = GetGeometryPaths();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n\n    // Add the cvWrap creation command to the modifier.\n    MString command = \"deformer -type cvWrap -n \\\"\" + name_ + \"\\\"\";\n    for (unsigned int i = 0; i < pathDriven_.length(); ++i) {\n      MFnDagNode fnDriven(pathDriven_[i]);\n      command += \" \" + fnDriven.partialPathName();\n    }\n    status = dgMod_.commandToExecute(command);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  }\n\n  return redoIt();\n}\n\n\nMStatus CVWrapCmd::GatherCommandArguments(const MArgList& args) {\n  MStatus status;\n  MArgDatabase argData(syntax(), args);\n  argData.getObjects(selectionList_);\n  if (argData.isFlagSet(kHelpFlagShort)) {\n    command_ = kCommandHelp;\n    DisplayHelp();\n    return MS::kSuccess;\n  } else if (argData.isFlagSet(kExportFlagShort)) {\n    command_ = kCommandExport;\n    filePath_ = argData.flagArgumentString(kExportFlagShort, 0, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  } else if (argData.isFlagSet(kImportFlagShort)) {\n    command_ = kCommandImport;\n    filePath_ = argData.flagArgumentString(kImportFlagShort, 0, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  }\n  newBindMesh_ = argData.isFlagSet(kNewBindMeshFlagShort);\n  if (argData.isFlagSet(kRadiusFlagShort)) {\n    radius_ = argData.flagArgumentDouble(kRadiusFlagShort, 0, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    // Make sure radius is positive\n    if (radius_ <= 0.0) {\n      radius_ = 0.001;\n    }\n  }\n  if (argData.isFlagSet(kNameFlagShort)) {\n    name_ = argData.flagArgumentString(kNameFlagShort, 0, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  }\n  if (argData.isFlagSet(kBindingFlagShort)) {\n    useBinding_ = true;\n    filePath_ = argData.flagArgumentString(kBindingFlagShort, 0, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  }\n  if (argData.isFlagSet(kRebindFlagShort)) {\n    command_ = kCommandRebind;\n    // Get the specified wrap node to rebind.\n    MString wrapNode = argData.flagArgumentString(kRebindFlagShort, 0, &status);\n    MSelectionList slist;\n    status = slist.add(wrapNode);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    status = slist.getDependNode(0, oWrapNode_);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MFnDependencyNode fnNode(oWrapNode_, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    if (fnNode.typeId() != CVWrap::id) {\n      MGlobal::displayError(fnNode.name() + \" is not a cvWrap node.\");\n      return MS::kFailure;\n    }\n  }\n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapCmd::GetGeometryPaths() {\n  MStatus status;\n  // The driver is selected last\n  status = selectionList_.getDagPath(selectionList_.length() - 1, pathDriver_, driverComponents_);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = GetShapeNode(pathDriver_);\n  // The driver must be a mesh for this specific algorithm.\n  if (!pathDriver_.hasFn(MFn::kMesh)) {\n    MGlobal::displayError(\"cvWrap driver must be a mesh.\");\n    return MS::kFailure;\n  }\n\n  MItSelectionList iter(selectionList_);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  pathDriven_.clear();\n  drivenComponents_.clear();\n  for (unsigned int i = 0; i < selectionList_.length() - 1; ++i, iter.next()) {\n    MDagPath path;\n    MObject component;\n    iter.getDagPath(path, component);\n    status = GetShapeNode(path);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    pathDriven_.append(path);\n    drivenComponents_.append(component);\n  }\n  \n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapCmd::redoIt() {\n  MStatus status;\n  if (command_ == kCommandImport) {\n    std::ifstream in(filePath_.asChar(), ios::binary);\n    if (!in.is_open()) {\n      MGlobal::displayInfo(\"Unable to open file for importing.\");\n      CHECK_MSTATUS_AND_RETURN_IT(MS::kFailure);\n    }\n    BindingIO exporter;\n    status = exporter.ImportBinding(in, oWrapNode_);\n    in.close();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    return MS::kSuccess;\n  } else if (command_ == kCommandExport) {\n    std::ofstream out(filePath_.asChar(), ios::binary);\n    if (!out.is_open()) {\n      MGlobal::displayError(\"Unable to open file for writing.\");\n      return MS::kFailure;\n    }\n    BindingIO exporter;\n    status = exporter.ExportBinding(out, oWrapNode_);\n    out.close();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    return MS::kSuccess;\n  } else if (command_ == kCommandRebind) {\n    status = dgMod_.doIt();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    return MS::kSuccess;\n  } else if (command_ == kCommandCreate) {\n    status = CreateWrapDeformer();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    return MS::kSuccess;\n  }\n  return MS::kFailure;\n}\n\n   \nMStatus CVWrapCmd::CreateWrapDeformer() {\n  MStatus status;\n  // Create the deformer\n  status = dgMod_.doIt();\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  // Reacquire the paths because on referenced geo, a new driven path is created (the ShapeDeformed).\n  status = GetGeometryPaths();\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  // Get the created wrap deformer node.\n  status = GetLatestWrapNode();\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  MFnDependencyNode fnNode(oWrapNode_, &status);\n  setResult(fnNode.name());\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  // Create a bind mesh so we can run rebind commands.  We need a mesh at the state of the \n  // initial binding in order to properly calculate rebinding information.  We can't use\n  // the intermediate mesh for rebinding because we may not be binding at the rest pose.\n  // Check if this driver already has a bind mesh.\n  MDagPath pathBindMesh;\n  status = GetExistingBindMesh(pathBindMesh);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  if (newBindMesh_ || !pathBindMesh.isValid()) {\n    // No bind mesh exists or the user wants to force create a new one.\n    status = CreateBindMesh(pathBindMesh);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  }\n  status = ConnectBindMesh(pathBindMesh);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  if (useBinding_) {\n    // Import a pre-existing binding.\n    std::ifstream in(filePath_.asChar(), ios::binary);\n    if (!in.is_open()) {\n      MGlobal::displayInfo(\"Unable to open file for importing.\");\n      CHECK_MSTATUS_AND_RETURN_IT(MS::kFailure);\n    }\n    BindingIO exporter;\n    status = exporter.ImportBinding(in, oWrapNode_);\n    in.close();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  } else {\n    MDGModifier dgMod;\n    BindData bindData;\n    status = CalculateBinding(pathBindMesh, bindData, dgMod);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    status = dgMod.doIt();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  }\n\n  // Connect the driver mesh to the wrap deformer.\n  MFnDagNode fnDriver(pathDriver_);\n  MPlug plugDriverMesh = fnDriver.findPlug(\"worldMesh\", false, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = plugDriverMesh.selectAncestorLogicalIndex(0, plugDriverMesh.attribute());\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MPlug plugDriverGeo(oWrapNode_, CVWrap::aDriverGeo);\n  MDGModifier dgMod;\n  dgMod.connect(plugDriverMesh, plugDriverGeo);\n  status = dgMod.doIt();\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapCmd::GetLatestWrapNode() {\n  MStatus status;\n  MObject oDriven = pathDriven_[0].node();\n  \n  // Since we use MDGModifier to execute the deformer command, we can't get\n  // the created deformer node, so we need to find it in the deformation chain.\n  MItDependencyGraph itDG(oDriven,\n                          MFn::kGeometryFilt,\n                          MItDependencyGraph::kUpstream, \n                          MItDependencyGraph::kDepthFirst,\n                          MItDependencyGraph::kNodeLevel, \n                          &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MObject oDeformerNode;\n  for (; !itDG.isDone(); itDG.next()) {\n    oDeformerNode = itDG.currentItem();\n    MFnDependencyNode fnNode(oDeformerNode, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    if (fnNode.typeId() == CVWrap::id) {\n      oWrapNode_ = oDeformerNode;\n      return MS::kSuccess;\n    }\n  }\n  return MS::kFailure;\n}\n\n\nMStatus CVWrapCmd::CreateBindMesh(MDagPath& pathBindMesh) {\n  MStatus status;\n  MStringArray duplicate;\n  MFnDependencyNode fnWrap(oWrapNode_, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MFnDagNode fnDriver(pathDriver_);\n\n  // Calling mesh.duplicate() can give incorrect results due to tweaks and such.\n  // We are doing the duplicate here rather than the MDGModifier because we need the name\n  // of the duplicated geometry and it would not be reliable to do it from the modifier.\n  MGlobal::executeCommand(\"duplicate -rr -n \" + fnWrap.name() + \"Base \" + fnDriver.partialPathName(), duplicate);\n  status = GetDagPath(duplicate[0], pathBindMesh);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = DeleteIntermediateObjects(pathBindMesh);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  bindMeshes_.append(duplicate[0]);\n\n  // Hide the duplicate\n  MFnDagNode fnBindMesh(pathBindMesh, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MPlug plug = fnBindMesh.findPlug(\"visibility\", false, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = plug.setBool(false);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  \n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapCmd::ConnectBindMesh(MDagPath& pathBindMesh) {\n  MStatus status;\n  // Connect the bind mesh to the wrap node\n  status = GetShapeNode(pathBindMesh);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MFnDagNode fnBindMeshShape(pathBindMesh, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MPlug plugBindMessage = fnBindMeshShape.findPlug(\"message\", false, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MPlug plugBindMesh(oWrapNode_, CVWrap::aBindDriverGeo);\n  MDGModifier dgMod;\n  dgMod.connect(plugBindMessage, plugBindMesh);\n  status = dgMod.doIt();\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapCmd::CalculateBinding(MDagPath& pathBindMesh, BindData& bindData,\n                                    MDGModifier& dgMod) {\n  MStatus status;\n  bindData.radius = radius_;\n\n  // Store the bind mesh information.\n  // Pre-gather the data from Maya so we can multithread the binding process\n  bindData.driverMatrix = pathBindMesh.inclusiveMatrix();\n  MObject oBindMesh = pathBindMesh.node();\n  status = bindData.intersector.create(oBindMesh, bindData.driverMatrix);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  // We need the adjacency of each vertex in order to crawl the mesh.\n  status = GetAdjacency(pathBindMesh, bindData.adjacency);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MFnMesh fnBindMesh(pathBindMesh, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  fnBindMesh.getPoints(bindData.driverPoints, MSpace::kWorld);\n  fnBindMesh.getVertexNormals(false, bindData.driverNormals, MSpace::kWorld);\n  bindData.perFaceVertices.resize(fnBindMesh.numPolygons());\n  bindData.perFaceTriangleVertices.resize(fnBindMesh.numPolygons());\n  MIntArray vertexCount, vertexList, triangleCounts, triangleVertices;\n  status = fnBindMesh.getVertices(vertexCount, vertexList);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = fnBindMesh.getTriangles(triangleCounts, triangleVertices);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  for (unsigned int faceId = 0, iter = 0, triIter = 0; faceId < vertexCount.length(); ++faceId) {\n    bindData.perFaceVertices[faceId].clear();\n    for (int i = 0; i < vertexCount[faceId]; ++i, ++iter) {\n      bindData.perFaceVertices[faceId].append(vertexList[iter]);\n    }\n    bindData.perFaceTriangleVertices[faceId].resize(triangleCounts[faceId]);\n    for (int triId = 0; triId < triangleCounts[faceId]; ++triId) {\n      bindData.perFaceTriangleVertices[faceId][triId].setLength(3);\n      bindData.perFaceTriangleVertices[faceId][triId][0] = triangleVertices[triIter++];\n      bindData.perFaceTriangleVertices[faceId][triId][1] = triangleVertices[triIter++];\n      bindData.perFaceTriangleVertices[faceId][triId][2] = triangleVertices[triIter++];\n    }\n  }\n\n  // Calculate the binding for each deformed geometry\n  MPlug plugBindData(oWrapNode_, CVWrap::aBindData);\n  MFnMatrixData fnMatrixData;\n  for (unsigned int geomIndex = 0; geomIndex < pathDriven_.length(); ++geomIndex) {\n    // Get the plugs to the binding attributes for this geometry\n    MPlug plugBind = plugBindData.elementByLogicalIndex(geomIndex, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugSampleWeights = plugBind.child(CVWrap::aSampleWeights, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugSampleVerts = plugBind.child(CVWrap::aSampleComponents, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugSampleBindMatrix = plugBind.child(CVWrap::aBindMatrix, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugTriangleVerts = plugBind.child(CVWrap::aTriangleVerts, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MPlug plugBarycentricWeights = plugBind.child(CVWrap::aBarycentricWeights, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n\n    // Use the intermediate object for the binding.  This assumes the intermediate object\n    // has the same component count as the displayed shape.\n    MDagPath pathDriven(pathDriven_[geomIndex]);\n    status = GetShapeNode(pathDriven, true);\n    if (MFAIL(status)) {\n      pathDriven = pathDriven_[geomIndex];\n    }\n    MItGeometry itGeo(pathDriven, drivenComponents_[geomIndex], &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    int geoCount = itGeo.count();\n\n    status = itGeo.allPositions(bindData.inputPoints, MSpace::kWorld);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    bindData.sampleIds.resize(itGeo.count());\n    bindData.weights.resize(itGeo.count());\n    bindData.bindMatrices.setLength(itGeo.count());\n    bindData.coords.resize(itGeo.count());\n    bindData.triangleVertices.resize(itGeo.count());\n\n    // Send off the threads to calculate the binding.\n    ThreadData<BindData> threadData[TASK_COUNT];\n    CreateThreadData<BindData>(TASK_COUNT, itGeo.count(), &bindData, threadData);\n    MThreadPool::init();\n    MThreadPool::newParallelRegion(CreateTasks, (void *)threadData);\n    MThreadPool::release();\n\n    for (int ii = 0; !itGeo.isDone(); itGeo.next(), ++ii) {\n      // Store all the binding data for this component\n      // Note for nurbs surfaces the indices may not be continuous.\n      int logicalIndex = itGeo.index();\n      // Store sample vert ids.\n      MFnIntArrayData fnIntData;\n      MObject oIntData = fnIntData.create(bindData.sampleIds[ii], &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MPlug plugSampleVertsElement = plugSampleVerts.elementByLogicalIndex(logicalIndex, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      status = dgMod.newPlugValue(plugSampleVertsElement, oIntData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n\n      // Store sample weights\n      MFnDoubleArrayData fnDoubleData;\n      MObject oDoubleData = fnDoubleData.create(bindData.weights[ii], &status);\n      assert(bindData.weights[ii].length() > 0);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MPlug plugSampleWeightsElement = plugSampleWeights.elementByLogicalIndex(logicalIndex,\n                                                                               &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      status = dgMod.newPlugValue(plugSampleWeightsElement, oDoubleData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n\n      // Store bind matrix\n      MObject oMatrixData = fnMatrixData.create(bindData.bindMatrices[ii], &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MPlug plugSampleBindMatrixElement = plugSampleBindMatrix.elementByLogicalIndex(logicalIndex,\n                                                                                     &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      status = dgMod.newPlugValue(plugSampleBindMatrixElement, oMatrixData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n\n      // Store triangle vertices\n      MFnNumericData fnNumericData;\n      MObject oNumericData = fnNumericData.create(MFnNumericData::k3Int, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      status = fnNumericData.setData3Int(bindData.triangleVertices[ii][0],\n                                         bindData.triangleVertices[ii][1],\n                                         bindData.triangleVertices[ii][2]);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MPlug plugTriangleVertsElement = plugTriangleVerts.elementByLogicalIndex(logicalIndex,\n                                                                               &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      status = dgMod.newPlugValue(plugTriangleVertsElement, oNumericData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n\n      // Store barycentric coordinates\n      oNumericData = fnNumericData.create(MFnNumericData::k3Float, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      status = fnNumericData.setData3Float(bindData.coords[ii][0], bindData.coords[ii][1],\n                                           bindData.coords[ii][2]);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      MPlug plugBarycentricWeightsElement = plugBarycentricWeights.elementByLogicalIndex(\n        logicalIndex, &status);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      status = dgMod.newPlugValue(plugBarycentricWeightsElement, oNumericData);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n    }\n  }\n  return MS::kSuccess;\n}\n\n\nvoid CVWrapCmd::CreateTasks(void *data, MThreadRootTask *pRoot) {\n  ThreadData<BindData>* threadData = static_cast<ThreadData<BindData>*>(data);\n\n  if (threadData) {\n    int numTasks = threadData[0].numTasks;\n    for(int i = 0; i < numTasks; i++) {\n      MThreadPool::createTask(CalculateBindingTask, (void *)&threadData[i], pRoot);\n    }\n    MThreadPool::executeAndJoin(pRoot);\n  }\n}\n\nbool SortCoords(std::pair<int, float> lhs, std::pair<int, float> rhs) {\n  return (lhs.second > rhs.second); \n}\n\n\nMThreadRetVal CVWrapCmd::CalculateBindingTask(void *pParam) {\n  ThreadData<BindData>* pThreadData = static_cast<ThreadData<BindData>*>(pParam);\n  double*& alignedStorage = pThreadData->alignedStorage;\n  BindData* pData = pThreadData->pData;\n  MMeshIntersector& intersector = pData->intersector;\n  MMeshIntersector& subsetIntersector = pData->subsetIntersector;\n  MPointArray& inputPoints = pData->inputPoints;\n  MPointArray& driverPoints = pData->driverPoints;\n  MFloatVectorArray& driverNormals = pData->driverNormals;\n  std::vector<std::set<int> >& adjacency = pData->adjacency;\n  std::vector<MIntArray>& sampleIds = pData->sampleIds;\n  std::vector<MDoubleArray>& weights = pData->weights;\n  std::vector<BaryCoords>& coords = pData->coords;\n  std::vector<MIntArray>& triangleVertices = pData->triangleVertices;\n  MMatrixArray& bindMatrices = pData->bindMatrices;\n\n  double radius = pData->radius;\n\n  MMatrix& driverMatrix = pData->driverMatrix;\n  std::vector<MIntArray>& perFaceVertices = pData->perFaceVertices;\n  std::vector<std::vector<MIntArray> >& perFaceTriangleVertices  = pData->perFaceTriangleVertices;\n\n  unsigned int taskStart = pThreadData->start;\n  unsigned int taskEnd = pThreadData->end;\n\n  // Pre-allocate the aligned storage for intrinsics calculation so we are not dynamically allocating\n  // memory in the loop.\n  std::vector<std::pair<int, float> > sortedCoords(3);\n  for (unsigned int i = taskStart; i < taskEnd; ++i) {\n    if (i >= inputPoints.length()) {\n      break;\n    }\n    // We need to calculate a bind matrix for each component.\n    // The closest point will be the origin of the coordinate system.\n    // The weighted normal of the vertices in the sample radius will be one axis.\n    // The weight vector from the closest point to the sample vertices will be the other axis.\n\n    MPoint inputPoint = inputPoints[i];\n    MPointOnMesh pointOnMesh;\n    if (subsetIntersector.isCreated()) {\n      // If we are rebinding, limit the closest point to the subset.\n      subsetIntersector.getClosestPoint(inputPoint, pointOnMesh);\n      inputPoint = MPoint(pointOnMesh.getPoint()) * driverMatrix;\n    }\n\n    intersector.getClosestPoint(inputPoint, pointOnMesh);\n    int faceId = pointOnMesh.faceIndex();\n    int triangleId = pointOnMesh.triangleIndex();\n\n    // Put point in world space so we can calculate the proper bind matrix.\n    MPoint closestPoint = MPoint(pointOnMesh.getPoint()) * driverMatrix;\n\n    // Get barycentric coordinates of closestPoint\n    triangleVertices[i] = perFaceTriangleVertices[faceId][triangleId];\n    GetBarycentricCoordinates(closestPoint, driverPoints[triangleVertices[i][0]],\n                              driverPoints[triangleVertices[i][1]],\n                              driverPoints[triangleVertices[i][2]],\n                              coords[i]);\n\n    // Sort coords highest to lowest so we can easility calculate the up vector\n    for (int j = 0; j < 3; ++j) {\n      sortedCoords[j] = std::pair<int, float>(triangleVertices[i][j], coords[i][j]);\n    }\n    std::sort(sortedCoords.begin(), sortedCoords.end(), SortCoords);\n    for (int j = 0; j < 3; ++j) {\n      triangleVertices[i][j] = sortedCoords[j].first;\n      coords[i][j] = sortedCoords[j].second;\n    }\n\n    // Get vertices of closest face so we can crawl out from them.\n    MIntArray& vertexList = perFaceVertices[faceId];\n\n    // Crawl the surface to find all the vertices within the sample radius.\n    std::map<int, double> distances;\n    CrawlSurface(closestPoint, vertexList, driverPoints, radius, adjacency, distances);\n\n    // Calculate the weight values per sampled vertex\n    CalculateSampleWeights(distances, radius, sampleIds[i], weights[i]);\n\n    // Get the components that form the orthonormal basis.\n    MPoint origin;\n    MVector up;\n    MVector normal;\n    CalculateBasisComponents(weights[i], coords[i], triangleVertices[i], driverPoints,\n                             driverNormals, sampleIds[i], alignedStorage, origin, up, normal);\n    CreateMatrix(origin, normal, up, bindMatrices[i]);\n    bindMatrices[i] = bindMatrices[i].inverse();\n  }\n  return 0;\n}\n\n\nMStatus CVWrapCmd::GetExistingBindMesh(MDagPath &pathBindMesh) {\n  MStatus status;\n  MObject oDriver = pathDriver_.node();\n  MFnDependencyNode fnDriver(oDriver, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  \n  // We'll find the bind mesh associated with the driver mesh by traversing the mesh connections\n  // through the cvWrap node.\n  MPlug plugOutGeom = fnDriver.findPlug(\"worldMesh\", false, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = plugOutGeom.selectAncestorLogicalIndex(0, plugOutGeom.attribute());\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MPlugArray geomPlugs;\n  plugOutGeom.connectedTo(geomPlugs, false, true);\n  for (unsigned int i = 0; i < geomPlugs.length(); i++) {\n    // First iterate through the outMesh connections to find a cvWrap node.\n    MObject oThisNode = geomPlugs[i].node();\n    MFnDependencyNode fnNode(oThisNode);\n    if (fnNode.typeId() == CVWrap::id) {\n      status = GetBindMesh(oThisNode, pathBindMesh);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n      return MS::kSuccess;\n    }\n  }\n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapCmd::Rebind() {\n  MStatus status;\n\n  // Create bind mesh based off of specified faces\n  MDagPath pathDriverSubset;\n  status = CreateRebindSubsetMesh(pathDriverSubset);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  // Initialize the subset intersector to enable the rebind during the threaded calculation.\n  BindData bindData;\n  MObject oBindSubsetMesh = pathDriverSubset.node();\n  status = bindData.subsetIntersector.create(oBindSubsetMesh, pathDriverSubset.inclusiveMatrix());\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  MDagPath pathBindMesh;\n  status = GetBindMesh(oWrapNode_, pathBindMesh);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  status = CalculateBinding(pathBindMesh, bindData, dgMod_);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  // Delete the subset mesh since we don't need it anymore\n  pathDriverSubset.pop();\n  status = MGlobal::executeCommand(\"delete \" + pathDriverSubset.partialPathName());\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapCmd::GetBindMesh(MObject& oWrapNode, MDagPath& pathBindMesh) {\n  MStatus status;\n  // Get the bind mesh connected to the message attribute of the wrap deformer\n  MPlug plugBindMesh(oWrapNode, CVWrap::aBindDriverGeo);\n  MPlugArray plugs;\n  plugBindMesh.connectedTo(plugs, true, false, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  if (plugs.length() == 0) {\n    MGlobal::displayError(\"Unable to rebind.  No bind mesh is connected.\");\n    return MS::kFailure;\n  }\n  MObject oBindMesh = plugs[0].node();\n  status = MDagPath::getAPathTo(oBindMesh, pathBindMesh);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  return MS::kSuccess;\n}\n\n\n\nMStatus CVWrapCmd::CreateRebindSubsetMesh(MDagPath& pathDriverSubset) {\n  // We will create the mesh subset by deleting all the non-selected faces.\n  MStatus status;\n\n  MDagPath pathBindMesh;\n  status = GetBindMesh(oWrapNode_, pathBindMesh);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MFnMesh fnBindMesh(pathBindMesh);\n\n  // Duplicate the bind mesh to create subset\n  MStringArray duplicate;\n  // Calling mesh.duplicate() gave jacked results.\n  status = MGlobal::executeCommand(\"duplicate -rr \" + fnBindMesh.partialPathName(), duplicate);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = GetDagPath(duplicate[0], pathDriverSubset);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = DeleteIntermediateObjects(pathDriverSubset);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  // Get selected driver faces\n  MFnSingleIndexedComponent fnDriverComp(driverComponents_, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MIntArray driverFaces;\n  status = fnDriverComp.getElements(driverFaces);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  int numFacesToDelete = fnBindMesh.numPolygons() - driverFaces.length();\n  if (numFacesToDelete) {\n    // Get all the face ids to delete.\n    MIntArray facesToDelete;\n    int selectedFaceIndex = 0;\n    for (int i = 0; i < fnBindMesh.numPolygons(); i++) {\n      if (i != driverFaces[selectedFaceIndex]) {\n        facesToDelete.append(i);\n      } else {\n        selectedFaceIndex++;\n      }\n    }\n\n    MFnSingleIndexedComponent fnDeleteComp;\n    MObject oFacesToDelete = fnDeleteComp.create(MFn::kMeshPolygonComponent, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    status = fnDeleteComp.addElements(facesToDelete);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MSelectionList deleteList;\n    status = deleteList.add(pathDriverSubset, oFacesToDelete);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    status = MGlobal::setActiveSelectionList(deleteList);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    status = MGlobal::executeCommand(\"delete;\");\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    // Reacquire the the dag path since it is invalid now after deleting the faces.\n    status = GetDagPath(duplicate[0], pathDriverSubset);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    status = GetShapeNode(pathDriverSubset);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n  }\n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapCmd::undoIt() {\n  MStatus status;\n  status = dgMod_.undoIt();\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  if (bindMeshes_.length()) {\n    // Delete any created bind meshes.\n    MDGModifier mod;\n    for (unsigned int i = 0; i < bindMeshes_.length(); i++) {\n      status = mod.commandToExecute(\"delete \" + bindMeshes_[i]);\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n    }\n    status = mod.doIt();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    bindMeshes_.clear();\n  }\n\n  return MS::kSuccess;\n}\n"
  },
  {
    "path": "src/cvWrapCmd.h",
    "content": "#ifndef CVWRAPCMD_H\n#define CVWRAPCMD_H\n\n#include <maya/MArgList.h>\n#include <maya/MDagPath.h>\n#include <maya/MDagPathArray.h>\n#include <maya/MDGModifier.h>\n#include <maya/MFloatArray.h>\n#include <maya/MFloatVectorArray.h>\n#include <maya/MMatrixArray.h>\n#include <maya/MMeshIntersector.h>\n#include <maya/MObjectArray.h>\n#include <maya/MPlug.h>\n#include <maya/MPointArray.h>\n#include <maya/MSelectionList.h>\n#include <maya/MString.h>\n#include <maya/MStringArray.h>\n#include <maya/MThreadPool.h>\n\n#include <maya/MPxCommand.h>\n\n#include <stdio.h>\n#include <math.h>\n#include <fstream>\n#include <iostream>\n#include <vector>\n#include <map>\n\n#include \"common.h\"\n\nstruct BindData {\n  MPointArray inputPoints;  /**< The world space points of the geometry to be wrapped. */\n  MPointArray driverPoints;  /**< The world space points of the driver geometry. */\n  MFloatVectorArray driverNormals;  /**< The world space normals of the driver geometry. */\n  std::vector<MIntArray> perFaceVertices;  /**< The per-face vertex ids of the driver. */\n  std::vector<std::vector<MIntArray> > perFaceTriangleVertices;  /**< The per-face per-triangle vertex ids of the driver. */\n  MMeshIntersector intersector;  /**< Closest point intersector on the driver mesh. */\n  MMeshIntersector subsetIntersector;  /**< Closest point intersector on a subset mesh if we are rebinding. */\n  std::vector<std::set<int> > adjacency;  /**< Driver adjacency for surface crawling. */\n  MMatrix driverMatrix;  /**< Driver matrix to convert closest points into world space. */\n  double radius;  /**< Max crawl sample radius. */\n\n  /**\n    Elements calculated in the threads.\n  */\n  std::vector<MIntArray> sampleIds;\n  std::vector<MDoubleArray> weights;\n  MMatrixArray bindMatrices;\n  std::vector<BaryCoords> coords;\n  std::vector<MIntArray> triangleVertices;\n};\n\n\n/**\n  The cvWrap command is used to create new cvWrap deformers and to import and export\n  wrap bindings.\n*/\nclass CVWrapCmd : public MPxCommand {              \n public:\n  enum CommandMode { kCommandCreate, kCommandExport, kCommandImport, kCommandHelp, kCommandRebind };\n  CVWrapCmd();              \n  virtual MStatus  doIt(const MArgList&);\n  virtual MStatus  undoIt();\n  virtual MStatus  redoIt();\n  virtual bool isUndoable() const;\n  static void* creator();    \n  static MSyntax newSyntax();\n\n  /**\n    Distributes the ThreadData objects to the parallel threads.\n    @param[in] data The user defined data.  In this case, the ThreadData array.\n    @param[in] pRoot Maya's root task.\n  */\n  static void CreateTasks(void *data, MThreadRootTask *pRoot);\n  static MThreadRetVal CalculateBindingTask(void *pParam);\n\n  const static char* kName;  /**< The name of the command. */\n  \n  /**\n    Specifies the name of the cvWrap node.\n  */\n  const static char* kNameFlagShort;\n  const static char* kNameFlagLong;\n  \n  /**\n    Specifies the sample radius of the binding.\n  */\n  const static char* kRadiusFlagShort;\n  const static char* kRadiusFlagLong;\n\n  /**\n    Specifies that a new bind mesh should be created.  The bind mesh is only used for rebinding\n    vertices and can be deleted at any time.  Sometimes, artists may want to wrap different\n    geometry with the same mesh.  By default the command will reuse the same bind mesh for a driver,\n    but if new geometry is being wrapped at a different pose, a new bind mesh should be created\n    in order to correctly rebind.\n  */\n  const static char* kNewBindMeshFlagShort;\n  const static char* kNewBindMeshFlagLong;\n\n  /**\n    Export file path.\n  */\n  const static char* kExportFlagShort;\n  const static char* kExportFlagLong;\n\n  /**\n    Import file path.\n  */\n  const static char* kImportFlagShort;\n  const static char* kImportFlagLong;\n  \n  /**\n    Path of a binding on disk rather than calculating binding from scratch.\n  */\n  const static char* kBindingFlagShort;  \n  const static char* kBindingFlagLong;\n\n  /**\n    Specifies that the user wants to rebind the select vertices.\n  */\n  const static char* kRebindFlagShort;\n  const static char* kRebindFlagLong;\n\n  /**\n    Displays help.\n  */\n  const static char* kHelpFlagShort;\n  const static char* kHelpFlagLong;\n\n private:\n  /**\n    Gathers all the command arguments and sets necessary command states.\n    @param[in] args Maya MArgList.\n  */\n  MStatus GatherCommandArguments(const MArgList& args);\n\n  /**\n    Acquires the driver and driven dag paths from the input selection list.\n  */\n  MStatus GetGeometryPaths();\n\n  /**\n    Creates a new wrap deformer.\n  */\n  MStatus CreateWrapDeformer();\n\n  /**\n    Gets the latest cvWrap node in the history of the deformed shape.\n  */\n  MStatus GetLatestWrapNode();\n\n  /**\n    Create a new bind mesh and connect it to the wrap node.\n    The bind mesh the mesh at the time of binding and is used to calculate binding information.\n  */\n  MStatus CreateBindMesh(MDagPath& pathBindMesh);\n\n  /**\n    Connects the bind mesh message attribute to the wrap deformer.\n  */\n  MStatus ConnectBindMesh(MDagPath& pathBindMesh);\n\n\n  /**\n    Calculates the binding data for the wrap deformer to work.\n    @param[in] pathBindMesh The path to the mesh to bind to.\n    @param[in] bindData The structure containing all the bind information.\n    @param[in,out] dgMod The modifier to hold all the plug operations.\n  */\n  MStatus CalculateBinding(MDagPath& pathBindMesh, BindData& bindData, MDGModifier& dgMod);\n    \n  /**\n    Gets the MDagPath of any existing bind wrap mesh so we don't have to duplicate it for each\n    new wrap.\n    @param[out] pathBindMesh Storage for path to an existing bind mesh\n  */\n  MStatus GetExistingBindMesh(MDagPath &pathBindMesh);\n\n  /**\n    Calculates new binding data for the selected components.\n  */\n  MStatus Rebind();\n\n  /**\n    Get the bind mesh connected to the wrap node.\n    @param[in] oWrapNode MObject to a cvWrap node..\n    @param[out] pathBindMesh The path to the bind mesh.\n  */\n  MStatus GetBindMesh(MObject& oWrapNode, MDagPath& pathBindMesh);\n\n\n  /**\n    Creates the mesh with the subset of faces used to calculate the rebind.\n    @param[out] pathDriverSubset Path the new driver subset mesh.\n  */\n  MStatus CreateRebindSubsetMesh(MDagPath& pathDriverSubset);\n\n\n  MString name_;  /**< Name of cvWrap node to create. */\n  double radius_;  /**< Binding sample radius. */\n  CommandMode command_;\n  MString filePath_;\n  bool useBinding_;\n  bool newBindMesh_;\n  MSelectionList selectionList_;  /**< Selected command input nodes. */\n  MObject oWrapNode_;  /**< MObject to the cvWrap node in focus. */\n  MDagPath pathDriver_;  /**< Path to the shape wrapping the other shape. */\n  MObject driverComponents_;  /**< Selected driver components used for rebinding. */\n  MDagPathArray pathDriven_;  /**< Paths to the shapes being wrapped. */\n  MObjectArray drivenComponents_;  /**< Selected driven components used for rebinding. */\n  MDGModifier dgMod_;\n  MStringArray bindMeshes_;\n\n  \n};  \n\n#endif\n"
  },
  {
    "path": "src/cvWrapDeformer.cpp",
    "content": "#include \"cvWrapDeformer.h\"\n#include <maya/MFnCompoundAttribute.h>\n#include <maya/MFnDoubleArrayData.h>\n#include <maya/MFnIntArrayData.h>\n#include <maya/MFnMatrixAttribute.h>\n#include <maya/MFnMesh.h>\n#include <maya/MFnMessageAttribute.h>\n#include <maya/MFnNumericAttribute.h>\n#include <maya/MFnTypedAttribute.h>\n#include <maya/MGlobal.h>\n#include <maya/MItGeometry.h>\n#include <maya/MNodeMessage.h>\n#include <maya/MPlugArray.h>\n#include <cassert>\n\nMTypeId CVWrap::id(0x0011580B);\n\nconst char* CVWrap::kName = \"cvWrap\";\nMObject CVWrap::aBindDriverGeo;\nMObject CVWrap::aDriverGeo;\nMObject CVWrap::aBindData;\nMObject CVWrap::aSampleComponents;\nMObject CVWrap::aSampleWeights;\nMObject CVWrap::aTriangleVerts;\nMObject CVWrap::aBarycentricWeights;\nMObject CVWrap::aBindMatrix;\nMObject CVWrap::aNumTasks;\nMObject CVWrap::aScale;\n\nMStatus CVWrap::initialize() {\n  MFnCompoundAttribute cAttr;\n  MFnMatrixAttribute mAttr;\n  MFnMessageAttribute meAttr;\n  MFnTypedAttribute tAttr;\n  MFnNumericAttribute nAttr;\n  MStatus status;\n\n  aDriverGeo = tAttr.create(\"driver\", \"driver\", MFnData::kMesh);\n  status = addAttribute(aDriverGeo);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = attributeAffects(aDriverGeo, outputGeom);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  aBindDriverGeo = meAttr.create(\"bindMesh\", \"bindMesh\");\n  status = addAttribute(aBindDriverGeo);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  /* Each outputGeometry needs:\n  -- bindData\n     | -- sampleComponents\n     | -- sampleWeights\n     | -- triangleVerts\n     | -- barycentricWeights\n     | -- bindMatrix\n  */\n\n  aSampleComponents = tAttr.create(\"sampleComponents\", \"sampleComponents\", MFnData::kIntArray);\n  tAttr.setArray(true);\n\n  aSampleWeights = tAttr.create(\"sampleWeights\", \"sampleWeights\", MFnData::kDoubleArray);\n  tAttr.setArray(true);\n\n  aTriangleVerts = nAttr.create(\"triangleVerts\", \"triangleVerts\", MFnNumericData::k3Int);\n  nAttr.setArray(true);\n\n  aBarycentricWeights = nAttr.create(\"barycentricWeights\", \"barycentricWeights\", MFnNumericData::k3Float);\n  nAttr.setArray(true);\n\n  aBindMatrix = mAttr.create(\"bindMatrix\", \"bindMatrix\");\n  mAttr.setDefault(MMatrix::identity);\n  mAttr.setArray(true);\n\n  aBindData = cAttr.create(\"bindData\", \"bindData\");\n  cAttr.setArray(true);\n  cAttr.addChild(aSampleComponents);\n  cAttr.addChild(aSampleWeights);\n  cAttr.addChild(aTriangleVerts);\n  cAttr.addChild(aBarycentricWeights);\n  cAttr.addChild(aBindMatrix);\n  status = addAttribute(aBindData);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = attributeAffects(aSampleComponents, outputGeom);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = attributeAffects(aSampleWeights, outputGeom);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = attributeAffects(aBindMatrix, outputGeom);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = attributeAffects(aTriangleVerts, outputGeom);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = attributeAffects(aBarycentricWeights, outputGeom);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n\n  aScale = nAttr.create(\"scale\", \"scale\", MFnNumericData::kFloat, 1.0);\n  nAttr.setKeyable(true);\n  status = addAttribute(aScale);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = attributeAffects(aScale, outputGeom);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  aNumTasks = nAttr.create(\"numTasks\", \"numTasks\", MFnNumericData::kInt, 32);\n  nAttr.setMin(1);\n  nAttr.setMax(64);\n  status = addAttribute(aNumTasks);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = attributeAffects(aNumTasks, outputGeom);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  status = MGlobal::executeCommandOnIdle(\"makePaintable -attrType multiFloat -sm deformer cvWrap weights\");\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  return MS::kSuccess;\n}\n\n\n/**\n  Utility method used by both the MPxDeformer and MPxGPUDeformer to pull the bind data out\n  of the datablock.\n  @param[in] data The node datablock.\n  @param[in] geomIndex The geometry logical index.\n  @param[out] taskData Bind info storage.\n*/\nMStatus GetBindInfo(MDataBlock& data, unsigned int geomIndex, TaskData& taskData) {\n  MStatus status;\n  MArrayDataHandle hBindDataArray = data.inputArrayValue(CVWrap::aBindData);\n  status = hBindDataArray.jumpToElement(geomIndex);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MDataHandle hBindData = hBindDataArray.inputValue();\n\n  MArrayDataHandle hSampleWeights = hBindData.child(CVWrap::aSampleWeights);\n  unsigned int numVerts = hSampleWeights.elementCount();\n  if (numVerts == 0) {\n    // No binding information yet.\n    return MS::kNotImplemented;\n  }\n  MArrayDataHandle hComponents = hBindData.child(CVWrap::aSampleComponents);\n  MArrayDataHandle hBindMatrix = hBindData.child(CVWrap::aBindMatrix);\n  MArrayDataHandle hTriangleVerts = hBindData.child(CVWrap::aTriangleVerts);\n  MArrayDataHandle hBarycentricWeights = hBindData.child(CVWrap::aBarycentricWeights);\n\n  hSampleWeights.jumpToArrayElement(0);\n  hComponents.jumpToArrayElement(0);\n  hBindMatrix.jumpToArrayElement(0);\n  hTriangleVerts.jumpToArrayElement(0);\n  hBarycentricWeights.jumpToArrayElement(0);\n\n  MFnNumericData fnNumericData;\n  taskData.bindMatrices.setLength(numVerts);\n  taskData.sampleIds.resize(numVerts);\n  taskData.sampleWeights.resize(numVerts);\n  taskData.triangleVerts.resize(numVerts);\n  taskData.baryCoords.resize(numVerts);\n\n  int sampleLength = (int)taskData.bindMatrices.length();\n  for (unsigned int i = 0; i < numVerts; ++i) {\n    int logicalIndex = hComponents.elementIndex();\n    if (logicalIndex >= sampleLength) {\n      // Nurbs surfaces may be sparse so make sure we have enough space.\n      taskData.bindMatrices.setLength(logicalIndex+1);\n      taskData.sampleIds.resize(logicalIndex+1);\n      taskData.sampleWeights.resize(logicalIndex+1);\n      taskData.triangleVerts.resize(logicalIndex+1);\n      taskData.baryCoords.resize(logicalIndex+1);\n    }\n\n    // Get sample ids\n    MObject oIndexData = hComponents.inputValue().data();\n    MFnIntArrayData fnIntData(oIndexData, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    taskData.sampleIds[logicalIndex] = fnIntData.array();\n\n    // Get sample weights\n    MObject oWeightData = hSampleWeights.inputValue().data();\n    MFnDoubleArrayData fnDoubleData(oWeightData, &status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    taskData.sampleWeights[logicalIndex] = fnDoubleData.array();\n    assert(taskData.sampleWeights[logicalIndex].length() == taskData.sampleIds[logicalIndex].length());\n    \n    // Get bind matrix\n    taskData.bindMatrices[logicalIndex] = hBindMatrix.inputValue().asMatrix();\n\n    // Get triangle vertex binding\n    int3& verts = hTriangleVerts.inputValue(&status).asInt3();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MIntArray& triangleVerts = taskData.triangleVerts[logicalIndex];\n    triangleVerts.setLength(3);\n    triangleVerts[0] = verts[0];\n    triangleVerts[1] = verts[1];\n    triangleVerts[2] = verts[2];\n\n    // Get barycentric weights\n    float3& baryWeights = hBarycentricWeights.inputValue(&status).asFloat3();\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    BaryCoords& coords = taskData.baryCoords[logicalIndex];\n    coords[0] = baryWeights[0];\n    coords[1] = baryWeights[1];\n    coords[2] = baryWeights[2];\n\n    hSampleWeights.next();\n    hComponents.next();\n    hBindMatrix.next();\n    hTriangleVerts.next();\n    hBarycentricWeights.next();\n  }\n  return MS::kSuccess;\n}\n\nMStatus GetDriverData(MDataBlock& data, TaskData& taskData) {\n  MStatus status;\n  // Get driver geo\n  MDataHandle hDriverGeo = data.inputValue(CVWrap::aDriverGeo, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MObject oDriverGeo = hDriverGeo.asMesh();\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MFnMesh fnDriver(oDriverGeo, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  // Get the driver point positions\n  status = fnDriver.getPoints(taskData.driverPoints, MSpace::kWorld);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  unsigned int numDriverPoints = taskData.driverPoints.length();\n  // Get the driver normals\n  taskData.driverNormals.setLength(numDriverPoints);\n  fnDriver.getVertexNormals(false, taskData.driverNormals, MSpace::kWorld);\n  return MS::kSuccess;\n}\n\n\n\nCVWrap::CVWrap() {\n  MThreadPool::init();\n  onDeleteCallbackId = 0;\n}\n\nCVWrap::~CVWrap() {\n  if (onDeleteCallbackId != 0)\n    MMessage::removeCallback(onDeleteCallbackId);\n\t\n  MThreadPool::release();\n  std::vector<ThreadData<TaskData>*>::iterator iter;\n  for (iter = threadData_.begin(); iter != threadData_.end(); ++iter) {\n    delete [] *iter;\n  }\n  threadData_.clear();\n}\n\n\nvoid* CVWrap::creator() { return new CVWrap(); }\n\nvoid CVWrap::postConstructor()\n{\n  MPxDeformerNode::postConstructor();\n\n  MStatus status = MS::kSuccess;\n  MObject obj = thisMObject();\n  onDeleteCallbackId = MNodeMessage::addNodeAboutToDeleteCallback(obj, aboutToDeleteCB, NULL, &status);\n}\n\nvoid CVWrap::aboutToDeleteCB(MObject &node, MDGModifier &modifier, void *clientData)\n{\n  // Find any node connected to .bindMesh and delete it with the deformer, for compatibility with wrap.\n  MPlug bindPlug(node, aBindDriverGeo);\n  MPlugArray bindGeometries;\n  bindPlug.connectedTo(bindGeometries, true, false);\n  for (unsigned int i = 0; i < bindGeometries.length(); i++) {\n    MObject node = bindGeometries[i].node();\n    modifier.deleteNode(node);\n  }\n}\n\n\nMStatus CVWrap::setDependentsDirty(const MPlug& plugBeingDirtied, MPlugArray& affectedPlugs) {\n  // Extract the geom index from the dirty plug and set the dirty flag so we know that we need to\n  // re-read the binding data.\n  if (plugBeingDirtied.isElement()) {\n    MPlug parent = plugBeingDirtied.array().parent();\n    if (parent == aBindData) {\n      unsigned int geomIndex = parent.logicalIndex();\n      dirty_[geomIndex] = true;\n    }\n  }\n  return MS::kSuccess;\n}\n\n\nMStatus CVWrap::deform(MDataBlock& data, MItGeometry& itGeo, const MMatrix& localToWorldMatrix,\n                       unsigned int geomIndex) {\n  MStatus status;\n  if (geomIndex >= taskData_.size()) {\n    taskData_.resize(geomIndex+1);\n  }\n  TaskData& taskData = taskData_[geomIndex];\n  \n  // Get driver geo\n  MDataHandle hDriverGeo = data.inputValue(aDriverGeo, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MObject oDriverGeo = hDriverGeo.asMesh();\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  if (oDriverGeo.isNull()) {\n    // Without a driver mesh, we can't do anything\n    return MS::kSuccess;\n  }\n\n  // Only pull bind information from the data block if it is dirty\n  if (dirty_[geomIndex] || taskData.sampleIds.size() == 0) {\n    dirty_[geomIndex] = false;\n    status = GetBindInfo(data, geomIndex, taskData);\n    if (status == MS::kNotImplemented) {\n      // If no bind information is stored yet, don't do anything.\n      return MS::kSuccess;\n    } else if (MFAIL(status)) {\n      CHECK_MSTATUS_AND_RETURN_IT(status);\n    }\n  }\n\n  // Get driver geo information\n  MFnMesh fnDriver(oDriverGeo, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  // Get the driver point positions\n  status = fnDriver.getPoints(taskData.driverPoints, MSpace::kWorld);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  unsigned int numDriverPoints = taskData.driverPoints.length();\n  // Get the driver normals\n  taskData.driverNormals.setLength(numDriverPoints);\n  fnDriver.getVertexNormals(false, taskData.driverNormals, MSpace::kWorld);\n\n  // Get the deformer membership and paint weights\n  unsigned int membershipCount = itGeo.count();\n  taskData.membership.setLength(membershipCount);\n  taskData.paintWeights.setLength(membershipCount);\n  status = itGeo.allPositions(taskData.points);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  for (int i = 0; !itGeo.isDone(); itGeo.next(), i++) {\n    taskData.membership[i] = itGeo.index();\n    taskData.paintWeights[i] = weightValue(data, geomIndex, itGeo.index());\n  }\n  \n  taskData.drivenMatrix = localToWorldMatrix;\n  taskData.drivenInverseMatrix = localToWorldMatrix.inverse();\n  \n  // See if we even need to calculate anything.\n  taskData.scale = data.inputValue(aScale).asFloat();\n  taskData.envelope = data.inputValue(envelope).asFloat();\n  int taskCount = data.inputValue(aNumTasks).asInt();\n  if (taskData.envelope == 0.0f || taskCount <= 0) {\n    return MS::kSuccess;\n  }\n\n  if (geomIndex >= threadData_.size()) {\n    // Make sure a ThreadData objects exist for this geomIndex.\n    size_t currentSize = threadData_.size();\n    threadData_.resize(geomIndex+1);\n    for (size_t i = currentSize; i < geomIndex+1; ++i) {\n      threadData_[i] = new ThreadData<TaskData>[taskCount];\n    }\n  } else {\n    // Make sure the number of ThreadData instances is correct for this geomIndex\n    if (threadData_[geomIndex][0].numTasks != taskCount) {\n      delete [] threadData_[geomIndex];\n      threadData_[geomIndex] = new ThreadData<TaskData>[taskCount];\n    }\n  }\n\n  CreateThreadData<TaskData>(taskCount, taskData_[geomIndex].points.length(),\n                             &taskData_[geomIndex], threadData_[geomIndex]);\n  MThreadPool::newParallelRegion(CreateTasks, (void *)threadData_[geomIndex]);\n\n  status = itGeo.setAllPositions(taskData.points);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  return MS::kSuccess;\n}\n\n\nvoid CVWrap::CreateTasks(void *data, MThreadRootTask *pRoot) {\n  ThreadData<TaskData>* threadData = static_cast<ThreadData<TaskData>*>(data);\n\n  if (threadData) {\n    int numTasks = threadData[0].numTasks;\n    for(int i = 0; i < numTasks; i++) {\n      MThreadPool::createTask(EvaluateWrap, (void *)&threadData[i], pRoot);\n    }\n    MThreadPool::executeAndJoin(pRoot);\n  }\n}\n\n\nMThreadRetVal CVWrap::EvaluateWrap(void *pParam) {\n  ThreadData<TaskData>* pThreadData = static_cast<ThreadData<TaskData>*>(pParam);\n  double*& alignedStorage = pThreadData->alignedStorage;\n  TaskData* pData = pThreadData->pData;\n  // Get the data out of the struct so it is easier to work with.\n  MMatrix& drivenMatrix = pData->drivenMatrix;\n  MMatrix& drivenInverseMatrix = pData->drivenInverseMatrix;\n  float env = pThreadData->pData->envelope;\n  float scale = pThreadData->pData->scale;\n  MIntArray& membership = pData->membership;\n  MFloatArray& paintWeights = pData->paintWeights;\n  MPointArray& points = pData->points;\n  MPointArray& driverPoints = pData->driverPoints;\n  MFloatVectorArray& driverNormals = pData->driverNormals;\n  MMatrixArray& bindMatrices = pData->bindMatrices;\n  std::vector <MIntArray>& sampleIds = pData->sampleIds;\n  std::vector <MDoubleArray>& sampleWeights = pData->sampleWeights;\n  std::vector <MIntArray>& triangleVerts = pData->triangleVerts;\n  std::vector <BaryCoords>& baryCoords = pData->baryCoords;\n\n  unsigned int taskStart = pThreadData->start;\n  unsigned int taskEnd = pThreadData->end;\n\n  MPoint newPt;\n  MMatrix scaleMatrix, matrix;\n  scaleMatrix[0][0] = scale;\n  scaleMatrix[1][1] = scale;\n  scaleMatrix[2][2] = scale;\n  for (unsigned int i = taskStart; i < taskEnd; ++i) {\n    if (i >= points.length()) {\n      break;\n    }\n    int index = membership[i];\n\n    MPoint origin;\n    MVector normal, up;\n    CalculateBasisComponents(sampleWeights[index], baryCoords[index], triangleVerts[index],\n                             driverPoints, driverNormals, sampleIds[index], alignedStorage,\n                             origin, up, normal);\n\n    CreateMatrix(origin, normal, up, matrix);\n    matrix = scaleMatrix * matrix;\n    MPoint newPt = ((points[i]  * drivenMatrix) * (bindMatrices[index] * matrix)) * drivenInverseMatrix;\n    points[i] = points[i] + ((newPt - points[i]) * paintWeights[i] * env);\n  }\n  return 0;\n}\n\n\n#if MAYA_API_VERSION >= 201600\nMString CVWrapGPU::pluginLoadPath;\n\n#if MAYA_API_VERSION >= 201650\ncl_command_queue (*getMayaDefaultOpenCLCommandQueue)() = MOpenCLInfo::getMayaDefaultOpenCLCommandQueue;\n#else\ncl_command_queue (*getMayaDefaultOpenCLCommandQueue)() = MOpenCLInfo::getOpenCLCommandQueue;\n#endif\n/**\n  Convenience function to copy array data to the gpu.\n*/\ncl_int EnqueueBuffer(MAutoCLMem& mclMem, size_t bufferSize, void* data) {\n  cl_int err = CL_SUCCESS;\n  if (!mclMem.get())\t{\n    // The buffer doesn't exist yet so create it and copy the data over.\n\t\tmclMem.attach(clCreateBuffer(MOpenCLInfo::getOpenCLContext(),\n                                        CL_MEM_COPY_HOST_PTR | CL_MEM_READ_ONLY,\n                                        bufferSize, data, &err));\n\t}\telse {\n\t\t// The buffer already exists so just copy the data over.\n\t\terr = clEnqueueWriteBuffer(getMayaDefaultOpenCLCommandQueue(),\n                               mclMem.get(), CL_TRUE, 0, bufferSize,\n                               data, 0, NULL, NULL);\n\t}\n  return err;\n}\n\nMGPUDeformerRegistrationInfo* CVWrapGPU::GetGPUDeformerInfo() {\n  static CVWrapGPUDeformerInfo wrapInfo;\n  return &wrapInfo;\n}\n\nCVWrapGPU::CVWrapGPU() {\n  // Remember the ctor must be fast.  No heavy work should be done here.\n  // Maya may allocate one of these and then never use it.\n}\n\nCVWrapGPU::~CVWrapGPU() {\n  terminate();\n}\n\n#if MAYA_API_VERSION <= 201700\nMPxGPUDeformer::DeformerStatus CVWrapGPU::evaluate(MDataBlock& block,\n                                                   const MEvaluationNode& evaluationNode,\n                                                   const MPlug& plug,\n                                                   unsigned int numElements,\n                                                   const MAutoCLMem inputBuffer,\n                                                   const MAutoCLEvent inputEvent,\n                                                   MAutoCLMem outputBuffer,\n                                                   MAutoCLEvent& outputEvent) {\n#else\nMPxGPUDeformer::DeformerStatus  CVWrapGPU::evaluate(MDataBlock& block,\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst MEvaluationNode& evaluationNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst MPlug& plug,\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst MGPUDeformerData& inputData,\n\t\t\t\t\t\t\t\t\t\t\t\t\tMGPUDeformerData& outputData) {\n\t// get the input GPU data and event\n\tMGPUDeformerBuffer inputDeformerBuffer = inputData.getBuffer(sPositionsName());\n\tconst MAutoCLMem inputBuffer = inputDeformerBuffer.buffer();\n\tunsigned int numElements = inputDeformerBuffer.elementCount();\n\tconst MAutoCLEvent inputEvent = inputDeformerBuffer.bufferReadyEvent();\n\n\t// create the output buffer\n\tMGPUDeformerBuffer outputDeformerBuffer = createOutputBuffer(inputDeformerBuffer);\n\tMAutoCLEvent outputEvent;\n\tMAutoCLMem outputBuffer = outputDeformerBuffer.buffer();\n#endif\n\n  MStatus status;\n  numElements_ = numElements;\n  // Copy all necessary data to the gpu.\n  status = EnqueueBindData(block, evaluationNode, plug);\n  CHECK_MSTATUS(status);\n  status = EnqueueDriverData(block, evaluationNode, plug);\n  CHECK_MSTATUS(status);\n  status = EnqueuePaintMapData(block, evaluationNode, numElements, plug);\n  CHECK_MSTATUS(status);\n\n  if (!kernel_.get())  {\n    // Load the OpenCL kernel if we haven't yet.\n    MString openCLKernelFile(pluginLoadPath);\n#if MAYA_API_VERSION > 201700\n\topenCLKernelFile += \"/cvwrap.cl\";\n#else\n    openCLKernelFile += \"/cvwrap_pre2018.cl\";\n#endif\n    kernel_ = MOpenCLInfo::getOpenCLKernel(openCLKernelFile, \"cvwrap\");\n    if (kernel_.isNull())  {\n      std::cerr << \"Could not compile kernel \" << openCLKernelFile.asChar() << \"\\n\";\n      return MPxGPUDeformer::kDeformerFailure;\n    }\n  }\n  float envelope = block.inputValue(MPxDeformerNode::envelope, &status).asFloat();\n  CHECK_MSTATUS(status);\n  cl_int err = CL_SUCCESS;\n  \n  // Set all of our kernel parameters.  Input buffer and output buffer may be changing every frame\n  // so always set them.\n  unsigned int parameterId = 0;\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)outputBuffer.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)inputBuffer.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)driverPoints_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)driverNormals_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)paintWeights_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)sampleCounts_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)sampleOffsets_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)sampleIds_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)sampleWeights_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)triangleVerts_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)baryCoords_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)bindMatrices_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)drivenMatrices_.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n#if MAYA_API_VERSION > 201700\n  // get the world space and inverse world space matrix mem handles\n  MGPUDeformerBuffer inputWorldSpaceMatrixDeformerBuffer = inputData.getBuffer(sGeometryMatrixName());\n  const MAutoCLMem deformerWorldSpaceMatrix = inputWorldSpaceMatrixDeformerBuffer.buffer();\n  MGPUDeformerBuffer inputInvWorldSpaceMatrixDeformerBuffer = inputData.getBuffer(sInverseGeometryMatrixName());\n  const MAutoCLMem deformerInvWorldSpaceMatrix = inputInvWorldSpaceMatrixDeformerBuffer.buffer();\n  // Note: these matrices are in row major order\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)deformerWorldSpaceMatrix.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_mem), (void*)deformerInvWorldSpaceMatrix.getReadOnlyRef());\n  MOpenCLInfo::checkCLErrorStatus(err);\n#endif\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_float), (void*)&envelope);\n  MOpenCLInfo::checkCLErrorStatus(err);\n  err = clSetKernelArg(kernel_.get(), parameterId++, sizeof(cl_uint), (void*)&numElements_);\n  MOpenCLInfo::checkCLErrorStatus(err);\n\n  // Figure out a good work group size for our kernel.\n  size_t workGroupSize;\n  size_t retSize;\n  err = clGetKernelWorkGroupInfo(\n    kernel_.get(),\n    MOpenCLInfo::getOpenCLDeviceId(),\n    CL_KERNEL_WORK_GROUP_SIZE,\n    sizeof(size_t),\n    &workGroupSize,\n    &retSize);\n  MOpenCLInfo::checkCLErrorStatus(err);\n\n  size_t localWorkSize = 256;\n  if (retSize > 0) {\n    localWorkSize = workGroupSize;\n  }\n  // global work size must be a multiple of localWorkSize\n  size_t globalWorkSize = (localWorkSize - numElements_ % localWorkSize) + numElements_;\n\n  // set up our input events.  The input event could be NULL, in that case we need to pass\n  // slightly different parameters into clEnqueueNDRangeKernel\n  unsigned int numInputEvents = 0;\n  if (inputEvent.get()) {\n    numInputEvents = 1;\n  }\n\n  // run the kernel\n  err = clEnqueueNDRangeKernel(\n    getMayaDefaultOpenCLCommandQueue(),\n    kernel_.get(),\n    1,\n    NULL,\n    &globalWorkSize,\n    &localWorkSize,\n    numInputEvents,\n    numInputEvents ? inputEvent.getReadOnlyRef() : 0,\n    outputEvent.getReferenceForAssignment() );\n  MOpenCLInfo::checkCLErrorStatus(err);\n\n#if MAYA_API_VERSION > 201700\n  // set the buffer into the output data\n  outputDeformerBuffer.setBufferReadyEvent(outputEvent);\n  outputData.setBuffer(outputDeformerBuffer);\n#endif\n\n  return MPxGPUDeformer::kDeformerSuccess;\n}\n\nMStatus CVWrapGPU::EnqueueBindData(MDataBlock& data, const MEvaluationNode& evaluationNode,\n                                   const MPlug& plug) {\n  MStatus status;\n\tif ((bindMatrices_.get() && (\n        !evaluationNode.dirtyPlugExists(CVWrap::aBindData, &status) &&\n        !evaluationNode.dirtyPlugExists(CVWrap::aSampleComponents, &status) &&\n        !evaluationNode.dirtyPlugExists(CVWrap::aSampleWeights, &status) &&\n        !evaluationNode.dirtyPlugExists(CVWrap::aTriangleVerts, &status) &&\n        !evaluationNode.dirtyPlugExists(CVWrap::aBarycentricWeights, &status) &&\n        !evaluationNode.dirtyPlugExists(CVWrap::aBindMatrix, &status)\n      )) || !status) {\n    // No bind data has changed, nothing to do.\n    return MS::kSuccess;\n  }\n\n  TaskData taskData;\n  unsigned int geomIndex = plug.logicalIndex();\n  status = GetBindInfo(data, geomIndex, taskData);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  // Flatten out bind matrices to float array\n  size_t arraySize = taskData.bindMatrices.length() * 16;\n\tfloat* bindMatrices = new float[arraySize];\n  for(unsigned int i = 0, idx = 0; i < taskData.bindMatrices.length(); ++i) {\n    for(unsigned int row = 0; row < 4; row++) {\n\t\t  for(unsigned int column = 0; column < 4; column++) {\n\t\t\t  bindMatrices[idx++] = (float)taskData.bindMatrices[i](row, column);\n      }\n\t\t}\n\t}\n  cl_int err = EnqueueBuffer(bindMatrices_, arraySize * sizeof(float), (void*)bindMatrices);\n  delete [] bindMatrices;\n\n  // Store samples per vertex\n  arraySize = taskData.sampleIds.size();\n  int* samplesPerVertex = new int[arraySize];\n  int* sampleOffsets = new int[arraySize];\n  int totalSamples = 0;\n  for(size_t i = 0; i < taskData.sampleIds.size(); ++i) {\n    samplesPerVertex[i] = (int)taskData.sampleIds[i].length();\n    sampleOffsets[i] = totalSamples;\n    totalSamples += samplesPerVertex[i];\n  }\n  err = EnqueueBuffer(sampleCounts_, arraySize * sizeof(int), (void*)samplesPerVertex);\n  err = EnqueueBuffer(sampleOffsets_, arraySize * sizeof(int), (void*)sampleOffsets);\n  delete [] samplesPerVertex;\n  delete [] sampleOffsets;\n\n  // Store sampleIds and sampleWeights\n  int* sampleIds = new int[totalSamples];\n  float* sampleWeights = new float[totalSamples];\n  int iter = 0;\n  for(size_t i = 0; i < taskData.sampleIds.size(); ++i) {\n    for(unsigned int j = 0; j < taskData.sampleIds[i].length(); ++j) {\n      sampleIds[iter] = taskData.sampleIds[i][j];\n      sampleWeights[iter] = (float)taskData.sampleWeights[i][j];\n      iter++;\n    }\n  }\n  err = EnqueueBuffer(sampleIds_, totalSamples * sizeof(int), (void*)sampleIds);\n  err = EnqueueBuffer(sampleWeights_, totalSamples * sizeof(float), (void*)sampleWeights);\n  delete [] sampleIds;\n  delete [] sampleWeights;\n\n  // Store triangle verts and bary coords\n  arraySize = taskData.triangleVerts.size() * 3;\n  int* triangleVerts = new int[arraySize];\n  float* baryCoords = new float[arraySize];\n  iter = 0;\n  for(size_t i = 0; i < taskData.triangleVerts.size(); ++i) {\n    for(unsigned int j = 0; j < 3; ++j) {\n      triangleVerts[iter] = taskData.triangleVerts[i][j];\n      baryCoords[iter] = (float)taskData.baryCoords[i][j];\n      iter++;\n    }\n  }\n  err = EnqueueBuffer(triangleVerts_, arraySize * sizeof(int), (void*)triangleVerts);\n  err = EnqueueBuffer(baryCoords_, arraySize * sizeof(float), (void*)baryCoords);\n  delete [] triangleVerts;\n  delete [] baryCoords;\n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapGPU::EnqueueDriverData(MDataBlock& data, const MEvaluationNode& evaluationNode, const MPlug& plug) {\n  MStatus status;\n  TaskData taskData;\n  status = GetDriverData(data, taskData);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  cl_int err = CL_SUCCESS;\n  // Store world space driver points and normals into float arrays.\n  // Reuse the same array for points and normals so we're not dynamically allocating double\n  // the memory.\n  unsigned int pointCount = taskData.driverPoints.length();\n  float* driverData = new float[pointCount * 3];\n\n  // Store the driver points on the gpu.\n  for (unsigned int i = 0, iter = 0; i < pointCount; ++i) {\n    driverData[iter++] = (float)taskData.driverPoints[i].x;\n    driverData[iter++] = (float)taskData.driverPoints[i].y;\n    driverData[iter++] = (float)taskData.driverPoints[i].z;\n  }\n  err = EnqueueBuffer(driverPoints_, pointCount * 3 * sizeof(float), (void*)driverData);\n\n  // Store the driver normals on the gpu.\n  for (unsigned int i = 0, iter = 0; i < pointCount; ++i) {\n    driverData[iter++] = taskData.driverNormals[i].x;\n    driverData[iter++] = taskData.driverNormals[i].y;\n    driverData[iter++] = taskData.driverNormals[i].z;\n  }\n  err = EnqueueBuffer(driverNormals_, pointCount * 3 * sizeof(float), (void*)driverData);\n\tdelete [] driverData;\n\n  int idx = 0;\n#if MAYA_API_VERSION <= 201700\n  // Store the driven matrices on the gpu.\n  MArrayDataHandle hInputs = data.inputValue(CVWrap::input, &status);\n  unsigned int geomIndex = plug.logicalIndex();\n  status = hInputs.jumpToElement(geomIndex);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MDataHandle hInput = hInputs.inputValue(&status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MDataHandle hGeom = hInput.child(CVWrap::inputGeom);\n  MMatrix localToWorldMatrix = hGeom.geometryTransformMatrix();\n  MMatrix worldToLocalMatrix = localToWorldMatrix.inverse();\n  float drivenMatrices[48]; // 0-15: localToWorld, 16-31: worldToLocal, 32-47: scale\n\n  // Store in column order so we can dot in the cl kernel.\n  for(unsigned int column = 0; column < 4; column++) {\n    for(unsigned int row = 0; row < 4; row++) {\n\t\t\tdrivenMatrices[idx++] = (float)localToWorldMatrix(row, column);\n    }\n\t}\n  for(unsigned int column = 0; column < 4; column++) {\n    for(unsigned int row = 0; row < 4; row++) {\n\t\t\tdrivenMatrices[idx++] = (float)worldToLocalMatrix(row, column);\n    }\n\t}\n#else\n\tfloat drivenMatrices[16]; // 0-15: scale\n#endif\n  // Scale matrix is stored row major\n  float scale = data.inputValue(CVWrap::aScale, &status).asFloat();\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  MMatrix scaleMatrix;\n  scaleMatrix[0][0] = scale;\n  scaleMatrix[1][1] = scale;\n  scaleMatrix[2][2] = scale;\n  for(unsigned int row = 0; row < 4; row++) {\n    for(unsigned int column = 0; column < 4; column++) {\n\t\t\tdrivenMatrices[idx++] = (float)scaleMatrix(row, column);\n    }\n\t}\n#if MAYA_API_VERSION <= 201700\n  err = EnqueueBuffer(drivenMatrices_, 48 * sizeof(float), (void*)drivenMatrices);\n#else\n  err = EnqueueBuffer(drivenMatrices_, 16 * sizeof(float), (void*)drivenMatrices);\n#endif\n  return MS::kSuccess;\n}\n\n\nMStatus CVWrapGPU::EnqueuePaintMapData(MDataBlock& data,\n                                       const MEvaluationNode& evaluationNode,\n                                       unsigned int numElements,\n                                       const MPlug& plug) {\n  MStatus status;\n  if ((paintWeights_.get() &&\n       !evaluationNode.dirtyPlugExists(MPxDeformerNode::weightList, &status)) || !status) {\n    // The paint weights are not dirty so no need to get them.\n\t\treturn MS::kSuccess;\n\t}\n\n  cl_int err = CL_SUCCESS;\n\n  // Store the paint weights on the gpu.\n  // Since we can't call MPxDeformerNode::weightValue, get the paint weights from the data block.\n  float* paintWeights = new float[numElements];\n  MArrayDataHandle weightList = data.outputArrayValue(MPxDeformerNode::weightList, &status);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  unsigned int geomIndex = plug.logicalIndex();\n  status = weightList.jumpToElement(geomIndex);\n  // it is possible that the jumpToElement fails.  In that case all weights are 1.\n  if (!status) {  \n    for(unsigned int i = 0; i < numElements; i++) {\n      paintWeights[i] = 1.0f;\n    }\n  } else {\n    // Initialize all weights to 1.0f\n    for(unsigned int i = 0; i < numElements; i++) {\n      paintWeights[i] = 1.0f;\n    }\n    MDataHandle weightsStructure = weightList.inputValue(&status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    MArrayDataHandle weights = weightsStructure.child(MPxDeformerNode::weights);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    // Gather all the non-zero weights\n    unsigned int numWeights = weights.elementCount(&status);\n    CHECK_MSTATUS_AND_RETURN_IT(status);\n    for (unsigned int i = 0; i < numWeights; i++, weights.next()) {\n      unsigned int weightsElementIndex = weights.elementIndex(&status);\n      MDataHandle value = weights.inputValue(&status);\n      // BUG: The weightsElementIndex may be sparse for nurbs surfaces so this would be incorrect\n      paintWeights[weightsElementIndex] = value.asFloat();\n    }\n  }\n  err = EnqueueBuffer(paintWeights_, numElements * sizeof(float), (void*)paintWeights);\n  delete [] paintWeights;\n  return MS::kSuccess;\n}\n\n\nvoid CVWrapGPU::terminate() {\n  driverPoints_.reset();\n  driverNormals_.reset();\n  paintWeights_.reset();\n  bindMatrices_.reset();\n  sampleCounts_.reset();\n  sampleIds_.reset();\n  sampleWeights_.reset();\n  triangleVerts_.reset();\n  baryCoords_.reset();\n  drivenMatrices_.reset();\n\tMOpenCLInfo::releaseOpenCLKernel(kernel_);\n\tkernel_.reset();\n}\n\n#endif\n\n"
  },
  {
    "path": "src/cvWrapDeformer.h",
    "content": "#ifndef CVWRAPDEFORMER_H\n#define CVWRAPDEFORMER_H\n\n#include <maya/MDGModifier.h>\n#include <maya/MFloatArray.h>\n#include <maya/MIntArray.h>\n#include <maya/MMatrix.h> \n#include <maya/MMatrixArray.h> \n#include <maya/MMessage.h>\n#include <maya/MPoint.h> \n#include <maya/MThreadPool.h>\n#include <maya/MPxDeformerNode.h>\n\n#if MAYA_API_VERSION >= 201600\n#include <maya/MPxGPUDeformer.h>\n#include <maya/MGPUDeformerRegistry.h>\n#include <maya/MOpenCLInfo.h>\n#include <clew/clew_cl.h>\n#endif\n\n#include <map>\n#include <vector>\n#include \"common.h\"\n\nstruct TaskData {\n  MMatrix drivenMatrix;\n  MMatrix drivenInverseMatrix;\n  float envelope;\n  float scale;\n\n  MIntArray membership;\n  MFloatArray paintWeights;\n  MPointArray points;\n\n  MPointArray driverPoints;\n  MFloatVectorArray driverNormals;\n  MMatrixArray bindMatrices;\n  std::vector<MIntArray> sampleIds;\n  std::vector<MDoubleArray> sampleWeights;\n  std::vector<MIntArray> triangleVerts;\n  std::vector<BaryCoords> baryCoords;\n};\n \n\nclass CVWrap : public MPxDeformerNode {\n public:\n  CVWrap();\n  virtual ~CVWrap(); \n  virtual void postConstructor();\n  virtual MStatus deform(MDataBlock& data, MItGeometry& iter, const MMatrix& mat,\n                         unsigned int mIndex);\n  virtual MStatus setDependentsDirty(const MPlug& plugBeingDirtied, MPlugArray& affectedPlugs);\n\n  static void* creator();\n  static MStatus initialize();\n\n\n  /**\n    Distributes the ThreadData objects to the parallel threads.\n    @param[in] data The user defined data.  In this case, the ThreadData array.\n    @param[in] pRoot Maya's root task.\n  */\n  static void CreateTasks(void *data, MThreadRootTask *pRoot);\n  static MThreadRetVal EvaluateWrap(void *pParam);\n    \n  const static char* kName;  /**< The name of the node. */\n  static MObject aBindDriverGeo;\n  static MObject aDriverGeo;\n  static MObject aBindData;\n  static MObject aSampleComponents;\n  static MObject aSampleWeights;\n    /** The vertex indices of the triangle containing the origin of each coordinate system. */\n  static MObject aTriangleVerts;\n  /** The indices of the tangents used to calculate the up vector. */\n  static MObject aBarycentricWeights;\n\n  static MObject aBindMatrix;\n  static MObject aNumTasks;\n  static MObject aScale;\n  static MTypeId id;\n\nprivate:\n  static void aboutToDeleteCB(MObject &node, MDGModifier &modifier, void *clientData);\n\n  std::map<unsigned int, bool> dirty_;\n  std::vector<TaskData> taskData_;  /**< Per geometry evaluation data. */\n  std::vector<ThreadData<TaskData>*> threadData_;\n  MCallbackId onDeleteCallbackId;\n};\n\n\n\n#if MAYA_API_VERSION >= 201600\n// the GPU override implementation of the offsetNode\n// \n\nclass CVWrapGPU : public MPxGPUDeformer {\n public:\n\t// Virtual methods from MPxGPUDeformer\n\tCVWrapGPU();\n\tvirtual ~CVWrapGPU();\n\n#if MAYA_API_VERSION <= 201700\n\tvirtual MPxGPUDeformer::DeformerStatus evaluate(MDataBlock& block, const MEvaluationNode&,\n                                                  const MPlug& plug, unsigned int numElements,\n                                                  const MAutoCLMem, const MAutoCLEvent,\n                                                  MAutoCLMem, MAutoCLEvent&);\n#else\n\tvirtual MPxGPUDeformer::DeformerStatus evaluate(MDataBlock& block, const MEvaluationNode& evaluationNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst MPlug& plug, const MGPUDeformerData& inputData,\n\t\t\t\t\t\t\t\t\t\t\t\t\tMGPUDeformerData& outputData);\n#endif\n\tvirtual void terminate();\n\n\tstatic MGPUDeformerRegistrationInfo* GetGPUDeformerInfo();\n\tstatic bool ValidateNode(MDataBlock& block, const MEvaluationNode&, const MPlug& plug, MStringArray* messages);\n  /**< The path of where the plug-in is loaded from.  Used to find the cl kernel. */\n  static MString pluginLoadPath;\n\nprivate:\n\t// helper methods\n\tMStatus EnqueueBindData(MDataBlock& data, const MEvaluationNode& evaluationNode, const MPlug& plug);\n\tMStatus EnqueueDriverData(MDataBlock& data, const MEvaluationNode& evaluationNode, const MPlug& plug);\n\tMStatus EnqueuePaintMapData(MDataBlock& data, const MEvaluationNode& evaluationNode, unsigned int numElements, const MPlug& plug);\n\n\t// Storage for data on the GPU\n\tMAutoCLMem driverPoints_;\n\tMAutoCLMem driverNormals_;\n\tMAutoCLMem paintWeights_;\n\tMAutoCLMem bindMatrices_;\n\tMAutoCLMem sampleCounts_;\n\tMAutoCLMem sampleOffsets_;\n\tMAutoCLMem sampleIds_;\n\tMAutoCLMem sampleWeights_;\n\tMAutoCLMem triangleVerts_;\n\tMAutoCLMem baryCoords_;\n\tMAutoCLMem drivenMatrices_;\n\n\tunsigned int numElements_;\n\n\t// Kernel\n\tMAutoCLKernel kernel_;\n};\n\n\n/**\n  The \n*/\nclass CVWrapGPUDeformerInfo : public MGPUDeformerRegistrationInfo {\n public:\n\tCVWrapGPUDeformerInfo(){}\n\tvirtual ~CVWrapGPUDeformerInfo(){}\n\n\tvirtual MPxGPUDeformer* createGPUDeformer()\t{\n\t\treturn new CVWrapGPU();\n\t}\n\t\n\n\n#if MAYA_API_VERSION >= 201650\n\tvirtual bool validateNodeInGraph(MDataBlock& block, const MEvaluationNode& evaluationNode,\n                                   const MPlug& plug, MStringArray* messages)\t{\n\t\treturn true;\n\t}\n\n\tvirtual bool validateNodeValues(MDataBlock& block, const MEvaluationNode& evaluationNode,\n                                  const MPlug& plug, MStringArray* messages) {\n\t\treturn true;\n\t}\n#else\n  virtual bool validateNode(MDataBlock& block, const MEvaluationNode& evaluationNode,\n                            const MPlug& plug, MStringArray* messages) {\n\t\treturn true;\n\t}\n#endif\n};\n\n#endif // End Maya 2016\n\n#endif\n"
  },
  {
    "path": "src/cvwrap.cl",
    "content": "/*\n  cvwrap kernel\n*/\n\n__kernel void cvwrap(__global float* finalPos,\n                     __global const float* initialPos,\n                     __global const float* driverPoints,\n                     __global const float* driverNormals,\n                     __global const float* paintWeights,\n                     __global const int* sampleCounts,\n                     __global const int* sampleOffsets,\n                     __global const int* sampleIds,\n                     __global const float* sampleWeights,\n                     __global const int* triangleVerts,\n                     __global const float* baryCoords,\n                     __global const float4* bindMatrices,\n                     __global const float4* scaleMatrix,\n                     __global const float* drivenWorldMatrix,\n                     __global const float* drivenInvMatrix,\n                     const float envelope,\n                     const uint positionCount) {\n  unsigned int positionId = get_global_id(0);\n  if (positionId >= positionCount) {\n    return;          \n  }\n  unsigned int positionOffset = positionId * 3;\n\n  // Start with the recreated point and normal using the barycentric coordinates of the hit point.\n  /*\n    Equivalent CPU code:\n    ====================\n    MVector hitNormal;\n    for (int i = 0; i < 3; ++i) {\n      origin += points[triangleVertices[i]] * coords[i];\n      hitNormal += MVector(normals[triangleVertices[i]]) * coords[i];\n    }\n  */\n  float baryA = baryCoords[positionOffset];\n  float baryB = baryCoords[positionOffset+1];\n  float baryC = baryCoords[positionOffset+2];\n  int triVertA = triangleVerts[positionOffset] * 3;\n  int triVertB = triangleVerts[positionOffset+1] * 3;\n  int triVertC = triangleVerts[positionOffset+2] * 3;\n  float originX = driverPoints[triVertA] * baryA +\n                  driverPoints[triVertB] * baryB +\n                  driverPoints[triVertC] * baryC;\n  float originY = driverPoints[triVertA+1] * baryA +\n                  driverPoints[triVertB+1] * baryB +\n                  driverPoints[triVertC+1] * baryC;\n  float originZ = driverPoints[triVertA+2] * baryA +\n                  driverPoints[triVertB+2] * baryB +\n                  driverPoints[triVertC+2] * baryC;\n  float hitNormalX = driverNormals[triVertA] * baryA +\n                     driverNormals[triVertB] * baryB +\n                     driverNormals[triVertC] * baryC;\n  float hitNormalY = driverNormals[triVertA+1] * baryA +\n                     driverNormals[triVertB+1] * baryB +\n                     driverNormals[triVertC+1] * baryC;\n  float hitNormalZ = driverNormals[triVertA+2] * baryA +\n                     driverNormals[triVertB+2] * baryB +\n                     driverNormals[triVertC+2] * baryC;\n\n  /*\n    Equivalent CPU code:\n    ====================\n    unsigned int hitIndex = weights.length()-1;\n    normal = hitNormal * weights[hitIndex];\n  */\n  int offset = sampleOffsets[positionId];\n  int hitIndex = offset + sampleCounts[positionId] - 1;\n  float hitWeight = sampleWeights[hitIndex];\n  float normalX = hitNormalX * hitWeight;\n  float normalY = hitNormalY * hitWeight;\n  float normalZ = hitNormalZ * hitWeight;\n\n  // Use crawl data to calculate normal\n  /*\n    Equivalent CPU code:\n    ====================\n    for (unsigned int j = 0; j < hitIndex; j++) {\n      normal += MVector(normals[sampleIds[j]]) * weights[j];\n    }\n  */\n  for (int j = offset; j < hitIndex; j++) {\n    float sw = sampleWeights[j];\n    int sampleId = sampleIds[j] * 3;\n    normalX += driverNormals[sampleId]   * sw;\n    normalY += driverNormals[sampleId+1] * sw;\n    normalZ += driverNormals[sampleId+2] * sw;\n  }\n\n  // Calculate the up vector\n  /*\n    Equivalent CPU code:\n    ====================\n    up = ((points[triangleVertices[0]] + points[triangleVertices[1]]) * 0.5) - origin;\n  */\n  float upX = ((driverPoints[triVertA] + driverPoints[triVertB]) * 0.5f) - originX;\n  float upY = ((driverPoints[triVertA+1] + driverPoints[triVertB+1]) * 0.5f) - originY;\n  float upZ = ((driverPoints[triVertA+2] + driverPoints[triVertB+2]) * 0.5f) - originZ;\n\n  // Use float3 so we can use the built-in functions.  We are mostly using single floats\n  // because the preferred vector width of most gpu's these days is 1.\n  /*\n    Equivalent CPU code:\n    ====================\n    MVector unitUp = up.normal();\n    // Adjust up if it's parallel to normal or if it's zero length\n    if (abs((unitUp * normal) - 1.0) < 0.001 || up.length() < 0.0001) {\n      for (unsigned int j = 0; j < weights.length()-1; ++j) {\n        up -= (points[sampleIds[j]] - origin) * weights[j];\n        unitUp = up.normal();\n        if (abs((unitUp * normal) - 1.0) > 0.001 && up.length() > 0.0001) {\n          // If the up and normal vectors are no longer parallel and the up vector has a length,\n          // then we are good to go.\n          break;\n        }\n      }\n      up.normalize();\n    } else {\n      up = unitUp;\n    }\n  */\n  float3 up = (float3)(upX, upY, upZ);\n  float3 normal = (float3)(normalX, normalY, normalZ);\n  normal = normalize(normal);\n  float3 unitUp = normalize(up);\n  float upLength = length(up);\n  if (fabs(dot(unitUp, normal) - 1.0f) < 0.001f || upLength < 0.0001f) {\n    for (int j = offset; j < hitIndex; j++) {\n      float sw = sampleWeights[j];\n      int sampleId = sampleIds[j] * 3;\n      up.x -= (driverPoints[sampleId] - originX) * sw;\n      up.y -= (driverPoints[sampleId+1] - originY) * sw;\n      up.z -= (driverPoints[sampleId+2] - originZ) * sw;\n      unitUp = normalize(up);\n      upLength = length(up);\n      if (fabs(dot(unitUp, normal) - 1.0f) > 0.001f && upLength > 0.0001f) {\n        // If the up and normal vectors are no longer parallel and the up vector has a length,\n        // then we are good to go.\n        break;\n      }\n    }\n    up = normalize(up);\n  } else {\n    up = unitUp;\n  }\n\n  // Create the transform matrix\n  // Store by columns so we can use dot to multiply with the scale matrix\n  float3 x = cross(normal, up);\n  float3 z = cross(x, normal);\n  x = normalize(x);\n  z = normalize(z);\n\n  float4 matrix0 = (float4)(x.x, normal.x, z.x, originX);\n  float4 matrix1 = (float4)(x.y, normal.y, z.y, originY);\n  float4 matrix2 = (float4)(x.z, normal.z, z.z, originZ);\n  float4 matrix3 = (float4)(0.0f, 0.0f, 0.0f, 1.0f);\n\n  // Scale matrix mult\n  /*\n    Equivalent CPU code:\n    ====================\n    matrix = scaleMatrix * matrix;\n  */\n  float4 scaleMatrix0 = (float4)(dot(scaleMatrix[0], matrix0),\n                        dot(scaleMatrix[0], matrix1),\n                        dot(scaleMatrix[0], matrix2),\n                        dot(scaleMatrix[0], matrix3));\n  float4 scaleMatrix1 = (float4)(dot(scaleMatrix[1], matrix0),\n                        dot(scaleMatrix[1], matrix1),\n                        dot(scaleMatrix[1], matrix2),\n                        dot(scaleMatrix[1], matrix3));\n  float4 scaleMatrix2 = (float4)(dot(scaleMatrix[2], matrix0),\n                        dot(scaleMatrix[2], matrix1),\n                        dot(scaleMatrix[2], matrix2),\n                        dot(scaleMatrix[2], matrix3));\n  float4 scaleMatrix3 = (float4)(dot(scaleMatrix[3], matrix0),\n                        dot(scaleMatrix[3], matrix1),\n                        dot(scaleMatrix[3], matrix2),\n                        dot(scaleMatrix[3], matrix3));\n  // Transpose so we can dot with bindMatrices\n  float4 smX = (float4)(scaleMatrix0.x, scaleMatrix1.x, scaleMatrix2.x, scaleMatrix3.x);\n  float4 smY = (float4)(scaleMatrix0.y, scaleMatrix1.y, scaleMatrix2.y, scaleMatrix3.y);\n  float4 smZ = (float4)(scaleMatrix0.z, scaleMatrix1.z, scaleMatrix2.z, scaleMatrix3.z);\n  float4 smW = (float4)(scaleMatrix0.w, scaleMatrix1.w, scaleMatrix2.w, scaleMatrix3.w);\n\n  // Multiply bindMatrix with matrix\n  /*\n    Equivalent CPU code:\n    ====================\n    MPoint newPt = ((points[i]  * drivenMatrix) * (bindMatrices[index] * matrix)) * drivenInverseMatrix;\n  */\n  float4 bm0 = bindMatrices[positionId*4];\n  float4 bm1 = bindMatrices[positionId*4+1];\n  float4 bm2 = bindMatrices[positionId*4+2];\n  float4 bm3 = bindMatrices[positionId*4+3];\n  float4 m0 = (float4)(dot(bm0, smX), dot(bm0, smY), dot(bm0, smZ), dot(bm0, smW));\n  float4 m1 = (float4)(dot(bm1, smX), dot(bm1, smY), dot(bm1, smZ), dot(bm1, smW));\n  float4 m2 = (float4)(dot(bm2, smX), dot(bm2, smY), dot(bm2, smZ), dot(bm2, smW));\n  float4 m3 = (float4)(dot(bm3, smX), dot(bm3, smY), dot(bm3, smZ), dot(bm3, smW));\n\n  float4 initialPosition = (float4)(initialPos[positionOffset],\n                                    initialPos[positionOffset+1],\n                                    initialPos[positionOffset+2],\n                                    1.0f);\n\n  float4 drivenMatrixTransposed[4];\n  float4 drivenInvMatrixTransposed[4];\n  for (uint i=0; i < 4; i++)\n  {\n    drivenMatrixTransposed[i] = (float4)(drivenWorldMatrix[i], drivenWorldMatrix[i+4], drivenWorldMatrix[i+8], drivenWorldMatrix[i+12]);\n    drivenInvMatrixTransposed[i] = (float4)(drivenInvMatrix[i], drivenInvMatrix[i+4], drivenInvMatrix[i+8], drivenInvMatrix[i+12]);\n  }\n\n  float4 worldPt = (float4)(dot(initialPosition, drivenMatrixTransposed[0]),\n                            dot(initialPosition, drivenMatrixTransposed[1]),\n                            dot(initialPosition, drivenMatrixTransposed[2]),\n                            dot(initialPosition, drivenMatrixTransposed[3]));\n  worldPt = (float4)(dot(worldPt, (float4)(m0.x, m1.x, m2.x, m3.x)),\n                     dot(worldPt, (float4)(m0.y, m1.y, m2.y, m3.y)),\n                     dot(worldPt, (float4)(m0.z, m1.z, m2.z, m3.z)),\n                     dot(worldPt, (float4)(m0.w, m1.w, m2.w, m3.w)));\n  float3 newPt = (float3)(dot(worldPt, drivenInvMatrixTransposed[0]),\n                          dot(worldPt, drivenInvMatrixTransposed[1]),\n                          dot(worldPt, drivenInvMatrixTransposed[2]));\n  /*\n    Equivalent CPU code:\n    ====================\n    points[i] = points[i] + ((newPt - points[i]) * paintWeights[i] * env);\n  */\n  float weight = paintWeights[positionId] * envelope;\n  finalPos[positionOffset] = initialPosition.x + ((newPt.x - initialPosition.x) * weight);\n  finalPos[positionOffset+1] = initialPosition.y + ((newPt.y - initialPosition.y) * weight);\n  finalPos[positionOffset+2] = initialPosition.z + ((newPt.z - initialPosition.z) * weight);\n}"
  },
  {
    "path": "src/cvwrap_pre2018.cl",
    "content": "/*\n  cvwrap kernel\n*/\n\n__kernel void cvwrap(__global float* finalPos,\n                     __global const float* initialPos,\n                     __global const float* driverPoints,\n                     __global const float* driverNormals,\n                     __global const float* paintWeights,\n                     __global const int* sampleCounts,\n                     __global const int* sampleOffsets,\n                     __global const int* sampleIds,\n                     __global const float* sampleWeights,\n                     __global const int* triangleVerts,\n                     __global const float* baryCoords,\n                     __global const float4* bindMatrices,\n                     __global const float4* drivenMatrices,\n                     const float envelope,\n                     const uint positionCount) {\n  unsigned int positionId = get_global_id(0);\n  if (positionId >= positionCount) {\n    return;          \n  }\n  unsigned int positionOffset = positionId * 3;\n\n  // Start with the recreated point and normal using the barycentric coordinates of the hit point.\n  /*\n    Equivalent CPU code:\n    ====================\n    MVector hitNormal;\n    for (int i = 0; i < 3; ++i) {\n      origin += points[triangleVertices[i]] * coords[i];\n      hitNormal += MVector(normals[triangleVertices[i]]) * coords[i];\n    }\n  */\n  float baryA = baryCoords[positionOffset];\n  float baryB = baryCoords[positionOffset+1];\n  float baryC = baryCoords[positionOffset+2];\n  int triVertA = triangleVerts[positionOffset] * 3;\n  int triVertB = triangleVerts[positionOffset+1] * 3;\n  int triVertC = triangleVerts[positionOffset+2] * 3;\n  float originX = driverPoints[triVertA] * baryA +\n                  driverPoints[triVertB] * baryB +\n                  driverPoints[triVertC] * baryC;\n  float originY = driverPoints[triVertA+1] * baryA +\n                  driverPoints[triVertB+1] * baryB +\n                  driverPoints[triVertC+1] * baryC;\n  float originZ = driverPoints[triVertA+2] * baryA +\n                  driverPoints[triVertB+2] * baryB +\n                  driverPoints[triVertC+2] * baryC;\n  float hitNormalX = driverNormals[triVertA] * baryA +\n                     driverNormals[triVertB] * baryB +\n                     driverNormals[triVertC] * baryC;\n  float hitNormalY = driverNormals[triVertA+1] * baryA +\n                     driverNormals[triVertB+1] * baryB +\n                     driverNormals[triVertC+1] * baryC;\n  float hitNormalZ = driverNormals[triVertA+2] * baryA +\n                     driverNormals[triVertB+2] * baryB +\n                     driverNormals[triVertC+2] * baryC;\n\n  /*\n    Equivalent CPU code:\n    ====================\n    unsigned int hitIndex = weights.length()-1;\n    normal = hitNormal * weights[hitIndex];\n  */\n  int offset = sampleOffsets[positionId];\n  int hitIndex = offset + sampleCounts[positionId] - 1;\n  float hitWeight = sampleWeights[hitIndex];\n  float normalX = hitNormalX * hitWeight;\n  float normalY = hitNormalY * hitWeight;\n  float normalZ = hitNormalZ * hitWeight;\n\n  // Use crawl data to calculate normal\n  /*\n    Equivalent CPU code:\n    ====================\n    for (unsigned int j = 0; j < hitIndex; j++) {\n      normal += MVector(normals[sampleIds[j]]) * weights[j];\n    }\n  */\n  for (int j = offset; j < hitIndex; j++) {\n    float sw = sampleWeights[j];\n    int sampleId = sampleIds[j] * 3;\n    normalX += driverNormals[sampleId]   * sw;\n    normalY += driverNormals[sampleId+1] * sw;\n    normalZ += driverNormals[sampleId+2] * sw;\n  }\n\n  // Calculate the up vector\n  /*\n    Equivalent CPU code:\n    ====================\n    up = ((points[triangleVertices[0]] + points[triangleVertices[1]]) * 0.5) - origin;\n  */\n  float upX = ((driverPoints[triVertA] + driverPoints[triVertB]) * 0.5f) - originX;\n  float upY = ((driverPoints[triVertA+1] + driverPoints[triVertB+1]) * 0.5f) - originY;\n  float upZ = ((driverPoints[triVertA+2] + driverPoints[triVertB+2]) * 0.5f) - originZ;\n\n  // Use float3 so we can use the built-in functions.  We are mostly using single floats\n  // because the preferred vector width of most gpu's these days is 1.\n  /*\n    Equivalent CPU code:\n    ====================\n    MVector unitUp = up.normal();\n    // Adjust up if it's parallel to normal or if it's zero length\n    if (abs((unitUp * normal) - 1.0) < 0.001 || up.length() < 0.0001) {\n      for (unsigned int j = 0; j < weights.length()-1; ++j) {\n        up -= (points[sampleIds[j]] - origin) * weights[j];\n        unitUp = up.normal();\n        if (abs((unitUp * normal) - 1.0) > 0.001 && up.length() > 0.0001) {\n          // If the up and normal vectors are no longer parallel and the up vector has a length,\n          // then we are good to go.\n          break;\n        }\n      }\n      up.normalize();\n    } else {\n      up = unitUp;\n    }\n  */\n  float3 up = (float3)(upX, upY, upZ);\n  float3 normal = (float3)(normalX, normalY, normalZ);\n  normal = normalize(normal);\n  float3 unitUp = normalize(up);\n  float upLength = length(up);\n  if (fabs(dot(unitUp, normal) - 1.0f) < 0.001f || upLength < 0.0001f) {\n    for (int j = offset; j < hitIndex; j++) {\n      float sw = sampleWeights[j];\n      int sampleId = sampleIds[j] * 3;\n      up.x -= (driverPoints[sampleId] - originX) * sw;\n      up.y -= (driverPoints[sampleId+1] - originY) * sw;\n      up.z -= (driverPoints[sampleId+2] - originZ) * sw;\n      unitUp = normalize(up);\n      upLength = length(up);\n      if (fabs(dot(unitUp, normal) - 1.0f) > 0.001f && upLength > 0.0001f) {\n        // If the up and normal vectors are no longer parallel and the up vector has a length,\n        // then we are good to go.\n        break;\n      }\n    }\n    up = normalize(up);\n  } else {\n    up = unitUp;\n  }\n\n  // Create the transform matrix\n  // Store by columns so we can use dot to multiply with the scale matrix\n  float3 x = cross(normal, up);\n  float3 z = cross(x, normal);\n  x = normalize(x);\n  z = normalize(z);\n\n  float4 matrix0 = (float4)(x.x, normal.x, z.x, originX);\n  float4 matrix1 = (float4)(x.y, normal.y, z.y, originY);\n  float4 matrix2 = (float4)(x.z, normal.z, z.z, originZ);\n  float4 matrix3 = (float4)(0.0f, 0.0f, 0.0f, 1.0f);\n\n  // Scale matrix mult\n  /*\n    Equivalent CPU code:\n    ====================\n    matrix = scaleMatrix * matrix;\n  */\n  __global const float4* scaleMatrix = &(drivenMatrices[8]);\n  float4 scaleMatrix0 = (float4)(dot(scaleMatrix[0], matrix0),\n                        dot(scaleMatrix[0], matrix1),\n                        dot(scaleMatrix[0], matrix2),\n                        dot(scaleMatrix[0], matrix3));\n  float4 scaleMatrix1 = (float4)(dot(scaleMatrix[1], matrix0),\n                        dot(scaleMatrix[1], matrix1),\n                        dot(scaleMatrix[1], matrix2),\n                        dot(scaleMatrix[1], matrix3));\n  float4 scaleMatrix2 = (float4)(dot(scaleMatrix[2], matrix0),\n                        dot(scaleMatrix[2], matrix1),\n                        dot(scaleMatrix[2], matrix2),\n                        dot(scaleMatrix[2], matrix3));\n  float4 scaleMatrix3 = (float4)(dot(scaleMatrix[3], matrix0),\n                        dot(scaleMatrix[3], matrix1),\n                        dot(scaleMatrix[3], matrix2),\n                        dot(scaleMatrix[3], matrix3));\n  // Transpose so we can dot with bindMatrices\n  float4 smX = (float4)(scaleMatrix0.x, scaleMatrix1.x, scaleMatrix2.x, scaleMatrix3.x);\n  float4 smY = (float4)(scaleMatrix0.y, scaleMatrix1.y, scaleMatrix2.y, scaleMatrix3.y);\n  float4 smZ = (float4)(scaleMatrix0.z, scaleMatrix1.z, scaleMatrix2.z, scaleMatrix3.z);\n  float4 smW = (float4)(scaleMatrix0.w, scaleMatrix1.w, scaleMatrix2.w, scaleMatrix3.w);\n\n  // Multiply bindMatrix with matrix\n  /*\n    Equivalent CPU code:\n    ====================\n    MPoint newPt = ((points[i]  * drivenMatrix) * (bindMatrices[index] * matrix)) * drivenInverseMatrix;\n  */\n  float4 bm0 = bindMatrices[positionId*4];\n  float4 bm1 = bindMatrices[positionId*4+1];\n  float4 bm2 = bindMatrices[positionId*4+2];\n  float4 bm3 = bindMatrices[positionId*4+3];\n  float4 m0 = (float4)(dot(bm0, smX), dot(bm0, smY), dot(bm0, smZ), dot(bm0, smW));\n  float4 m1 = (float4)(dot(bm1, smX), dot(bm1, smY), dot(bm1, smZ), dot(bm1, smW));\n  float4 m2 = (float4)(dot(bm2, smX), dot(bm2, smY), dot(bm2, smZ), dot(bm2, smW));\n  float4 m3 = (float4)(dot(bm3, smX), dot(bm3, smY), dot(bm3, smZ), dot(bm3, smW));\n\n  float4 initialPosition = (float4)(initialPos[positionOffset],\n                                    initialPos[positionOffset+1],\n                                    initialPos[positionOffset+2],\n                                    1.0f);\n  __global const float4* drivenInverseMatrix = &(drivenMatrices[4]);\n\t__global const float4* drivenMatrix = drivenMatrices;\n  float4 worldPt = (float4)(dot(initialPosition, drivenMatrix[0]),\n                            dot(initialPosition, drivenMatrix[1]),\n                            dot(initialPosition, drivenMatrix[2]),\n                            dot(initialPosition, drivenMatrix[3]));\n  worldPt = (float4)(dot(worldPt, (float4)(m0.x, m1.x, m2.x, m3.x)),\n                     dot(worldPt, (float4)(m0.y, m1.y, m2.y, m3.y)),\n                     dot(worldPt, (float4)(m0.z, m1.z, m2.z, m3.z)),\n                     dot(worldPt, (float4)(m0.w, m1.w, m2.w, m3.w)));\n  float3 newPt = (float3)(dot(worldPt, drivenInverseMatrix[0]),\n                          dot(worldPt, drivenInverseMatrix[1]),\n                          dot(worldPt, drivenInverseMatrix[2]));\n  /*\n    Equivalent CPU code:\n    ====================\n    points[i] = points[i] + ((newPt - points[i]) * paintWeights[i] * env);\n  */\n  float weight = paintWeights[positionId] * envelope;\n  finalPos[positionOffset] = initialPosition.x + ((newPt.x - initialPosition.x) * weight);\n  finalPos[positionOffset+1] = initialPosition.y + ((newPt.y - initialPosition.y) * weight);\n  finalPos[positionOffset+2] = initialPosition.z + ((newPt.z - initialPosition.z) * weight);\n}"
  },
  {
    "path": "src/pluginMain.cpp",
    "content": "#include \"cvWrapDeformer.h\"\n#include \"cvWrapCmd.h\"\n\n#include <maya/MFnPlugin.h>\n#include <maya/MGlobal.h>\n\nMStatus initializePlugin(MObject obj) { \n  MStatus status;\n  MFnPlugin plugin(obj, \"Chad Vernon\", \"1.0\", \"Any\");\n  status = plugin.registerNode(CVWrap::kName, CVWrap::id, CVWrap::creator, CVWrap::initialize,\n                               MPxNode::kDeformerNode);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = plugin.registerCommand(CVWrapCmd::kName, CVWrapCmd::creator, CVWrapCmd::newSyntax);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n#if MAYA_API_VERSION >= 201600\n\tstatus = MGPUDeformerRegistry::registerGPUDeformerCreator(CVWrap::kName, \"cvWrapOverride\",\n                                                            CVWrapGPU::GetGPUDeformerInfo());\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  // Set the load path so we can find the cl kernel.\n  CVWrapGPU::pluginLoadPath = plugin.loadPath();\n#endif\n\n\n  if (MGlobal::mayaState() == MGlobal::kInteractive) {\n    MGlobal::executePythonCommandOnIdle(\"import cvwrap.menu\");\n\t\tMGlobal::executePythonCommandOnIdle(\"cvwrap.menu.create_menuitems()\");\n  }\n\n  return status;\n}\n\nMStatus uninitializePlugin( MObject obj) {\n  MStatus status;\n  MFnPlugin plugin(obj);\n\n#if MAYA_API_VERSION >= 201600\n  status = MGPUDeformerRegistry::deregisterGPUDeformerCreator(CVWrap::kName, \"cvWrapOverride\");\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n#endif\n  status = plugin.deregisterCommand(CVWrapCmd::kName);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n  status = plugin.deregisterNode(CVWrap::id);\n  CHECK_MSTATUS_AND_RETURN_IT(status);\n\n  if (MGlobal::mayaState() == MGlobal::kInteractive) {\n    MGlobal::executePythonCommandOnIdle(\"import cvwrap.menu\");\n\t\tMGlobal::executePythonCommandOnIdle(\"cvwrap.menu.destroy_menuitems()\");\n  }\n  \n  return status;\n}\n"
  }
]