├── .gitignore ├── CMakeLists.txt ├── CustomSphereSolution ├── CMakeLists.txt ├── include │ └── CustomSphere.h └── src │ ├── CustomSphere.cpp │ └── Plugin.cpp ├── JoyPadMaya ├── .qmake.stash ├── JoyPadNode.pro ├── include │ ├── JoyPadNode.h │ └── api_macros.h ├── src │ ├── JoyPadNode.cpp │ └── Plugin.cpp └── test.mel ├── Lecture1 ├── CustomSphere │ ├── CMakeLists.txt │ ├── Makefile │ ├── include │ │ └── CustomSphere.h │ └── src │ │ ├── CustomSphere.cpp │ │ └── Plugin.cpp ├── Hello │ ├── CMakeLists.txt │ └── HelloMaya.cpp └── README.md ├── Lecture2 ├── CMakeLists.txt ├── Locator │ ├── CMakeLists.txt │ ├── Makefile │ ├── include │ │ ├── CubeLocatorNode.h │ │ └── CubeLocatorNodeDrawOverride.h │ └── src │ │ ├── CubeLocatorNode.cpp │ │ ├── CubeLocatorNodeDrawOverride.cpp │ │ └── Plugin.cpp ├── Noise │ ├── CMakeLists.txt │ ├── createMesh.mel │ ├── include │ │ ├── Noise.h │ │ └── NoiseNode.h │ ├── src │ │ ├── Noise.cpp │ │ ├── NoiseNode.cpp │ │ └── Plugin.cpp │ └── updateMesh.py ├── SineNode │ ├── CMakeLists.txt │ ├── createSineBall.py │ ├── include │ │ └── SineNode.h │ └── src │ │ ├── Plugin.cpp │ │ └── SineNode.cpp └── TriLocatorNode │ ├── CMakeLists.txt │ ├── README.md │ ├── include │ ├── TriLocatorNode.h │ ├── TriLocatorNodeData.h │ └── TriLocatorNodeDrawOverride.h │ └── src │ ├── Plugin.cpp │ ├── TriLocatorNode.cpp │ ├── TriLocatorNodeData.cpp │ └── TriLocatorNodeDrawOverride.cpp ├── Lecture3 ├── CMakeLists.txt ├── DAGIt │ ├── CMakeLists.txt │ └── DAGIt.cpp ├── Frames │ ├── CMakeLists.txt │ ├── Makefile │ ├── TimeLine.cpp │ └── frames.py ├── GetSelection │ ├── CMakeLists.txt │ ├── GetSelection.cpp │ └── get_selection.py ├── MComputation │ ├── CMakeLists.txt │ ├── MComputation.cpp │ └── mcomp.py └── RibExporter │ ├── .qmake.stash │ ├── CMakeLists.txt │ ├── RibExportScript.mel │ ├── include │ └── RibExport.h │ └── src │ ├── Plugin.cpp │ └── RibExport.cpp ├── PythonPlugins ├── CustomSphere.py ├── CustomSphereSolution.py ├── HelloMaya.py ├── IPNodeDemo.py ├── InputNode.py ├── QtUI │ ├── QtUIMaya.mod │ ├── README.md │ ├── installModule.py │ ├── plug-ins │ │ └── QtUIMaya.py │ └── ui │ │ └── form.ui ├── SineNode.py └── tests │ ├── test_CustomSphere.py │ ├── test_CustomSphereSolution.py │ └── test_SineNode.py ├── PythonScripts ├── ImageMapExample.py ├── MayaImage.py ├── UnitTestMaya │ ├── MayaImage.py │ ├── TestMayaImage.py │ └── test.png ├── bounce.py ├── imageMap.png ├── imageMap.tif ├── mayaExample1.py ├── mayaExample2.py ├── mayaExample3.py ├── output.jpeg └── pymelExample1.py ├── QtGuiC++ ├── CMakeLists.txt ├── QtGuiCpp.pro ├── include │ └── QtGui.h └── src │ ├── Plugin.cpp │ └── QtGui.cpp ├── README.md └── Sequence ├── TestNode.pro ├── include └── TestNode.h └── src ├── Plugin.cpp └── TestNode.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | # Compiled Object files 3 | *.slo 4 | *.lo 5 | *.o 6 | *.obj 7 | # qt files 8 | *.pro.user* 9 | Makefile 10 | *.bundle 11 | .qmake.cache 12 | # Compiled Dynamic libraries 13 | *.so 14 | *.dylib 15 | *.dll 16 | 17 | # Compiled Static libraries 18 | *.lai 19 | *.la 20 | *.a 21 | *.lib 22 | 23 | # Visual Studio Crap 24 | *.sln 25 | *.user 26 | *.filters 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | # image files 32 | *.psd 33 | 34 | *.app 35 | # temp files under linux 36 | *.~* 37 | #bzr files 38 | .bzr/ 39 | #zips and tgz 40 | *.tgz 41 | *.zip 42 | *.tar.gz -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | project (MayaAPIBuildAllDemos) 4 | 5 | # Check if the DEVKIT_LOCATION was passed via -D 6 | if(NOT DEFINED DEVKIT_LOCATION) 7 | MESSAGE("DEVKIT_LOCATION is not defined checking environment variable") 8 | # Fallback to environment variable 9 | if(DEFINED ENV{DEVKIT_LOCATION}) 10 | set(DEVKIT_LOCATION $ENV{DEVKIT_LOCATION}) 11 | else() 12 | MESSAGE(FATAL_ERROR "DEVKIT_LOCATION is not defined") 13 | endif() 14 | endif() 15 | set(DEVKIT_LOCATION "${DEVKIT_LOCATION}" CACHE STRING "DEVKIT_LOCATION") 16 | 17 | set(PLUGINS_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/plugins) 18 | 19 | add_subdirectory(Lecture1/Hello) 20 | add_subdirectory(Lecture1/CustomSphere) 21 | add_subdirectory(CustomSphereSolution) 22 | add_subdirectory(Lecture2/) 23 | add_subdirectory(Lecture3/) 24 | 25 | 26 | # Copy the python files from the PythonPlugins folder to the plugins folder 27 | file(GLOB PYTHON_PLUGINS ${CMAKE_CURRENT_SOURCE_DIR}/PythonPlugins/*.py) 28 | file(COPY ${PYTHON_PLUGINS} DESTINATION ${PLUGINS_FOLDER}) 29 | # add to the IDE none of these get built 30 | add_custom_target(PythonPluginsForIDE SOURCES ${PYTHON_PLUGINS}) 31 | -------------------------------------------------------------------------------- /CustomSphereSolution/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(CustomSphereSolution) 3 | # include the project setting file 4 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 5 | 6 | 7 | # set SOURCE_FILES 8 | set(SOURCE_FILES src/CustomSphere.cpp src/Plugin.cpp include/CustomSphere.h ) 9 | include_directories(include) 10 | # set linking libraries 11 | set(LIBRARIES OpenMaya Foundation) 12 | # Build plugin 13 | build_plugin() 14 | 15 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 16 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 17 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 18 | ) 19 | -------------------------------------------------------------------------------- /CustomSphereSolution/include/CustomSphere.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOMSPHERE_H__ 2 | #define CUSTOMSPHERE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | //---------------------------------------------------------------------------------------------------------------------- 10 | // @brief a simple maya command class 11 | //---------------------------------------------------------------------------------------------------------------------- 12 | 13 | class CustomSphere : public MPxCommand 14 | { 15 | public: 16 | //---------------------------------------------------------------------------------------------------------------------- 17 | // virtual dtor 18 | //---------------------------------------------------------------------------------------------------------------------- 19 | virtual ~CustomSphere()=default; 20 | //---------------------------------------------------------------------------------------------------------------------- 21 | /// @brief the doIt command is called everytime the command is exectured in the maya shell 22 | /// @param _args the command arguments passed when command is run 23 | /// @note from the maya docs 24 | /// The doIt method should collect whatever information is 25 | /// required to do the task, and store it in local class data. It 26 | /// should finally call redoIt to make the command happen. The 27 | /// redoIt method should do the actual work, using only the local 28 | /// class data. The undoIt method should undo the actual work 29 | /// again using only the local class data. 30 | //---------------------------------------------------------------------------------------------------------------------- 31 | MStatus doIt( const MArgList& _args ) override; 32 | //---------------------------------------------------------------------------------------------------------------------- 33 | /// @brief does the actual commands work 34 | //---------------------------------------------------------------------------------------------------------------------- 35 | MStatus redoIt() override; 36 | //---------------------------------------------------------------------------------------------------------------------- 37 | /// @brief undo what was done by the class using local data only 38 | //---------------------------------------------------------------------------------------------------------------------- 39 | MStatus undoIt() override; 40 | //---------------------------------------------------------------------------------------------------------------------- 41 | /// @brief tell of the class is undoable (in this case true) 42 | //---------------------------------------------------------------------------------------------------------------------- 43 | bool isUndoable() const override; 44 | //---------------------------------------------------------------------------------------------------------------------- 45 | /// @brief our creator function called when the class is created 46 | /// the returns a new instance of this class 47 | //---------------------------------------------------------------------------------------------------------------------- 48 | static void* creator(); 49 | //---------------------------------------------------------------------------------------------------------------------- 50 | /// @brief a function to return the syntax object for this mel command 51 | /// @return The syntax object set up for this mel command 52 | /// 53 | //---------------------------------------------------------------------------------------------------------------------- 54 | static MSyntax newSyntax(); 55 | virtual bool hasSyntax()const override{return true;} 56 | private: 57 | //---------------------------------------------------------------------------------------------------------------------- 58 | /// @brief the number of spheres created 59 | //---------------------------------------------------------------------------------------------------------------------- 60 | unsigned int m_count; 61 | //---------------------------------------------------------------------------------------------------------------------- 62 | /// @brief min radius 63 | //---------------------------------------------------------------------------------------------------------------------- 64 | double m_minRadius; 65 | //---------------------------------------------------------------------------------------------------------------------- 66 | /// @brief max radius 67 | //---------------------------------------------------------------------------------------------------------------------- 68 | double m_maxRadius; 69 | //---------------------------------------------------------------------------------------------------------------------- 70 | /// @brief x extents 71 | //---------------------------------------------------------------------------------------------------------------------- 72 | double m_xExtent; 73 | //---------------------------------------------------------------------------------------------------------------------- 74 | /// @brief y extents 75 | //---------------------------------------------------------------------------------------------------------------------- 76 | double m_yExtent; 77 | //---------------------------------------------------------------------------------------------------------------------- 78 | /// @brief z extents 79 | //---------------------------------------------------------------------------------------------------------------------- 80 | double m_zExtent; 81 | }; 82 | 83 | //---------------------------------------------------------------------------------------------------------------------- 84 | 85 | #endif 86 | 87 | -------------------------------------------------------------------------------- /CustomSphereSolution/src/CustomSphere.cpp: -------------------------------------------------------------------------------- 1 | #include "CustomSphere.h" 2 | #include 3 | #include 4 | #include 5 | std::mt19937 g_RandomEngine; 6 | 7 | 8 | //---------------------------------------------------------------------------------------------------------------------- 9 | /// @brief simple macro to check status and return if error 10 | //---------------------------------------------------------------------------------------------------------------------- 11 | #define CHECK_STATUS_AND_RETURN_IF_FAIL( stat , message ) \ 12 | if( !status ) \ 13 | { \ 14 | MString errorString = status.errorString() + MString( message); \ 15 | MGlobal::displayError( errorString ); \ 16 | return MStatus::kFailure; \ 17 | } \ 18 | 19 | 20 | 21 | //---------------------------------------------------------------------------------------------------------------------- 22 | MSyntax CustomSphere::newSyntax() 23 | { 24 | 25 | // The syntax object we will be returning 26 | MSyntax syn; 27 | // these could be stored as static data instead to save repetition 28 | syn.addFlag("-h","-help"); 29 | syn.addFlag("-x","-xExtents", MSyntax::kDouble ); 30 | syn.addFlag("-y","-yExtents", MSyntax::kDouble ); 31 | syn.addFlag("-z","-zExtents", MSyntax::kDouble ); 32 | 33 | syn.addFlag("-n","-number", MSyntax::kUnsigned ); 34 | syn.addFlag("-mr","-minRadius", MSyntax::kDouble ); 35 | syn.addFlag("-mm","-maxRadius", MSyntax::kDouble ); 36 | 37 | 38 | // set the currently selected items 39 | return syn; 40 | } 41 | 42 | const char * g_HelpText="Usage : customCreateSphere \n-h this help\n-x/y/z set the extents\n-n number of spheres\n-mr min radius\n-mm max radius\n"; 43 | 44 | //---------------------------------------------------------------------------------------------------------------------- 45 | 46 | MStatus CustomSphere::doIt( const MArgList& _args ) 47 | { 48 | // set some default values 49 | m_count=100; 50 | m_xExtent=20; 51 | m_yExtent=20; 52 | m_zExtent=20; 53 | m_minRadius=0.2; 54 | m_maxRadius=2.5; 55 | 56 | 57 | // lets process the command line arguments 58 | MStatus status; 59 | MString arg; 60 | // Create a parser for our arguments using the syntax for this command, 61 | // and the args passed to the function by the user 62 | MArgDatabase parser(syntax(),_args,&status); 63 | 64 | if(status != MS::kSuccess) 65 | { 66 | CHECK_STATUS_AND_RETURN_IF_FAIL(status,"error parsing arguments"); 67 | } 68 | // if the help flag is set, display some useful info. 69 | if(parser.isFlagSet("-h")) 70 | { 71 | MGlobal::displayInfo(g_HelpText); 72 | return MS::kSuccess; 73 | } 74 | 75 | // 76 | // get the command line arguments that were specified 77 | // 78 | if(parser.isFlagSet("-n")) 79 | { 80 | parser.getFlagArgument( "-n", 0, m_count ); 81 | } 82 | if(parser.isFlagSet("-mr")) 83 | { 84 | parser.getFlagArgument( "-mr", 0, m_minRadius ); 85 | } 86 | if(parser.isFlagSet("-mm")) 87 | { 88 | parser.getFlagArgument( "-mm", 0, m_maxRadius ); 89 | } 90 | if(parser.isFlagSet("-x")) 91 | { 92 | parser.getFlagArgument( "-x", 0, m_xExtent ); 93 | } 94 | if(parser.isFlagSet("-y")) 95 | { 96 | parser.getFlagArgument( "-y", 0, m_yExtent ); 97 | } 98 | if(parser.isFlagSet("-z")) 99 | { 100 | parser.getFlagArgument( "-z", 0, m_zExtent ); 101 | } 102 | 103 | // now call the redoIt method which actually does the work 104 | return redoIt(); 105 | } 106 | 107 | 108 | 109 | //---------------------------------------------------------------------------------------------------------------------- 110 | MStatus CustomSphere::redoIt() 111 | { 112 | static const MString create("sphere -name \"sphere^1s\" -r ^2s"); 113 | static const MString move("move ^1s ^2s ^3s \"sphere^4s\""); 114 | // loop for the number of arguments passed in and create some random spheres 115 | MString cmd, index, radius, x, y, z; 116 | std::uniform_real_distribution<>radiusDist(m_minRadius,m_maxRadius); 117 | std::uniform_real_distribution<>xExtent(-m_xExtent,m_xExtent); 118 | std::uniform_real_distribution<>yExtent(-m_yExtent,m_yExtent); 119 | std::uniform_real_distribution<>zExtent(-m_zExtent,m_zExtent); 120 | 121 | for( unsigned int i = 0; i < m_count; ++i ) 122 | { 123 | // fist I'm going to create a maya command as follows 124 | // sphere -name "sphere[n]" where n is the value of i 125 | // and this is why I hate MString! 126 | radius.set(radiusDist(g_RandomEngine) ); 127 | index.set(i); 128 | cmd.format(create, index, radius); 129 | // now execute the command 130 | MStatus status = MGlobal::executeCommand(cmd); 131 | // and check that is was successful 132 | CHECK_STATUS_AND_RETURN_IF_FAIL(status,"Unable to execute sphere command"); 133 | 134 | // now move to a random position first grab some positions 135 | x.set(xExtent(g_RandomEngine)); 136 | y.set(yExtent(g_RandomEngine)); 137 | z.set(zExtent(g_RandomEngine)); 138 | // build the command string 139 | // move x y z "sphere[n]" 140 | cmd.format(move, x, y, z, index); 141 | // execute 142 | status = MGlobal::executeCommand(cmd); 143 | CHECK_STATUS_AND_RETURN_IF_FAIL(status,"unable to move object"); 144 | 145 | } 146 | MString message, count; 147 | count.set(m_count); 148 | message.format("Created ^1s spheres", count); 149 | MGlobal::displayInfo(message); 150 | return MStatus::kSuccess; 151 | } 152 | 153 | //---------------------------------------------------------------------------------------------------------------------- 154 | 155 | MStatus CustomSphere::undoIt() 156 | { 157 | // here we undo what was done in the re-do method, 158 | // this will be called when maya calls the undo method 159 | MString cmd, index; 160 | 161 | for (unsigned int i = 0; i < m_count; ++i) 162 | { 163 | index.set(i); 164 | // delete the objects as created previously 165 | cmd.format("delete \"sphere^1s\"", index); 166 | MStatus status = MGlobal::executeCommand(cmd); 167 | // check that is was ok 168 | CHECK_STATUS_AND_RETURN_IF_FAIL(status, "unable to delete objects in undo"); 169 | 170 | } 171 | return MStatus::kSuccess; 172 | } 173 | 174 | //---------------------------------------------------------------------------------------------------------------------- 175 | 176 | bool CustomSphere::isUndoable() const 177 | { 178 | return true; 179 | } 180 | 181 | //---------------------------------------------------------------------------------------------------------------------- 182 | 183 | void* CustomSphere::creator() 184 | { 185 | return new CustomSphere(); 186 | } 187 | 188 | //---------------------------------------------------------------------------------------------------------------------- 189 | 190 | -------------------------------------------------------------------------------- /CustomSphereSolution/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "CustomSphere.h" 2 | #include 3 | 4 | //---------------------------------------------------------------------------------------------------------------------- 5 | 6 | MStatus initializePlugin( MObject obj ) 7 | { 8 | MStatus status; 9 | MFnPlugin plugin( obj, "", "NCCA" , "Any" ); 10 | // as we are using a syntax parser we need to register this as well, see 11 | // http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/maya/MSyntax.html for more details 12 | status = plugin.registerCommand( "customCreateSphere", CustomSphere::creator,CustomSphere::newSyntax ); 13 | if ( !status ) 14 | { 15 | status.perror( "Unable to register command \"customCreateSphere\"" ); 16 | return status; 17 | } 18 | 19 | return status; 20 | } 21 | 22 | //---------------------------------------------------------------------------------------------------------------------- 23 | 24 | MStatus uninitializePlugin( MObject obj ) 25 | { 26 | MStatus status; 27 | MFnPlugin plugin( obj ); 28 | 29 | status = plugin.deregisterCommand( "customCreateSphere" ); 30 | if ( !status ) 31 | { 32 | status.perror( "Unable to register command \"customCreateSphere\"" ); 33 | return status; 34 | } 35 | 36 | return status; 37 | } 38 | 39 | //---------------------------------------------------------------------------------------------------------------------- 40 | 41 | -------------------------------------------------------------------------------- /JoyPadMaya/.qmake.stash: -------------------------------------------------------------------------------- 1 | QMAKE_XCODE_DEVELOPER_PATH = /Applications/Xcode.app/Contents/Developer 2 | QMAKE_XCODE_VERSION = 6.3.1 3 | QMAKE_MAC_SDK.macosx10.9.path = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk 4 | QMAKE_MAC_SDK.macx-clang.macosx10.9.QMAKE_CC = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang 5 | QMAKE_MAC_SDK.macx-clang.macosx10.9.QMAKE_CXX = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ 6 | QMAKE_MAC_SDK.macx-clang.macosx10.9.QMAKE_FIX_RPATH = \ 7 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/install_name_tool \ 8 | -id 9 | QMAKE_MAC_SDK.macx-clang.macosx10.9.QMAKE_AR = \ 10 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar \ 11 | cq 12 | QMAKE_MAC_SDK.macx-clang.macosx10.9.QMAKE_RANLIB = \ 13 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib \ 14 | -s 15 | QMAKE_MAC_SDK.macx-clang.macosx10.9.QMAKE_LINK = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ 16 | QMAKE_MAC_SDK.macx-clang.macosx10.9.QMAKE_LINK_SHLIB = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ 17 | QMAKE_MAC_SDK.macosx10.9.platform_name = macosx 18 | -------------------------------------------------------------------------------- /JoyPadMaya/JoyPadNode.pro: -------------------------------------------------------------------------------- 1 | #################################################################################### 2 | # This file is split into Three sections 3 | # The first configures Qt and the source files for all platforms 4 | # The second is the linux build 5 | # The third the mac build 6 | # (if your using windows you will need to add a fourth one!) 7 | # first lets remove Qt core and gui not going to need it 8 | #################################################################################### 9 | QT -= core gui 10 | #################################################################################### 11 | # This is the name of the plugin / final lib file 12 | #################################################################################### 13 | TARGET = JoyPad 14 | #################################################################################### 15 | # for for mac we need a bundle so change the name 16 | #################################################################################### 17 | macx:TARGET=JoyPad.bundle 18 | #################################################################################### 19 | # here we add the source files (and headers if required) 20 | #################################################################################### 21 | SOURCES+=src/JoyPadNode.cpp \ 22 | src/Plugin.cpp 23 | 24 | HEADERS+=include/JoyPadNode.h 25 | 26 | OTHER_FILES+=test.mel \ 27 | examples.mel 28 | INCLUDEPATH+=include 29 | INCLUDEPATH+=/usr/local/include 30 | # these are defines required by Maya to re-define some C++ 31 | # stuff, we will add some more later to tell what platform 32 | # we are on as well 33 | DEFINES+=REQUIRE_IOSTREAM \ 34 | _BOOL 35 | #################################################################################### 36 | # These are the maya libs we need to link to, this will change depending 37 | # upon which maya framework we use, just add them to the end of 38 | # this list as required and they will be added to the build 39 | #################################################################################### 40 | MAYALIBS=-lOpenMaya \ 41 | -lFoundation\ 42 | -lOpenMayaUI \ 43 | -lDataModel \ 44 | -lCommandEngine \ 45 | -lFoundation \ 46 | -lIMFbase \ 47 | -lm -ldl 48 | # this demo uses SDL so add the paths using the sdl2-config tool 49 | QMAKE_CXXFLAGS+=$$system(sdl2-config --cflags) 50 | message(output from sdl2-config --cflags added to CXXFLAGS= $$QMAKE_CXXFLAGS) 51 | 52 | LIBS+= $$system(sdl2-config --libs) 53 | #message(output from sdl2-config --static-libs added to LIB=$$LIBS) 54 | 55 | #################################################################################### 56 | # these are all the libs usually included by mayald in case you need 57 | # them just add them to the list above and make sure you escape 58 | #################################################################################### 59 | #-lAnimSlice \ 60 | #-lDeformSlice \ 61 | #-lModifiers \ 62 | #-lDynSlice \ 63 | #-lKinSlice \ 64 | #-lModelSlice \ 65 | #-lNurbsSlice \ 66 | #-lPolySlice \ 67 | #-lProjectSlice \ 68 | #-lImage \ 69 | #-lShared \ 70 | #-lTranslators \ 71 | #-lDataModel \ 72 | #-lRenderModel \ 73 | #-lNurbsEngine \ 74 | #-lDependEngine \ 75 | #-lCommandEngine \ 76 | #-lFoundation \ 77 | #-lIMFbase \ 78 | #-lm -ldl 79 | #################################################################################### 80 | # now tell linux we need to build a lib 81 | #################################################################################### 82 | linux-g++*:TEMPLATE = lib 83 | #################################################################################### 84 | # this tells qmake where maya is 85 | #################################################################################### 86 | linux-g++*:MAYALOCATION=/opt/autodesk/maya/ 87 | #################################################################################### 88 | # under linux we need to use the version of g++ used to build maya 89 | # in this case g++412 90 | #################################################################################### 91 | #linux-g++-64:QMAKE_CXX = g++412 92 | #################################################################################### 93 | # set the include path for linux 94 | #################################################################################### 95 | linux-g++*:INCLUDEPATH += $$MAYALOCATION/include \ 96 | /usr/X11R6/include 97 | #################################################################################### 98 | # set which libs we need to include 99 | #################################################################################### 100 | linux-g++*:LIBS += -L$$MAYALOCATION/lib \ 101 | $$MAYALIBS 102 | #################################################################################### 103 | # tell maya we're building for linux 104 | #################################################################################### 105 | linux:DEFINES+=linux 106 | 107 | #################################################################################### 108 | # tell maya we're building for Mac 109 | #################################################################################### 110 | macx:DEFINES+=OSMac_ 111 | macx:MAYALOCATION=/Applications/Autodesk/maya2014 112 | macx:CONFIG -= app_bundle 113 | macx:INCLUDEPATH+=$$MAYALOCATION/devkit/include 114 | #################################################################################### 115 | # under mac we need to build a bundle, to do this use 116 | # the -bundle flag but we also need to not use -dynamic lib so 117 | # remove this 118 | #################################################################################### 119 | macx:LIBS +=-bundle 120 | mac:LIBS -=-dynamiclib 121 | #################################################################################### 122 | 123 | #################################################################################### 124 | macx:LIBS += -L$$MAYALOCATION/Maya.app/Contents/MacOS \ 125 | $$MAYALIBS 126 | #################################################################################### 127 | 128 | 129 | -------------------------------------------------------------------------------- /JoyPadMaya/include/JoyPadNode.h: -------------------------------------------------------------------------------- 1 | #ifndef JOYPADNODE_H__ 2 | #define JOYPADNODE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class JoyPadNode : public MPxThreadedDeviceNode 14 | { 15 | 16 | public: 17 | 18 | 19 | 20 | JoyPadNode(); 21 | virtual ~JoyPadNode(); 22 | 23 | virtual void postConstructor(); 24 | virtual MStatus compute( const MPlug& plug, MDataBlock& data ); 25 | virtual void threadHandler(); 26 | virtual void threadShutdownHandler(); 27 | 28 | static void* creator(); 29 | static MStatus initialize(); 30 | private: 31 | /// @brief this is a dummy output given a random vlaue to trigger the compute 32 | /// method and update the other joystick values. 33 | static MObject m_output; 34 | /// @brief left hat values Left to right (again may change to be dynamic later) 35 | static MObject m_leftHatLR; 36 | /// @brief right hat values Up down 37 | static MObject m_leftHatUD; 38 | /// @brief left hat values Left Right 39 | static MObject m_rightHatLR; 40 | /// @brief left hat values Up down 41 | static MObject m_rightHatUD; 42 | static MObject m_rightTrigger; 43 | static MObject m_leftTrigger; 44 | /// @brief global trigger threshold sensistivity value used to trigger analog event 45 | static MObject m_sensitivity; 46 | static MObject m_analogMode; 47 | static MObject m_analogRange; 48 | static MObjectArray m_buttons; 49 | /// @brief these are the position in the data block for the thread 50 | /// to pass data from the SDL reading to the compute method 51 | /// Adding these values will increase DATABLOCKSIZE which is used in the 52 | /// allocation of the data block memory and __MUST__ always be last 53 | enum DataBlockValues{DBLOCKLEFTHATLR, 54 | DBLOCKLEFTHATUD, 55 | DBLOCKRIGHTHATLR, 56 | DBLOCKRIGHTHATUD, 57 | DBLOCKLEFTTRIGGER, 58 | DBLOCKRIGHTTRIGGER, 59 | DBLOCKUPDATEHACK, // leave this last used for updates 60 | DATABLOCKSIZE //this must be last 61 | }; 62 | /// @brief enum used for analog stick modes at present one global version 63 | /// however may change this to a per hat mode for more flexibility 64 | enum ANALOGMODE{ABSOLUTE,ADD,SUB}; 65 | /// @brief BUTTON mode determines if it will latch or not may be deprecated. 66 | enum BUTTONMODE{CONTACT,LATCH}; 67 | 68 | 69 | public : 70 | static MTypeId m_id; 71 | static SDL_Joystick *m_js; 72 | }; 73 | 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /JoyPadMaya/include/api_macros.h: -------------------------------------------------------------------------------- 1 | //- 2 | // ========================================================================== 3 | // Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved. 4 | // 5 | // Use of this software is subject to the terms of the Autodesk 6 | // license agreement provided at the time of installation or download, 7 | // or which otherwise accompanies this software in either electronic 8 | // or hard copy form. 9 | // ========================================================================== 10 | //+ 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | // 14 | // api_macros.h 15 | // 16 | // Description: 17 | // Convenience macros for error checking and attribute creation, 18 | // 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | ////////////////////////////////////////////////////////////////////// 22 | // 23 | // Error checking 24 | // 25 | // MCHECKERROR - check the status and print the given error message 26 | // MCHECKERRORNORET - same as above but does not return 27 | // 28 | ////////////////////////////////////////////////////////////////////// 29 | 30 | #define MCHECKERROR(STAT,MSG) \ 31 | if ( MS::kSuccess != STAT ) { \ 32 | cerr << MSG << endl; \ 33 | return MS::kFailure; \ 34 | } 35 | 36 | #define MCHECKERRORNORET(STAT,MSG) \ 37 | if ( MS::kSuccess != STAT ) { \ 38 | cerr << MSG << endl; \ 39 | } 40 | 41 | ////////////////////////////////////////////////////////////////////// 42 | // 43 | // Attribute creation 44 | // 45 | // MAKE_TYPED_ATTR - creates and adds a typed attribute 46 | // MAKE_NUMERIC_ATTR - creates and adds a numeric attribute 47 | // ADD_ATTRIBUTE - adds the given attribute 48 | // ATTRIBUTE_AFFECTS - calls attributeAffects 49 | // 50 | ////////////////////////////////////////////////////////////////////// 51 | 52 | #define MAKE_TYPED_ATTR( NAME, LONGNAME, SHORTNAME, TYPE, DEFAULT ) \ 53 | \ 54 | MStatus NAME##_stat; \ 55 | MFnTypedAttribute NAME##_fn; \ 56 | NAME = NAME##_fn.create( LONGNAME, SHORTNAME, TYPE, DEFAULT ); \ 57 | NAME##_fn.setHidden( true ); \ 58 | NAME##_stat = addAttribute( NAME ); \ 59 | MCHECKERROR(NAME##_stat, "addAttribute error"); 60 | 61 | #define MAKE_NUMERIC_ATTR( NAME, LONGNAME, SHORTNAME, TYPE, DEFAULT, \ 62 | ARRAY, BUILDER, KEYABLE ) \ 63 | \ 64 | MStatus NAME##_stat; \ 65 | MFnNumericAttribute NAME##_fn; \ 66 | NAME = NAME##_fn.create( LONGNAME, SHORTNAME, TYPE, DEFAULT ); \ 67 | MCHECKERROR(NAME##_stat, "numeric attr create error"); \ 68 | NAME##_fn.setArray( ARRAY ); \ 69 | NAME##_fn.setUsesArrayDataBuilder( BUILDER ); \ 70 | NAME##_fn.setHidden( ARRAY ); \ 71 | NAME##_fn.setKeyable( KEYABLE ); \ 72 | NAME##_stat = addAttribute( NAME ); \ 73 | MCHECKERROR(NAME##_stat, "addAttribute error"); 74 | 75 | #define ADD_ATTRIBUTE( ATTR ) \ 76 | MStatus ATTR##_stat; \ 77 | ATTR##_stat = addAttribute( ATTR ); \ 78 | MCHECKERROR( ATTR##_stat, "addAttribute: ATTR" ) 79 | 80 | #define ATTRIBUTE_AFFECTS( IN, OUT ) \ 81 | MStatus IN##OUT##_stat; \ 82 | IN##OUT##_stat = attributeAffects( IN, OUT ); \ 83 | MCHECKERROR(IN##OUT##_stat,"attributeAffects:" #IN "->" #OUT); 84 | 85 | -------------------------------------------------------------------------------- /JoyPadMaya/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "JoyPadNode.h" 2 | #include 3 | 4 | //---------------------------------------------------------------------------------------------------------------------- 5 | 6 | MStatus initializePlugin( MObject obj ) 7 | { 8 | MStatus status; 9 | MFnPlugin plugin( obj, "", "NCCA" , "Any" ); 10 | 11 | // register our nodes and the commands which will be called 12 | status = plugin.registerNode( "JoyPadNode", JoyPadNode::m_id, &JoyPadNode::creator, &JoyPadNode::initialize, MPxNode::kThreadedDeviceNode ); 13 | if (!status) 14 | { 15 | status.perror("Unable to register JoyPadNode" ); 16 | return status; 17 | } 18 | 19 | 20 | return status; 21 | } 22 | 23 | //---------------------------------------------------------------------------------------------------------------------- 24 | 25 | MStatus uninitializePlugin( MObject obj ) 26 | { 27 | MStatus status; 28 | MFnPlugin plugin( obj ); 29 | 30 | 31 | status = plugin.deregisterNode( JoyPadNode::m_id ); 32 | if (!status) 33 | { 34 | status.perror( "unable to deregister JoyPadNode" ); 35 | return status; 36 | } 37 | 38 | return status; 39 | } 40 | //---------------------------------------------------------------------------------------------------------------------- 41 | 42 | 43 | -------------------------------------------------------------------------------- /JoyPadMaya/test.mel: -------------------------------------------------------------------------------- 1 | file -f -new; 2 | unloadPlugin JoyPad; 3 | loadPlugin "/Users/jmacey/teaching/Code/MayaAPICode/JoyPadMaya/JoyPad.bundle"; 4 | string $node = `createNode JoyPadNode`; 5 | string $cube[] = `polyCube`; 6 | connectAttr ( $node + ".lhlr" ) ( $cube[0] + ".tx" ); 7 | connectAttr ( $node + ".lhud" ) ( $cube[0] + ".tz" ); 8 | connectAttr ( $node + ".rhud" ) ( $cube[0] + ".ty" ); 9 | connectAttr ( $node + ".rtrig" ) ( $cube[0] + ".rx" ); 10 | connectAttr ( $node + ".ltrig" ) ( $cube[0] + ".rz" ); 11 | 12 | setAttr ( $node + ".live" ) 1; 13 | -------------------------------------------------------------------------------- /Lecture1/CustomSphere/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(CustomSphere) 3 | # include the project setting file 4 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 5 | # set SOURCE_FILES 6 | set(SOURCE_FILES src/CustomSphere.cpp src/Plugin.cpp include/CustomSphere.h ) 7 | include_directories(include) 8 | # set linking libraries 9 | set(LIBRARIES OpenMaya Foundation) 10 | # Build plugin 11 | build_plugin() 12 | 13 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 14 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 15 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 16 | ) 17 | -------------------------------------------------------------------------------- /Lecture1/CustomSphere/include/CustomSphere.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOMSPHERE_H_ 2 | #define CUSTOMSPHERE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | //---------------------------------------------------------------------------------------------------------------------- 9 | // @brief a simple maya command class 10 | //---------------------------------------------------------------------------------------------------------------------- 11 | 12 | class CustomSphere : public MPxCommand 13 | { 14 | public: 15 | //---------------------------------------------------------------------------------------------------------------------- 16 | // virtual dtor 17 | //---------------------------------------------------------------------------------------------------------------------- 18 | virtual ~CustomSphere()=default; 19 | //---------------------------------------------------------------------------------------------------------------------- 20 | /// @brief the doIt command is called everytime the command is executed in the maya shell 21 | /// @param _args the command arguments passed when command is run 22 | /// @note from the maya docs 23 | /// The doIt method should collect whatever information is 24 | /// required to do the task, and store it in local class data. It 25 | /// should finally call redoIt to make the command happen. The 26 | /// redoIt method should do the actual work, using only the local 27 | /// class data. The undoIt method should undo the actual work 28 | /// again using only the local class data. 29 | //---------------------------------------------------------------------------------------------------------------------- 30 | MStatus doIt( const MArgList& _args ) override; 31 | //---------------------------------------------------------------------------------------------------------------------- 32 | /// @brief does the actual commands work 33 | //---------------------------------------------------------------------------------------------------------------------- 34 | MStatus redoIt() override; 35 | //---------------------------------------------------------------------------------------------------------------------- 36 | /// @brief undo what was done by the class using local data only 37 | //---------------------------------------------------------------------------------------------------------------------- 38 | MStatus undoIt() override; 39 | //---------------------------------------------------------------------------------------------------------------------- 40 | /// @brief tell of the class is undoable (in this case true) 41 | //---------------------------------------------------------------------------------------------------------------------- 42 | bool isUndoable() const override; 43 | //---------------------------------------------------------------------------------------------------------------------- 44 | /// @brief our creator function called when the class is created 45 | /// the returns a new instance of this class 46 | //---------------------------------------------------------------------------------------------------------------------- 47 | static void* creator(); 48 | 49 | private: 50 | //---------------------------------------------------------------------------------------------------------------------- 51 | /// @brief the number of spheres created 52 | //---------------------------------------------------------------------------------------------------------------------- 53 | int m_count; 54 | 55 | }; 56 | 57 | //---------------------------------------------------------------------------------------------------------------------- 58 | 59 | #endif 60 | 61 | -------------------------------------------------------------------------------- /Lecture1/CustomSphere/src/CustomSphere.cpp: -------------------------------------------------------------------------------- 1 | #include "CustomSphere.h" 2 | #include 3 | #include 4 | 5 | #define CHECK_STATUS_AND_RETURN_IF_FAIL( stat , message ) \ 6 | if( !status ) \ 7 | { \ 8 | MString errorString = status.errorString() + MString( message); \ 9 | MGlobal::displayError( errorString ); \ 10 | return MStatus::kFailure; \ 11 | } 12 | 13 | std::mt19937 g_RandomEngine; 14 | 15 | MStatus CustomSphere::doIt( const MArgList& _args ) 16 | { 17 | MStatus status; 18 | 19 | // Verify argument count 20 | if ( _args.length() != 1 ) 21 | { 22 | MGlobal::displayError( "Command requires one argument" ); 23 | return MStatus::kFailure; 24 | } 25 | 26 | // Check argument type 27 | m_count = _args.asInt( 0, &status ); 28 | if( !status ) 29 | { 30 | MGlobal::displayError( "argument is not an integer" ); 31 | return MStatus::kFailure; 32 | } 33 | 34 | // Check argument range 35 | if( m_count <= 0 ) 36 | { 37 | MGlobal::displayError( "argument must be greater than zero" ); 38 | return MStatus::kFailure; 39 | } 40 | // now call the redoIt method which actually does the work 41 | return redoIt(); 42 | } 43 | 44 | 45 | 46 | //---------------------------------------------------------------------------------------------------------------------- 47 | MStatus CustomSphere::redoIt() 48 | { 49 | const MString create("sphere -name \"sphere^1s\" -r ^2s"); 50 | const MString move("move ^1s ^2s ^3s \"sphere^4s\""); 51 | 52 | MString cmd,index,radius,x,y,z; 53 | std::uniform_real_distributionradiusDist(0.8f,4.5f); 54 | std::uniform_real_distributionpositionDist(-20,20); 55 | // loop for the number of arguments passed in and create some random spheres 56 | for( int i = 0; i < m_count; ++i ) 57 | { 58 | // fist I'm going to create a maya command as follows 59 | // sphere -name "sphere[n]" where n is the value of i 60 | // and this is why I hate MString! 61 | radius.set(radiusDist(g_RandomEngine)); 62 | index.set(i); 63 | cmd.format(create, index, radius); 64 | // now execute the command 65 | MStatus status = MGlobal::executeCommand( cmd ); 66 | // and check that is was successful 67 | CHECK_STATUS_AND_RETURN_IF_FAIL(status,"Unable to execute sphere command"); 68 | 69 | // now move to a random position first grab some positions 70 | x.set(positionDist(g_RandomEngine)); 71 | y.set(positionDist(g_RandomEngine)); 72 | z.set(positionDist(g_RandomEngine)); 73 | // build the command string 74 | // move x y z "sphere[n]" 75 | cmd.format(move, x, y, z, index); 76 | // execute 77 | status=MGlobal::executeCommand(cmd); 78 | CHECK_STATUS_AND_RETURN_IF_FAIL(status,"unable to move object"); 79 | 80 | } 81 | MString message,count; 82 | count.set(m_count); 83 | message.format("Created ^1s spheres", count) ; 84 | MGlobal::displayInfo( message ); 85 | return MStatus::kSuccess; 86 | } 87 | 88 | //---------------------------------------------------------------------------------------------------------------------- 89 | 90 | MStatus CustomSphere::undoIt() 91 | { 92 | // here we undo what was done in the re-do method, 93 | // this will be called when maya calls the undo method 94 | MString cmd,index; 95 | 96 | for( int i = 0; i < m_count; ++i ) 97 | { 98 | index.set(i); 99 | // delete the objects as created previously 100 | cmd.format("delete \"sphere^1s\"", index) ; 101 | MStatus status=MGlobal::executeCommand(cmd); 102 | // check that is was ok 103 | CHECK_STATUS_AND_RETURN_IF_FAIL(status,"unable to delete objects in undo"); 104 | 105 | } 106 | return MStatus::kSuccess; 107 | } 108 | 109 | //---------------------------------------------------------------------------------------------------------------------- 110 | 111 | bool CustomSphere::isUndoable() const 112 | { 113 | return true; 114 | } 115 | 116 | //---------------------------------------------------------------------------------------------------------------------- 117 | 118 | void* CustomSphere::creator() 119 | { 120 | return new CustomSphere(); 121 | } 122 | 123 | //---------------------------------------------------------------------------------------------------------------------- 124 | 125 | -------------------------------------------------------------------------------- /Lecture1/CustomSphere/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "CustomSphere.h" 2 | #include 3 | 4 | //---------------------------------------------------------------------------------------------------------------------- 5 | 6 | MStatus initializePlugin( MObject obj ) 7 | { 8 | MStatus status; 9 | MFnPlugin plugin( obj, "", "NCCA" , "Any" ); 10 | 11 | status = plugin.registerCommand( "customCreateSphere", CustomSphere::creator ); 12 | if ( !status ) 13 | { 14 | status.perror( "Unable to register command \"customCreateSphere\"" ); 15 | return status; 16 | } 17 | 18 | return status; 19 | } 20 | 21 | //---------------------------------------------------------------------------------------------------------------------- 22 | 23 | MStatus uninitializePlugin( MObject obj ) 24 | { 25 | MStatus status; 26 | MFnPlugin plugin( obj ); 27 | 28 | status = plugin.deregisterCommand( "customCreateSphere" ); 29 | if ( !status ) 30 | { 31 | status.perror( "Unable to register command \"customCreateSphere\"" ); 32 | return status; 33 | } 34 | 35 | return status; 36 | } 37 | 38 | //---------------------------------------------------------------------------------------------------------------------- 39 | 40 | -------------------------------------------------------------------------------- /Lecture1/Hello/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(HelloMaya) 3 | # include the project setting file 4 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 5 | 6 | # specify project name 7 | set(PROJECT_NAME HelloMaya) 8 | 9 | # set SOURCE_FILES 10 | set(SOURCE_FILES HelloMaya.cpp) 11 | 12 | # set linking libraries 13 | set(LIBRARIES OpenMaya Foundation) 14 | # Build plugin 15 | build_plugin() 16 | 17 | 18 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 19 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 21 | ) 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Lecture1/Hello/HelloMaya.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | // This is a macro to create a simple command 5 | // the compiler expands it to a bunch of code 6 | 7 | DeclareSimpleCommand( HelloMaya , "NCCA", "Maya 2024"); 8 | 9 | MStatus HelloMaya::doIt( const MArgList& ) 10 | { 11 | std::cout<<"This should come from the shell\n"; 12 | MGlobal::displayInfo("Hello Maya in the maya command shell"); 13 | MGlobal::displayWarning("This should be a warning"); 14 | MGlobal::displayError("This should be an error"); 15 | 16 | return MS::kSuccess; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /Lecture1/README.md: -------------------------------------------------------------------------------- 1 | # Lecture 1 2 | 3 | The slides can be found here 4 | 5 | https://nccastaff.bournemouth.ac.uk/jmacey/Lectures/MayaAPI/Commands/#/ 6 | 7 | 8 | ## Part 1 9 | 10 | [![Part 1](https://img.youtube.com/vi/0Tyd6rTmikw/0.jpg)](https://www.youtube.com/watch?v=0Tyd6rTmikw) 11 | 12 | ## Part 2 13 | 14 | [![Part 2](https://img.youtube.com/vi/d6UCUrH5-eI/0.jpg)](https://www.youtube.com/watch?v=d6UCUrH5-eI) 15 | 16 | ## Part 3 17 | 18 | [![Part 3](https://img.youtube.com/vi/WyeXxn5w5eM/0.jpg)](https://www.youtube.com/watch?v=WyeXxn5w5eM) 19 | 20 | ## Part 4 21 | 22 | [![Part 4](https://img.youtube.com/vi/B_vHOymkuqQ/0.jpg)](https://www.youtube.com/watch?v=B_vHOymkuqQ) 23 | 24 | ## Part 5 25 | 26 | [![Part 5](https://img.youtube.com/vi/XAuvXJBlvyA/0.jpg)](https://www.youtube.com/watch?v=XAuvXJBlvyA) 27 | 28 | -------------------------------------------------------------------------------- /Lecture2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | #------------------------------------------------------------------------------------------- 3 | # I'm going to use vcpk in most cases for our install of 3rd party libs 4 | # this is going to check the environment variable for CMAKE_TOOLCHAIN_FILE and this must point to where 5 | # vcpkg.cmake is in the University this is set in your .bash_profile to 6 | # export CMAKE_TOOLCHAIN_FILE=/public/devel/2020/vcpkg/scripts/buildsystems/vcpkg.cmake 7 | #------------------------------------------------------------------------------------------- 8 | if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{CMAKE_TOOLCHAIN_FILE}) 9 | set(CMAKE_TOOLCHAIN_FILE $ENV{CMAKE_TOOLCHAIN_FILE}) 10 | endif() 11 | 12 | # Name of the project 13 | project(Lecture2BuildCall) 14 | 15 | add_subdirectory(${PROJECT_SOURCE_DIR}/Locator/ ) 16 | add_subdirectory(${PROJECT_SOURCE_DIR}/Noise/ ) 17 | add_subdirectory(${PROJECT_SOURCE_DIR}/SineNode/ ) 18 | add_subdirectory(${PROJECT_SOURCE_DIR}/TriLocatorNode/ ) 19 | 20 | -------------------------------------------------------------------------------- /Lecture2/Locator/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(CubeLocatorNode) 3 | # include the project setting file 4 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 5 | 6 | 7 | # set SOURCE_FILES 8 | set(SOURCE_FILES src/CubeLocatorNode.cpp src/CubeLocatorNodeDrawOverride.cpp src/Plugin.cpp include/CubeLocatorNode.h ) 9 | include_directories(include) 10 | # set linking libraries 11 | set(LIBRARIES OpenMaya OpenMayaRender OpenMayaUI Foundation ) 12 | # This function is from the MayaAPI cmake files and get the platform specific includes for OpenGL 13 | find_opengl() 14 | 15 | # Build plugin 16 | build_plugin() 17 | 18 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 19 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 21 | ) 22 | -------------------------------------------------------------------------------- /Lecture2/Locator/include/CubeLocatorNode.h: -------------------------------------------------------------------------------- 1 | #ifndef CUBENODE_H_ 2 | #define CUBENODE_H_ 3 | 4 | #ifndef WIN32 5 | #define LINUX 6 | #endif 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | 17 | 18 | 19 | //---------------------------------------------------------------------------------------------------------------------- 20 | /// @brief a simple maya locator node using OpenGL to draw a cube with width / height / depth 21 | /// paramaters. Also the volume of the cube is created each time an attribute is changed 22 | //---------------------------------------------------------------------------------------------------------------------- 23 | 24 | class CubeLocatorNode : public MPxLocatorNode 25 | { 26 | 27 | public: 28 | //---------------------------------------------------------------------------------------------------------------------- 29 | /// @brief the creator node is called when the plugin is created 30 | //---------------------------------------------------------------------------------------------------------------------- 31 | static void *creator(); 32 | //---------------------------------------------------------------------------------------------------------------------- 33 | /// @brief initialize called when plug is created 34 | //---------------------------------------------------------------------------------------------------------------------- 35 | static MStatus initialize(); 36 | //---------------------------------------------------------------------------------------------------------------------- 37 | /// @brief compute is called every time the attributes of the class change 38 | /// @param [in] _plug plug representing the attribute that needs to be recomputed 39 | /// @param [in] _block data block containing storage for the node's attributes 40 | //---------------------------------------------------------------------------------------------------------------------- 41 | virtual MStatus compute( const MPlug &_plug , MDataBlock &_block); 42 | 43 | 44 | MFloatPoint getDimensions() const ; 45 | //---------------------------------------------------------------------------------------------------------------------- 46 | /// @brief the id of this plugin must be public so we can set outside of class 47 | //---------------------------------------------------------------------------------------------------------------------- 48 | static MTypeId s_id; 49 | static MString s_drawDbClassification; 50 | static MString s_drawRegistrantId; 51 | //---------------------------------------------------------------------------------------------------------------------- 52 | /// @brief the unique type name of our custom node. Mainly for mel purposes. 53 | /// must be public so maya can access 54 | //---------------------------------------------------------------------------------------------------------------------- 55 | static const MString m_typeName; 56 | //---------------------------------------------------------------------------------------------------------------------- 57 | /// @brief the width of our locator cube this can be changed by the user 58 | //---------------------------------------------------------------------------------------------------------------------- 59 | static MObject m_width; 60 | //---------------------------------------------------------------------------------------------------------------------- 61 | /// @brief the height of our locator cube this can be changed by the user 62 | //---------------------------------------------------------------------------------------------------------------------- 63 | static MObject m_height; 64 | //---------------------------------------------------------------------------------------------------------------------- 65 | /// @brief the depth of our locator cube this can be changed by the user 66 | //---------------------------------------------------------------------------------------------------------------------- 67 | static MObject m_depth; 68 | 69 | private : 70 | 71 | //---------------------------------------------------------------------------------------------------------------------- 72 | /// @brief the volume of our locator cube this is calculated each time the attributes 73 | /// above are changed 74 | //---------------------------------------------------------------------------------------------------------------------- 75 | static MObject m_volume; 76 | 77 | }; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | #endif 86 | //---------------------------------------------------------------------------------------------------------------------- 87 | 88 | 89 | -------------------------------------------------------------------------------- /Lecture2/Locator/include/CubeLocatorNodeDrawOverride.h: -------------------------------------------------------------------------------- 1 | #ifndef CUBENODEDATAOVERRIDE_H_ 2 | #define CUBENODEDATAOVERRIDE_H_ 3 | // Viewport 2.0 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "CubeLocatorNode.h" 12 | #include 13 | class CubeLocatorNodeData : public MUserData 14 | { 15 | public: 16 | CubeLocatorNodeData() {} // don't delete after draw 17 | virtual ~CubeLocatorNodeData() {} 18 | 19 | MColor fColor; 20 | MPointArray m_vertices; 21 | MPointArray m_normals; 22 | }; 23 | 24 | class CubeLocatorDrawOverride : public MHWRender::MPxDrawOverride 25 | { 26 | public: 27 | static MHWRender::MPxDrawOverride* Creator(const MObject& obj) 28 | { 29 | return new CubeLocatorDrawOverride(obj); 30 | } 31 | 32 | virtual ~CubeLocatorDrawOverride(); 33 | 34 | virtual MHWRender::DrawAPI supportedDrawAPIs() const; 35 | 36 | virtual bool isBounded(const MDagPath& objPath,const MDagPath& cameraPath) const; 37 | 38 | virtual MBoundingBox boundingBox( const MDagPath& objPath, const MDagPath& cameraPath) const; 39 | 40 | virtual bool disableInternalBoundingBoxDraw() const; 41 | 42 | virtual MUserData* prepareForDraw( 43 | const MDagPath& objPath, 44 | const MDagPath& cameraPath, 45 | const MHWRender::MFrameContext& frameContext, 46 | MUserData* oldData); 47 | 48 | virtual bool hasUIDrawables() const { return true; } 49 | 50 | virtual void addUIDrawables( 51 | const MDagPath& objPath, 52 | MHWRender::MUIDrawManager& drawManager, 53 | const MHWRender::MFrameContext& frameContext, 54 | const MUserData* data); 55 | 56 | protected: 57 | MBoundingBox mCurrentBoundingBox; 58 | 59 | private: 60 | CubeLocatorDrawOverride(const MObject& obj); 61 | MFloatPoint getDimensions(const MDagPath& objPath) const; 62 | MCallbackId m_ModelEditorChangedCbId; 63 | static void OnModelEditorChanged(void *clientData); 64 | CubeLocatorNode *m_locator; 65 | 66 | 67 | }; 68 | 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /Lecture2/Locator/src/CubeLocatorNodeDrawOverride.cpp: -------------------------------------------------------------------------------- 1 | #include "CubeLocatorNodeDrawOverride.h" 2 | 3 | CubeLocatorDrawOverride::CubeLocatorDrawOverride(const MObject& obj) 4 | : MHWRender::MPxDrawOverride(obj, nullptr) 5 | { 6 | m_ModelEditorChangedCbId = MEventMessage::addEventCallback( 7 | "modelEditorChanged", OnModelEditorChanged, this); 8 | 9 | MStatus status; 10 | MFnDependencyNode node(obj, &status); 11 | m_locator = status ? dynamic_cast(node.userNode()) : nullptr; 12 | 13 | } 14 | 15 | CubeLocatorDrawOverride::~CubeLocatorDrawOverride() 16 | { 17 | m_locator = nullptr; 18 | 19 | if (m_ModelEditorChangedCbId != 0) 20 | { 21 | MMessage::removeCallback(m_ModelEditorChangedCbId); 22 | m_ModelEditorChangedCbId = 0; 23 | } 24 | } 25 | 26 | MHWRender::DrawAPI CubeLocatorDrawOverride::supportedDrawAPIs() const 27 | { 28 | // this plugin supports both GL and DX 29 | return (MHWRender::kOpenGL | MHWRender::kDirectX11); 30 | } 31 | 32 | 33 | bool CubeLocatorDrawOverride::isBounded(const MDagPath& /*objPath*/, 34 | const MDagPath& /*cameraPath*/) const 35 | { 36 | return true; 37 | } 38 | 39 | MBoundingBox CubeLocatorDrawOverride::boundingBox(const MDagPath& objPath,const MDagPath& cameraPath) const 40 | { 41 | //MPoint size=getDimensions(); 42 | MPoint corner1( -1, 0.0, -1 ); 43 | MPoint corner2( 1, 0.0, 1 ); 44 | 45 | 46 | CubeLocatorDrawOverride *nonConstThis = (CubeLocatorDrawOverride *)this; 47 | nonConstThis->mCurrentBoundingBox.clear(); 48 | nonConstThis->mCurrentBoundingBox.expand( corner1 ); 49 | nonConstThis->mCurrentBoundingBox.expand( corner2 ); 50 | 51 | return mCurrentBoundingBox; 52 | } 53 | 54 | bool CubeLocatorDrawOverride::disableInternalBoundingBoxDraw() const 55 | { 56 | return false; 57 | } 58 | 59 | void CubeLocatorDrawOverride::OnModelEditorChanged(void *clientData) 60 | { 61 | // Mark the node as being dirty so that it can update on display appearance 62 | // switch among wireframe and shaded. 63 | CubeLocatorDrawOverride *ovr = static_cast(clientData); 64 | // if (ovr && ovr->m_verteces) 65 | // { 66 | // MHWRender::MRenderer::setGeometryDrawDirty(ovr->fFootPrint->thisMObject()); 67 | // } 68 | } 69 | 70 | MUserData* CubeLocatorDrawOverride::prepareForDraw(const MDagPath& objPath,const MDagPath& cameraPath,const MHWRender::MFrameContext& frameContext,MUserData* oldData) 71 | { 72 | // Retrieve data cache (create if does not exist) 73 | auto data = dynamic_cast(oldData); 74 | if (!data) 75 | { 76 | data = new CubeLocatorNodeData(); 77 | } 78 | MPoint dimension=getDimensions(objPath); 79 | char string[40]; 80 | snprintf(string,40,"Dim [%f %f %f]",dimension.x,dimension.y,dimension.z); 81 | MGlobal::displayInfo(string); 82 | 83 | // compute data and cache it 84 | data->fColor = MHWRender::MGeometryUtilities::wireframeColor(objPath); 85 | 86 | return data; 87 | } 88 | 89 | void CubeLocatorDrawOverride::addUIDrawables( 90 | const MDagPath& objPath, 91 | MHWRender::MUIDrawManager& drawManager, 92 | const MHWRender::MFrameContext& frameContext, 93 | const MUserData* data) 94 | { 95 | CubeLocatorNodeData* pLocatorData = (CubeLocatorNodeData*)data; 96 | 97 | drawManager.beginDrawable(); 98 | 99 | // Draw the foot print solid/wireframe 100 | drawManager.setColor( pLocatorData->fColor ); 101 | drawManager.setDepthPriority(5); 102 | 103 | // Draw a text "Foot" 104 | MPoint pos( 0.0, 0.0, 0.0 ); // Position of the text 105 | MColor textColor( 0.1f, 0.8f, 0.8f, 1.0f ); // Text color 106 | 107 | drawManager.setColor( textColor ); 108 | drawManager.setFontSize( MHWRender::MUIDrawManager::kSmallFontSize ); 109 | drawManager.text( pos, MString("Cube"), MHWRender::MUIDrawManager::kCenter ); 110 | drawManager.endDrawable(); 111 | 112 | } 113 | 114 | 115 | MFloatPoint CubeLocatorDrawOverride::getDimensions(const MDagPath& objPath) const 116 | { 117 | MFloatPoint p(1.0f,1.0f,1.0f); 118 | // Retrieve value of the size attribute from the node 119 | MStatus status; 120 | MObject cubleLocatorNode = objPath.node(&status); 121 | 122 | if (status) 123 | { 124 | float sizeVal; 125 | MPlug width(cubleLocatorNode, CubeLocatorNode::m_width); 126 | if (width.getValue(sizeVal)) 127 | { 128 | p.x=sizeVal; 129 | } 130 | MPlug height(cubleLocatorNode, CubeLocatorNode::m_height); 131 | if (height.getValue(sizeVal)) 132 | { 133 | p.y=sizeVal; 134 | } 135 | MPlug depth(cubleLocatorNode, CubeLocatorNode::m_depth); 136 | if (depth.getValue(sizeVal)) 137 | { 138 | p.z=sizeVal; 139 | } 140 | 141 | } 142 | return p; 143 | 144 | } 145 | -------------------------------------------------------------------------------- /Lecture2/Locator/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "CubeLocatorNode.h" 2 | #include "CubeLocatorNodeDrawOverride.h" 3 | 4 | #include 5 | 6 | //---------------------------------------------------------------------------------------------------------------------- 7 | 8 | MStatus initializePlugin( MObject obj ) 9 | { 10 | MStatus status; 11 | MFnPlugin plugin( obj, "", "NCCA" , "Any" ); 12 | 13 | // register our nodes and the commands which will be called 14 | status = plugin.registerNode( "cubeLocator", CubeLocatorNode::s_id, &CubeLocatorNode::creator, &CubeLocatorNode::initialize, MPxNode::kLocatorNode ); 15 | if (!status) 16 | { 17 | status.perror("Unable to register CubeLocatorNode" ); 18 | return status; 19 | } 20 | 21 | status = MHWRender::MDrawRegistry::registerDrawOverrideCreator( 22 | CubeLocatorNode::s_drawDbClassification, 23 | CubeLocatorNode::s_drawRegistrantId, 24 | CubeLocatorDrawOverride::Creator); 25 | if (!status) 26 | { 27 | status.perror("registerDrawOverrideCreator"); 28 | return status; 29 | } 30 | return status; 31 | } 32 | 33 | //---------------------------------------------------------------------------------------------------------------------- 34 | 35 | MStatus uninitializePlugin( MObject obj ) 36 | { 37 | MStatus status; 38 | MFnPlugin plugin( obj ); 39 | 40 | 41 | status = plugin.deregisterNode( CubeLocatorNode::s_id ); 42 | if (!status) 43 | { 44 | status.perror( "unable to deregister CubeLocatorNode" ); 45 | return status; 46 | } 47 | 48 | status = MHWRender::MDrawRegistry::deregisterDrawOverrideCreator( 49 | CubeLocatorNode::s_drawDbClassification, 50 | CubeLocatorNode::s_drawRegistrantId); 51 | if (!status) 52 | { 53 | status.perror("deregisterNode"); 54 | return status; 55 | } 56 | 57 | return status; 58 | } 59 | //---------------------------------------------------------------------------------------------------------------------- 60 | 61 | 62 | -------------------------------------------------------------------------------- /Lecture2/Noise/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(noiseNode) 3 | set(CMAKE_CXX_STANDARD 17) 4 | # include the project setting file 5 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 6 | 7 | 8 | # set SOURCE_FILES 9 | set(SOURCE_FILES src/NoiseNode.cpp src/Noise.cpp src/Plugin.cpp include/Noise.h include/NoiseNode.h ) 10 | include_directories(include) 11 | # set linking libraries 12 | set(LIBRARIES OpenMaya Foundation) 13 | # Build plugin 14 | build_plugin() 15 | 16 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 17 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 18 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 19 | ) 20 | -------------------------------------------------------------------------------- /Lecture2/Noise/createMesh.mel: -------------------------------------------------------------------------------- 1 | 2 | proc createMesh() 3 | { 4 | int $nV[]=`polyEvaluate -v pPlane1`; 5 | 6 | for($i=0; $i<$nV[0]; $i++) 7 | { 8 | float $a[]=`pointPosition pPlane1.vtx[$i]`; 9 | setAttr noiseNode1.tx $a[0]; 10 | setAttr noiseNode1.tz $a[2]; 11 | $o=`getAttr noiseNode1.output`; 12 | 13 | xform -a -ws -t $a[0] $o $a[2] pPlane1.vtx[$i]; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Lecture2/Noise/include/Noise.h: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------------------------------------------- 2 | /// @brief 3 | /// simple Perlin noise class cobbled together from Computer Graphics OpenGL by F,S Hill 4 | /// and Texturing and Modeling Ebert et-al 5 | /// also thanks to Ian Stephenson for help and debuging tips 6 | /// more work needs to be done to add interpolated noise functions and improve the 7 | /// aliasing of the textures but it is ok for basic use 8 | //---------------------------------------------------------------------------------------------------------------------- 9 | 10 | 11 | #ifndef NOISE_H_ 12 | #define NOISE_H_ 13 | 14 | #include 15 | #include 16 | //---------------------------------------------------------------------------------------------------------------------- 17 | /// @class Noise a simple Perlin noise class 18 | /// @author Jon Macey 19 | /// @version 1.0 20 | /// @date 19/4/12 21 | //---------------------------------------------------------------------------------------------------------------------- 22 | 23 | class Noise 24 | { 25 | public : 26 | //---------------------------------------------------------------------------------------------------------------------- 27 | /// @brief a ctor dynamically allocates two tables 28 | //---------------------------------------------------------------------------------------------------------------------- 29 | Noise(); 30 | //---------------------------------------------------------------------------------------------------------------------- 31 | /// @brief dtor will remove tables allocated by dtor 32 | //---------------------------------------------------------------------------------------------------------------------- 33 | ~Noise(); 34 | //---------------------------------------------------------------------------------------------------------------------- 35 | /// @brief a noise function to return a float based on input point and scale 36 | /// @param [in] _scale the scale to process the noise with 37 | /// @param [in] _p the point to sample noise from 38 | /// @brief returns a noise value 39 | //---------------------------------------------------------------------------------------------------------------------- 40 | float noise( float _scale,const MPoint &_p); 41 | //---------------------------------------------------------------------------------------------------------------------- 42 | /// @brief turbulance function creates higher frequency versions of noise as harmonics 43 | /// @param [in] _scale the scale to process the noise with 44 | /// @param [in] _p the point to sample noise from 45 | /// @brief returns a noise value 46 | //---------------------------------------------------------------------------------------------------------------------- 47 | float turbulance(float _scale, const MPoint &_p ); 48 | float complex(int _steps, float _persistence, float _scale, const MPoint &_p ); 49 | //---------------------------------------------------------------------------------------------------------------------- 50 | /// @brief reset the noise tables will also re-set the seed so must be called after setSeed is 51 | /// called 52 | //---------------------------------------------------------------------------------------------------------------------- 53 | void resetTables(); 54 | //---------------------------------------------------------------------------------------------------------------------- 55 | /// @brief set the seed of the random number generator 56 | //---------------------------------------------------------------------------------------------------------------------- 57 | void setSeed(unsigned int _s){m_seed=_s;} 58 | 59 | 60 | private : 61 | 62 | //---------------------------------------------------------------------------------------------------------------------- 63 | /// @brief noise table used for the noise generation 64 | //---------------------------------------------------------------------------------------------------------------------- 65 | std::array m_noiseTable; 66 | //---------------------------------------------------------------------------------------------------------------------- 67 | /// @brief index into the noise table 68 | //---------------------------------------------------------------------------------------------------------------------- 69 | std::array m_index; 70 | //---------------------------------------------------------------------------------------------------------------------- 71 | /// @brief random number generator seed (default to 1) 72 | //---------------------------------------------------------------------------------------------------------------------- 73 | unsigned int m_seed=1; 74 | 75 | //---------------------------------------------------------------------------------------------------------------------- 76 | /// @brief function to generate latticeNoise (from Ian Stephenson) 77 | /// @param [in] _i index into table 78 | /// @param [in] _j index into table 79 | /// @param [in] _k index into table 80 | //---------------------------------------------------------------------------------------------------------------------- 81 | float latticeNoise(int _i, int _j, int _k); 82 | 83 | 84 | }; 85 | 86 | 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /Lecture2/Noise/include/NoiseNode.h: -------------------------------------------------------------------------------- 1 | #ifndef NOISE_NODE_H_ 2 | #define NOISE_NODE_H_ 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "Noise.h" 15 | //---------------------------------------------------------------------------------------------------------------------- 16 | /// @brief this class implements a perlin noise node with one output, it has 3 different noise 17 | /// access types noise, turbulance and complex descriptions below 18 | //---------------------------------------------------------------------------------------------------------------------- 19 | 20 | class NoiseNode : public MPxNode 21 | { 22 | 23 | public: 24 | //---------------------------------------------------------------------------------------------------------------------- 25 | /// @brief the creator node is called when the plugin is created 26 | //---------------------------------------------------------------------------------------------------------------------- 27 | static void *creator(); 28 | //---------------------------------------------------------------------------------------------------------------------- 29 | /// @brief initialize called when plug is created 30 | //---------------------------------------------------------------------------------------------------------------------- 31 | static MStatus initialize(); 32 | //---------------------------------------------------------------------------------------------------------------------- 33 | /// @brief compute is called every time the attributes of the class change 34 | /// @param [in] _plug plug representing the attribute that needs to be recomputed 35 | /// @param [in] _block data block containing storage for the node'_scale attributes 36 | //---------------------------------------------------------------------------------------------------------------------- 37 | virtual MStatus compute( 38 | const MPlug &_plug , 39 | MDataBlock &_block 40 | ); 41 | 42 | 43 | //---------------------------------------------------------------------------------------------------------------------- 44 | /// @brief the id of this plugin must be public so we can set outside of class 45 | //---------------------------------------------------------------------------------------------------------------------- 46 | static MTypeId m_id; 47 | //---------------------------------------------------------------------------------------------------------------------- 48 | /// @brief the unique type name of our custom node. Mainly for mel purposes. 49 | /// must be public so maya can access 50 | //---------------------------------------------------------------------------------------------------------------------- 51 | static const MString typeName; 52 | //---------------------------------------------------------------------------------------------------------------------- 53 | /// @brief ctor this will dynamically allocate a Noise class 54 | //---------------------------------------------------------------------------------------------------------------------- 55 | NoiseNode(); 56 | //---------------------------------------------------------------------------------------------------------------------- 57 | /// @brief dtor this will destroy the noise class created in the ctor 58 | //---------------------------------------------------------------------------------------------------------------------- 59 | ~NoiseNode(); 60 | 61 | private : 62 | //---------------------------------------------------------------------------------------------------------------------- 63 | /// @brief the amplitude of our node 64 | //---------------------------------------------------------------------------------------------------------------------- 65 | static MObject m_amplitude; 66 | ///---------------------------------------------------------------------------------------------------------------------- 67 | // @brief the xpos of our node input 68 | //---------------------------------------------------------------------------------------------------------------------- 69 | static MObject m_tx; 70 | //---------------------------------------------------------------------------------------------------------------------- 71 | /// @brief the ypos of our node input 72 | //---------------------------------------------------------------------------------------------------------------------- 73 | static MObject m_ty; 74 | //---------------------------------------------------------------------------------------------------------------------- 75 | /// @brief the zpos of our node input 76 | //---------------------------------------------------------------------------------------------------------------------- 77 | static MObject m_tz; 78 | //---------------------------------------------------------------------------------------------------------------------- 79 | /// @brief the output to our node 80 | //---------------------------------------------------------------------------------------------------------------------- 81 | static MObject m_output; 82 | //---------------------------------------------------------------------------------------------------------------------- 83 | /// @brief noise type 84 | //---------------------------------------------------------------------------------------------------------------------- 85 | static MObject m_noiseType; 86 | //---------------------------------------------------------------------------------------------------------------------- 87 | /// @brief scale type 88 | //---------------------------------------------------------------------------------------------------------------------- 89 | static MObject m_scale; 90 | //---------------------------------------------------------------------------------------------------------------------- 91 | /// @brief seed for the rng type 92 | //---------------------------------------------------------------------------------------------------------------------- 93 | static MObject m_seed; 94 | //---------------------------------------------------------------------------------------------------------------------- 95 | /// @brief steps for complex noise 96 | //---------------------------------------------------------------------------------------------------------------------- 97 | static MObject m_steps; 98 | //---------------------------------------------------------------------------------------------------------------------- 99 | /// @brief persitence for complex noise 100 | //---------------------------------------------------------------------------------------------------------------------- 101 | static MObject m_persistence; 102 | 103 | //---------------------------------------------------------------------------------------------------------------------- 104 | /// @brief the noise generator 105 | //---------------------------------------------------------------------------------------------------------------------- 106 | std::unique_ptrm_noise; 107 | //---------------------------------------------------------------------------------------------------------------------- 108 | /// @brief the noise types to use 109 | /// @note can't use enum classes from C++ 11 as need to be short or convertable 110 | //---------------------------------------------------------------------------------------------------------------------- 111 | enum NoiseTypes{NOISE,TURBULANCE,COMPLEX}; 112 | //---------------------------------------------------------------------------------------------------------------------- 113 | /// @brief the previous seed value to compare with (see notes in compute for rationelle) 114 | //---------------------------------------------------------------------------------------------------------------------- 115 | unsigned int m_seedValue; 116 | //---------------------------------------------------------------------------------------------------------------------- 117 | 118 | }; 119 | //---------------------------------------------------------------------------------------------------------------------- 120 | 121 | #endif 122 | 123 | 124 | -------------------------------------------------------------------------------- /Lecture2/Noise/src/Noise.cpp: -------------------------------------------------------------------------------- 1 | #include "Noise.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | //---------------------------------------------------------------------------------------------------------------------- 11 | void Noise :: resetTables() 12 | { 13 | // create an instance of the Mersenne Twister random number generator 14 | std::mt19937 gen(m_seed); 15 | // and create a RandFloat function 16 | std::uniform_real_distribution randPosFloat(0.0f, 1.0f); 17 | // shuffle the index table randomly 18 | std::shuffle(std::begin(m_index), std::end(m_index), gen); 19 | 20 | for(auto &t : m_noiseTable) 21 | { 22 | t=randPosFloat(gen); 23 | } 24 | } 25 | //---------------------------------------------------------------------------------------------------------------------- 26 | Noise :: Noise() 27 | { 28 | std::iota(std::begin(m_index),std::end(m_index),0); 29 | resetTables(); 30 | } 31 | //---------------------------------------------------------------------------------------------------------------------- 32 | Noise::~Noise() 33 | { 34 | } 35 | 36 | 37 | //---------------------------------------------------------------------------------------------------------------------- 38 | 39 | float Noise::latticeNoise(int _i, int _j, int _k) 40 | { 41 | 42 | #define PERM(x) m_index[(x)&255] 43 | #define INDEX(ix,iy,iz) PERM( (ix) + PERM((iy)+PERM(iz))) 44 | // m_noiseTable[m_index[((_i) + m_index[((_j)+m_index[(_k)&255])&255])&255]]; 45 | return m_noiseTable[INDEX(_i,_j,_k)]; 46 | 47 | 48 | } 49 | 50 | template T lerp(T _a, T _b, float _t) 51 | { 52 | T p; 53 | p=_a+(_b-_a)*_t; 54 | return p; 55 | } 56 | 57 | 58 | float Noise::noise(float _scale, const MPoint &_p) 59 | { 60 | 61 | float d[2][2][2]; 62 | MPoint pp; 63 | pp.x=_p.x * _scale ; 64 | pp.y=_p.y * _scale ; 65 | pp.z=_p.z * _scale ; 66 | 67 | long ix = (long) pp.x; 68 | long iy = (long) pp.y; 69 | long iz = (long) pp.z; 70 | float tx,ty,tz,x0,x1,x2,x3,y0,y1; 71 | tx=pp.x-ix; ty=pp.y-iy; tz=pp.z-iz; 72 | 73 | 74 | for(int k=0; k<=1; ++k) 75 | { 76 | for(int j=0; j<=1; ++j) 77 | { 78 | for(int i=0; i<=1; ++i) 79 | { 80 | d[k][j][i]=latticeNoise(ix+i,iy+j,iz+k); 81 | } 82 | } 83 | } 84 | 85 | x0=lerp(d[0][0][0],d[0][0][1],tx); 86 | x1=lerp(d[0][1][0],d[0][1][1],tx); 87 | x2=lerp(d[1][0][0],d[1][0][1],tx); 88 | x3=lerp(d[1][1][0],d[1][1][1],tx); 89 | y0=lerp(x0,x1,ty); 90 | y1=lerp(x2,x3,ty); 91 | return lerp(y0,y1,tz); 92 | } 93 | 94 | 95 | 96 | //---------------------------------------------------------------------------------------------------------------------- 97 | float Noise :: turbulance(float _scale, const MPoint &_p ) 98 | { 99 | float val= (noise(_scale,_p)/2.0) + 100 | (noise(2.0*_scale,_p)/4.0) + 101 | (noise(4.0*_scale,_p)/8.0) + 102 | (noise(8.0*_scale,_p)/16.0); 103 | return val; 104 | } 105 | 106 | //---------------------------------------------------------------------------------------------------------------------- 107 | // values for this are based on http://freespace.virgin.net/hugo.elias/models/m_perlin.htm 108 | //---------------------------------------------------------------------------------------------------------------------- 109 | 110 | float Noise::complex( int _steps,float _persistence,float _scale, const MPoint &_p ) 111 | { 112 | float val=0.0; 113 | 114 | for(int i=1; i<=_steps; ++i) 115 | { 116 | val+=(noise(i*_scale,_p)/std::pow(_persistence,i)); 117 | } 118 | return val; 119 | 120 | } 121 | //---------------------------------------------------------------------------------------------------------------------- 122 | -------------------------------------------------------------------------------- /Lecture2/Noise/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "NoiseNode.h" 2 | #include 3 | 4 | //---------------------------------------------------------------------------------------------------------------------- 5 | 6 | MStatus initializePlugin( MObject obj ) 7 | { 8 | MStatus status; 9 | MFnPlugin plugin( obj, "", "NCCA" , "Any" ); 10 | 11 | // register our nodes and the commands which will be called 12 | status = plugin.registerNode( "noiseNode", NoiseNode::m_id, &NoiseNode::creator, &NoiseNode::initialize, MPxNode::kDependNode ); 13 | if (!status) 14 | { 15 | status.perror("Unable to register NoiseNode" ); 16 | return status; 17 | } 18 | 19 | 20 | return status; 21 | } 22 | 23 | //---------------------------------------------------------------------------------------------------------------------- 24 | 25 | MStatus uninitializePlugin( MObject obj ) 26 | { 27 | MStatus status; 28 | MFnPlugin plugin( obj ); 29 | 30 | 31 | status = plugin.deregisterNode( NoiseNode::m_id ); 32 | if (!status) 33 | { 34 | status.perror( "unable to deregister NoiseNode" ); 35 | return status; 36 | } 37 | 38 | return status; 39 | } 40 | //---------------------------------------------------------------------------------------------------------------------- 41 | 42 | 43 | -------------------------------------------------------------------------------- /Lecture2/Noise/updateMesh.py: -------------------------------------------------------------------------------- 1 | import maya.cmds as cmds 2 | 3 | cmds.createNode('noiseNode') 4 | cmds.polyPlane(w=42,h=42,sx=80,sy=80) 5 | 6 | 7 | def updateMesh() : 8 | nVerts=cmds.polyEvaluate('pPlane1',v=True) 9 | for i in range(0,nVerts) : 10 | a=cmds.pointPosition('pPlane1.vtx[%d]'%i ) 11 | cmds.setAttr('noiseNode1.tx',a[0]) 12 | cmds.setAttr('noiseNode1.tz',a[2]) 13 | o=cmds.getAttr('noiseNode1.output') 14 | cmds.xform( 'pPlane1.vtx[%d]' %i ,a=True,ws=True,t=( a[0], o, a[2]) ) 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Lecture2/SineNode/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(sineNode) 3 | set(CMAKE_CXX_STANDARD 17) 4 | # include the project setting file 5 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 6 | 7 | 8 | # set SOURCE_FILES 9 | set(SOURCE_FILES src/SineNode.cpp src/Plugin.cpp include/SineNode.h ) 10 | include_directories(include) 11 | # set linking libraries 12 | set(LIBRARIES OpenMaya Foundation) 13 | # Build plugin 14 | build_plugin() 15 | 16 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 17 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 18 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 19 | ) 20 | -------------------------------------------------------------------------------- /Lecture2/SineNode/createSineBall.py: -------------------------------------------------------------------------------- 1 | import maya.cmds as cmds 2 | 3 | cmds.createNode('sineNode') 4 | cmds.connectAttr('time1.outTime','sineNode1.time') 5 | cmds.polySphere() 6 | cmds.connectAttr('sineNode1.o','pSphere1.tx') 7 | cmds.connectAttr('sineNode1.o','pSphere1.ty') 8 | cmds.select('sineNode1') -------------------------------------------------------------------------------- /Lecture2/SineNode/include/SineNode.h: -------------------------------------------------------------------------------- 1 | #ifndef SINE_NODE_H_ 2 | #define SINE_NODE_H_ 3 | #ifndef WIN32 4 | #define LINUX 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | //---------------------------------------------------------------------------------------------------------------------- 13 | /// @brief a simple node that takes an input value (time) and creates a sin output value based on two parameters 14 | //---------------------------------------------------------------------------------------------------------------------- 15 | 16 | class SineNode : public MPxNode 17 | { 18 | 19 | public: 20 | //---------------------------------------------------------------------------------------------------------------------- 21 | /// @brief the creator node is called when the plugin is created 22 | //---------------------------------------------------------------------------------------------------------------------- 23 | static void *creator(); 24 | //---------------------------------------------------------------------------------------------------------------------- 25 | /// @brief initialize called when plug is created 26 | //---------------------------------------------------------------------------------------------------------------------- 27 | static MStatus initialize(); 28 | ///---------------------------------------------------------------------------------------------------------------------- 29 | // @brief compute is called every time the attributes of the class change 30 | /// @param [in] _plug plug representing the attribute that needs to be recomputed 31 | /// @param [in] _block data block containing storage for the node's attributes 32 | //---------------------------------------------------------------------------------------------------------------------- 33 | virtual MStatus compute(const MPlug &_plug , MDataBlock &_block ); 34 | 35 | //---------------------------------------------------------------------------------------------------------------------- 36 | /// @brief the id of this plugin must be public so we can set outside of class 37 | //---------------------------------------------------------------------------------------------------------------------- 38 | static MTypeId m_id; 39 | //---------------------------------------------------------------------------------------------------------------------- 40 | /// @brief the unique type name of our custom node. Mainly for mel purposes. 41 | /// must be public so maya can access 42 | //---------------------------------------------------------------------------------------------------------------------- 43 | static const MString m_typeName; 44 | 45 | private : 46 | //---------------------------------------------------------------------------------------------------------------------- 47 | /// @brief the amplitude of our node 48 | //---------------------------------------------------------------------------------------------------------------------- 49 | static MObject m_amplitude; 50 | //---------------------------------------------------------------------------------------------------------------------- 51 | /// @brief the frequency of our node 52 | //---------------------------------------------------------------------------------------------------------------------- 53 | static MObject m_frequency; 54 | //---------------------------------------------------------------------------------------------------------------------- 55 | /// @brief the input to our node 56 | //---------------------------------------------------------------------------------------------------------------------- 57 | static MObject m_time; 58 | //---------------------------------------------------------------------------------------------------------------------- 59 | /// @brief the output to our node 60 | //---------------------------------------------------------------------------------------------------------------------- 61 | static MObject m_output; 62 | //---------------------------------------------------------------------------------------------------------------------- 63 | /// @brief the use sine cose etc to our node 64 | //---------------------------------------------------------------------------------------------------------------------- 65 | static MObject m_functionType; 66 | //---------------------------------------------------------------------------------------------------------------------- 67 | /// @brief enum of different function types 68 | //---------------------------------------------------------------------------------------------------------------------- 69 | enum FunctionTypes{SINE,COS,COMPLEX}; 70 | 71 | }; 72 | 73 | #endif 74 | //---------------------------------------------------------------------------------------------------------------------- 75 | 76 | 77 | -------------------------------------------------------------------------------- /Lecture2/SineNode/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "SineNode.h" 2 | #include 3 | 4 | //---------------------------------------------------------------------------------------------------------------------- 5 | 6 | MStatus initializePlugin( MObject obj ) 7 | { 8 | MStatus status; 9 | MFnPlugin plugin( obj, "", "NCCA" , "Any" ); 10 | 11 | // register our nodes and the commands which will be called 12 | status = plugin.registerNode( "sineNode", SineNode::m_id, &SineNode::creator, &SineNode::initialize, MPxNode::kDependNode ); 13 | if (!status) 14 | { 15 | status.perror("Unable to register sineNode" ); 16 | return status; 17 | } 18 | 19 | 20 | return status; 21 | } 22 | 23 | //---------------------------------------------------------------------------------------------------------------------- 24 | 25 | MStatus uninitializePlugin( MObject obj ) 26 | { 27 | MStatus status; 28 | MFnPlugin plugin( obj ); 29 | 30 | 31 | status = plugin.deregisterNode( SineNode::m_id ); 32 | if (!status) 33 | { 34 | status.perror( "unable to deregister sineNode" ); 35 | return status; 36 | } 37 | 38 | return status; 39 | } 40 | //---------------------------------------------------------------------------------------------------------------------- 41 | 42 | 43 | -------------------------------------------------------------------------------- /Lecture2/SineNode/src/SineNode.cpp: -------------------------------------------------------------------------------- 1 | #include "SineNode.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | //---------------------------------------------------------------------------------------------------------------------- 9 | /// @brief simple macro to check status and return if error 10 | /// originally written by Sola Aina 11 | //---------------------------------------------------------------------------------------------------------------------- 12 | #define CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( stat , message ) \ 13 | if( !status ) \ 14 | { \ 15 | MString errorString = status.errorString() + " -- " + MString( message ); \ 16 | MGlobal::displayError( errorString ); \ 17 | return MStatus::kFailure; \ 18 | } \ 19 | //---------------------------------------------------------------------------------------------------------------------- 20 | /// @brief simple macro to check status and return if error 21 | /// originally written by Sola Aina 22 | //---------------------------------------------------------------------------------------------------------------------- 23 | 24 | #define CHECK_STATUS_AND_RETURN_IF_FAIL( stat , message ) \ 25 | if( !status ) \ 26 | { \ 27 | MString errorString = status.errorString() + " -- " + MString( message ); \ 28 | MGlobal::displayError( errorString ); \ 29 | } \ 30 | 31 | /// @brief macro to get rid of compiler warnings 32 | #define UNUSED(arg) (void)arg; 33 | 34 | //---------------------------------------------------------------------------------------------------------------------- 35 | // as these are static initialisers we need to set them here. 36 | MTypeId SineNode::m_id( 0x70001 ); // numeric Id of node 37 | const MString SineNode::m_typeName( "sineNode" ); 38 | MObject SineNode::m_amplitude; 39 | MObject SineNode::m_frequency; 40 | MObject SineNode::m_time; 41 | MObject SineNode::m_output; 42 | MObject SineNode::m_functionType; 43 | 44 | //---------------------------------------------------------------------------------------------------------------------- 45 | MStatus SineNode::initialize() 46 | { 47 | MStatus status; 48 | // the input attibute is a time attribute that will be connected to the global time node attribute, 49 | // time1.outTime 50 | // 51 | // Any attribute types that are measured in changeable units, ie distance(cm, mm, miles, feet), 52 | // angles( degrees, radians ), time( secs, mins, frames ) etc should be created using the 53 | // MFnUnitAttribute function set. 54 | // 55 | MFnUnitAttribute uAttr; 56 | m_time = uAttr.create( "time", "t", MFnUnitAttribute::kTime ); 57 | // since the input attribute will be connected to the time value, there is little point 58 | // in storing this attribute within the maya binary file. There is also no point in 59 | // making the attribute keyable. 60 | uAttr.setStorable(false); 61 | uAttr.setKeyable(false); 62 | status = addAttribute( m_time ); 63 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to add \"time\" attribute to SineNode" ); 64 | 65 | 66 | MFnEnumAttribute enumAttr; 67 | // note that f is used later as the shortcut for frequency so we use ft 68 | m_functionType = enumAttr.create( "function", "ft", 0, &status ); 69 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to create \"function\" attribute" ); 70 | 71 | enumAttr.addField( "sine", SINE ); 72 | enumAttr.addField( "cos", COS ); 73 | enumAttr.addField( "complex", COMPLEX ); 74 | enumAttr.setHidden( false ); 75 | enumAttr.setKeyable( true ); 76 | status = addAttribute( m_functionType ); 77 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to add \"function type\" attribute to SineNode" ); 78 | 79 | 80 | MFnNumericAttribute numAttr; 81 | 82 | m_amplitude = numAttr.create( "amplitude", "a", MFnNumericData::kDouble, 1.0, &status ); 83 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to create \"amplitude\" attribute" ); 84 | numAttr.setChannelBox( true ); // length attribute appears in channel box 85 | // add attribute 86 | status = addAttribute( m_amplitude ); 87 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to add \"amplitude\" attribute to SineNode" ); 88 | 89 | // now the freq one 90 | m_frequency = numAttr.create( "frequency", "f", MFnNumericData::kDouble, 1.0, &status ); 91 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to create \"frequency\" attribute" ); 92 | numAttr.setChannelBox( true ); // freq attribute appears in channel box 93 | // Add attribute to node 94 | status = addAttribute( m_frequency ); 95 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to add \"freq\" attribute to SineNode" ); 96 | 97 | 98 | 99 | // create the output attribute 100 | m_output = numAttr.create( "output", "o",MFnNumericData::kDouble,0.0f); 101 | 102 | // make this attribute a read only output value 103 | numAttr.setReadable(true); 104 | numAttr.setKeyable(false); 105 | numAttr.setWritable(false); 106 | status = addAttribute( m_output ); 107 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to add \"output\" attribute to SineNode" ); 108 | 109 | attributeAffects(m_time, m_output); 110 | attributeAffects(m_frequency, m_output); 111 | attributeAffects(m_amplitude, m_output); 112 | attributeAffects(m_functionType,m_output); 113 | 114 | return MStatus::kSuccess; 115 | } 116 | 117 | //---------------------------------------------------------------------------------------------------------------------- 118 | // This method should be overridden in user defined nodes. 119 | // Recompute the given output based on the nodes inputs. 120 | // The plug represents the data value that needs to be recomputed, and the data block holds the storage 121 | // for all of the node's attributes. 122 | //---------------------------------------------------------------------------------------------------------------------- 123 | MStatus SineNode::compute( const MPlug &_plug , MDataBlock &_data ) 124 | { 125 | MStatus status; 126 | if( _plug == m_output) 127 | { 128 | // get data handle for length 129 | MDataHandle timeData = _data.inputValue( m_time , &status ); 130 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to get data handle for time plug" ); 131 | 132 | // get data handle for function 133 | MDataHandle funcData = _data.inputValue( m_functionType , &status ); 134 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to get data handle for function plug" ); 135 | 136 | 137 | 138 | MDataHandle outputData = _data.outputValue(m_output); 139 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to get data handle for output plug" ); 140 | 141 | // get the input value as a time value 142 | MTime Time = timeData.asTime(); 143 | 144 | // get data handle for length 145 | MDataHandle dataHandle = _data.inputValue( m_frequency , &status ); 146 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to get data handle for freq plug" ); 147 | // now get the value for the data handle as a double 148 | double freq = dataHandle.asDouble(); 149 | dataHandle = _data.inputValue( m_amplitude , &status ); 150 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to get data handle for amplitude plug" ); 151 | // now get the value for the data handle as a double 152 | double amp = dataHandle.asDouble(); 153 | double out; 154 | switch(funcData.asInt()) 155 | { 156 | case SINE : out=amp*sin(freq*M_PI*Time.as(MTime::kSeconds)); break; 157 | case COS : out=amp*cos(freq*M_PI*Time.as(MTime::kSeconds)); break; 158 | case COMPLEX : 159 | out=amp*cos(freq*M_PI*Time.as(MTime::kSeconds)) + 160 | amp*sin(2*freq*M_PI*Time.as(MTime::kSeconds)); 161 | break; 162 | } 163 | 164 | 165 | // set the output to the sine of the time (in seconds) 166 | outputData.set(out); 167 | // clean the output plug, ie unset it from dirty so that maya does not re-evaluate it 168 | _data.setClean( _plug ); 169 | 170 | return MStatus::kSuccess; 171 | } 172 | 173 | return MStatus::kUnknownParameter; 174 | } 175 | 176 | //---------------------------------------------------------------------------------------------------------------------- 177 | void* SineNode::creator() 178 | { 179 | return new SineNode(); 180 | } 181 | //---------------------------------------------------------------------------------------------------------------------- 182 | 183 | 184 | -------------------------------------------------------------------------------- /Lecture2/TriLocatorNode/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(triLocator) 3 | # include the project setting file 4 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 5 | 6 | 7 | # set SOURCE_FILES 8 | set(SOURCE_FILES src/Plugin.cpp 9 | src/TriLocatorNode.cpp 10 | src/TriLocatorNodeData.cpp 11 | src/TriLocatorNodeDrawOverride.cpp 12 | include/TriLocatorNode.h 13 | include/TriLocatorNodeData.h 14 | include/TriLocatorNodeDrawOverride.h 15 | 16 | 17 | ) 18 | include_directories(include) 19 | # set linking libraries 20 | set(LIBRARIES OpenMaya OpenMayaRender OpenMayaUI Foundation ) 21 | # This function is from the MayaAPI cmake files and get the platform specific includes for OpenGL 22 | find_opengl() 23 | 24 | # Build plugin 25 | build_plugin() 26 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 27 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 28 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 29 | ) 30 | -------------------------------------------------------------------------------- /Lecture2/TriLocatorNode/README.md: -------------------------------------------------------------------------------- 1 | # TriangleNodeLocator 2 | 3 | This is my attempt as the smallest Viewport 2.0 locator node, to help untangle some of the mess that is the footprintManip demo (from Autodesk). 4 | 5 | This demo draws a simple Triangle with colours at each vertex and normals. It also has data for line (wireframe) and a single solid triangle. 6 | 7 | This is the basis of demos to create more complex meshes with viewport 2.0 8 | -------------------------------------------------------------------------------- /Lecture2/TriLocatorNode/include/TriLocatorNode.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class TriLocatorNode : public MPxLocatorNode 6 | { 7 | public: 8 | TriLocatorNode()=default; 9 | ~TriLocatorNode()=default ; 10 | MStatus compute(const MPlug& plug, MDataBlock &data) override; 11 | bool isBounded() const override; 12 | MBoundingBox boundingBox() const override; 13 | static void * creator(); 14 | static MStatus initialize(); 15 | static MObject m_size; // The size of the triangle 16 | public: 17 | static MTypeId s_id; 18 | static MString s_drawDbClassification; 19 | static MString s_drawRegistrantId; 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /Lecture2/TriLocatorNode/include/TriLocatorNodeData.h: -------------------------------------------------------------------------------- 1 | #ifndef TRILOCATORNODEDATA_H_ 2 | #define TRILOCATORNODEDATA_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | class TriLocatorNodeData : public MUserData 11 | { 12 | public: 13 | TriLocatorNodeData() : MUserData(false) 14 | { 15 | m_normalArray = new MVectorArray(); 16 | m_colourArray = new MColorArray(); 17 | } // don't delete after draw 18 | ~TriLocatorNodeData() override 19 | { 20 | delete m_normalArray; 21 | delete m_colourArray; 22 | } 23 | 24 | //MColor m_colour; 25 | MPointArray m_lineArray; 26 | MPointArray m_triangleArray; 27 | MVectorArray *m_normalArray; // raw pointers can we use smart ones? 28 | MColorArray *m_colourArray; 29 | }; 30 | 31 | #endif -------------------------------------------------------------------------------- /Lecture2/TriLocatorNode/include/TriLocatorNodeDrawOverride.h: -------------------------------------------------------------------------------- 1 | #ifndef TRILOCATORNODEDRAWOVERRIDE_H_ 2 | #define TRILOCATORNODEDRAWOVERRIDE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | class TriLocatorNodeDrawOverride : public MHWRender::MPxDrawOverride 14 | { 15 | public: 16 | static MHWRender::MPxDrawOverride* Creator(const MObject& obj) 17 | { 18 | return new TriLocatorNodeDrawOverride(obj); 19 | } 20 | 21 | ~TriLocatorNodeDrawOverride() override; 22 | 23 | MHWRender::DrawAPI supportedDrawAPIs() const override; 24 | 25 | bool isBounded(const MDagPath& objPath,const MDagPath& cameraPath) const override; 26 | 27 | MBoundingBox boundingBox(const MDagPath& objPath,const MDagPath& cameraPath) const override; 28 | 29 | bool disableInternalBoundingBoxDraw() const override; 30 | 31 | MUserData* prepareForDraw( const MDagPath& objPath,const MDagPath& cameraPath,const MHWRender::MFrameContext& frameContext,MUserData* oldData) override; 32 | 33 | bool hasUIDrawables() const override { return true; } 34 | 35 | void addUIDrawables(const MDagPath& objPath,MHWRender::MUIDrawManager& drawManager, const MHWRender::MFrameContext& frameContext, const MUserData* data) override; 36 | 37 | protected: 38 | MBoundingBox m_CurrentBoundingBox; 39 | 40 | MCallbackId m_ModelEditorChangedCbId; 41 | MObject m_triLocator; 42 | 43 | private: 44 | TriLocatorNodeDrawOverride(const MObject& obj); 45 | 46 | float getMultiplier(const MDagPath& objPath) const; 47 | static void OnModelEditorChanged(void *clientData); 48 | }; 49 | 50 | 51 | #endif -------------------------------------------------------------------------------- /Lecture2/TriLocatorNode/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "TriLocatorNode.h" 2 | #include "TriLocatorNodeDrawOverride.h" 3 | #include 4 | #include 5 | 6 | 7 | MStatus initializePlugin(MObject obj) 8 | { 9 | MStatus status; 10 | MFnPlugin plugin(obj, "NCCA", "1.0", "Any"); 11 | 12 | status = plugin.registerNode("TriLocatorNode", 13 | TriLocatorNode::s_id, 14 | &TriLocatorNode::creator, 15 | &TriLocatorNode::initialize, 16 | MPxNode::kLocatorNode, 17 | &TriLocatorNode::s_drawDbClassification 18 | ); 19 | if (!status) 20 | { 21 | status.perror("registerNode"); 22 | return status; 23 | } 24 | 25 | status = MHWRender::MDrawRegistry::registerDrawOverrideCreator( 26 | TriLocatorNode::s_drawDbClassification, 27 | TriLocatorNode::s_drawRegistrantId, 28 | TriLocatorNodeDrawOverride::Creator); 29 | if (!status) 30 | { 31 | status.perror("registerDrawOverrideCreator"); 32 | return status; 33 | } 34 | 35 | return status; 36 | } 37 | 38 | 39 | MStatus uninitializePlugin(MObject obj) 40 | { 41 | MStatus status; 42 | MFnPlugin plugin(obj); 43 | 44 | status = plugin.deregisterNode(TriLocatorNode::s_id); 45 | if (!status) 46 | { 47 | status.perror("deregisterNode"); 48 | return status; 49 | } 50 | 51 | status = MHWRender::MDrawRegistry::deregisterDrawOverrideCreator( 52 | TriLocatorNode::s_drawDbClassification, 53 | TriLocatorNode::s_drawRegistrantId); 54 | if (!status) 55 | { 56 | status.perror("deregisterNode"); 57 | return status; 58 | } 59 | return status; 60 | } 61 | -------------------------------------------------------------------------------- /Lecture2/TriLocatorNode/src/TriLocatorNode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "TriLocatorNode.h" 5 | 6 | MTypeId TriLocatorNode::s_id( 0x8001c ); 7 | MObject TriLocatorNode::m_size; 8 | MString TriLocatorNode::s_drawDbClassification("drawdb/geometry/TriLocator"); 9 | MString TriLocatorNode::s_drawRegistrantId("TriManipPlugin"); 10 | 11 | 12 | 13 | MStatus TriLocatorNode::compute(const MPlug &plug, MDataBlock &data) 14 | { 15 | return MS::kUnknownParameter; 16 | } 17 | 18 | 19 | bool TriLocatorNode::isBounded() const 20 | { 21 | return true; 22 | } 23 | 24 | 25 | MBoundingBox TriLocatorNode::boundingBox() const 26 | { 27 | // Get the size 28 | // 29 | MObject thisNode = thisMObject(); 30 | MPlug plug(thisNode, m_size); 31 | MDistance sizeVal; 32 | plug.getValue(sizeVal); 33 | 34 | double multiplier = sizeVal.asCentimeters(); 35 | constexpr static float size = 0.5f; 36 | MPoint corner1(-size,size, -0.0f); 37 | MPoint corner2(size, -size, 0.0f); 38 | 39 | corner1 = corner1 * multiplier; 40 | corner2 = corner2 * multiplier; 41 | 42 | return MBoundingBox(corner1, corner2); 43 | } 44 | 45 | 46 | void* TriLocatorNode::creator() 47 | { 48 | return new TriLocatorNode(); 49 | } 50 | 51 | 52 | MStatus TriLocatorNode::initialize() 53 | { 54 | MFnUnitAttribute unitFn; 55 | MStatus stat; 56 | 57 | m_size = unitFn.create("size", "sz", MFnUnitAttribute::kDistance); 58 | unitFn.setDefault(1.0); 59 | unitFn.setStorable(true); 60 | unitFn.setWritable(true); 61 | 62 | stat = addAttribute(m_size); 63 | if (!stat) 64 | { 65 | stat.perror("addAttribute"); 66 | return stat; 67 | } 68 | 69 | return MS::kSuccess; 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /Lecture2/TriLocatorNode/src/TriLocatorNodeData.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NCCA/MayaAPICode/f0ecc9f9559913be642d7d38069d33b915de2d60/Lecture2/TriLocatorNode/src/TriLocatorNodeData.cpp -------------------------------------------------------------------------------- /Lecture2/TriLocatorNode/src/TriLocatorNodeDrawOverride.cpp: -------------------------------------------------------------------------------- 1 | #include "TriLocatorNodeDrawOverride.h" 2 | #include "TriLocatorNode.h" 3 | #include "TriLocatorNodeData.h" 4 | #include 5 | #include 6 | #include 7 | 8 | // Tri Data 9 | // V2 10 | // 11 | // V1 V3 12 | 13 | constexpr static float scale = 0.5f; 14 | constexpr static std::array triangle = 15 | { -scale, -scale, 0.0f, 16 | 0.0f, scale, 0.0f , 17 | scale, -scale, 0.0f }; 18 | 19 | 20 | // By setting isAlwaysDirty to false in MPxDrawOverride constructor, the 21 | // draw override will be updated (via prepareForDraw()) only when the node 22 | // is marked dirty via DG evaluation or dirty propagation. Additional 23 | // callback is also added to explicitly mark the node as being dirty (via 24 | // MRenderer::setGeometryDrawDirty()) for certain circumstances. Note that 25 | // the draw callback in MPxDrawOverride constructor is set to NULL in order 26 | // to achieve better performance. 27 | TriLocatorNodeDrawOverride::TriLocatorNodeDrawOverride(const MObject& obj): MHWRender::MPxDrawOverride(obj, NULL, false), m_triLocator(obj) 28 | { 29 | m_ModelEditorChangedCbId = MEventMessage::addEventCallback( 30 | "modelEditorChanged", OnModelEditorChanged, this); 31 | } 32 | 33 | TriLocatorNodeDrawOverride::~TriLocatorNodeDrawOverride() 34 | { 35 | if (m_ModelEditorChangedCbId != 0) 36 | { 37 | MMessage::removeCallback(m_ModelEditorChangedCbId); 38 | m_ModelEditorChangedCbId = 0; 39 | } 40 | } 41 | 42 | void TriLocatorNodeDrawOverride::OnModelEditorChanged(void *clientData) 43 | { 44 | // Mark the node as being dirty so that it can update on display mode switch, 45 | // e.g. between wireframe and shaded. 46 | TriLocatorNodeDrawOverride *ovr = static_cast(clientData); 47 | if (ovr) MHWRender::MRenderer::setGeometryDrawDirty(ovr->m_triLocator); 48 | } 49 | 50 | MHWRender::DrawAPI TriLocatorNodeDrawOverride::supportedDrawAPIs() const 51 | { 52 | return MHWRender::kAllDevices; 53 | } 54 | 55 | float TriLocatorNodeDrawOverride::getMultiplier(const MDagPath& objPath) const 56 | { 57 | // Retrieve value of the size attribute from the node 58 | MStatus status; 59 | MObject TriLocatorNode = objPath.node(&status); 60 | if (status) 61 | { 62 | MPlug plug(TriLocatorNode, TriLocatorNode::m_size); 63 | if (!plug.isNull()) 64 | { 65 | MDistance sizeVal; 66 | if (plug.getValue(sizeVal)) 67 | { 68 | return (float)sizeVal.asCentimeters(); 69 | } 70 | } 71 | } 72 | 73 | return 1.0f; 74 | } 75 | 76 | bool TriLocatorNodeDrawOverride::isBounded(const MDagPath& /*objPath*/, 77 | const MDagPath& /*cameraPath*/) const 78 | { 79 | return true; 80 | } 81 | 82 | MBoundingBox TriLocatorNodeDrawOverride::boundingBox( 83 | const MDagPath& objPath, 84 | const MDagPath& cameraPath) const 85 | { 86 | MPoint corner1( -scale, scale, 0.0f ); 87 | MPoint corner2( scale, -scale, 0.0f ); 88 | 89 | float multiplier = getMultiplier(objPath); 90 | corner1 = corner1 * multiplier; 91 | corner2 = corner2 * multiplier; 92 | 93 | TriLocatorNodeDrawOverride *nonConstThis = (TriLocatorNodeDrawOverride *)this; 94 | nonConstThis->m_CurrentBoundingBox.clear(); 95 | nonConstThis->m_CurrentBoundingBox.expand( corner1 ); 96 | nonConstThis->m_CurrentBoundingBox.expand( corner2 ); 97 | 98 | return m_CurrentBoundingBox; 99 | } 100 | 101 | bool TriLocatorNodeDrawOverride::disableInternalBoundingBoxDraw() const 102 | { 103 | return false; 104 | } 105 | 106 | MUserData* TriLocatorNodeDrawOverride::prepareForDraw(const MDagPath& objPath,const MDagPath& cameraPath,const MHWRender::MFrameContext& frameContext,MUserData* oldData) 107 | { 108 | // Retrieve data cache (create if does not exist) 109 | TriLocatorNodeData* data = dynamic_cast(oldData); 110 | if (!data) 111 | { 112 | data = new TriLocatorNodeData(); 113 | } 114 | 115 | float fMultiplier = getMultiplier(objPath); 116 | 117 | data->m_lineArray.clear(); 118 | // line 1 119 | data->m_lineArray.append(triangle[0][0] * fMultiplier, triangle[0][1] * fMultiplier, triangle[0][2] * fMultiplier); 120 | data->m_lineArray.append(triangle[1][0] * fMultiplier, triangle[1][1] * fMultiplier, triangle[1][2] * fMultiplier); 121 | 122 | // line 2 123 | data->m_lineArray.append(triangle[1][0] * fMultiplier, triangle[1][1] * fMultiplier, triangle[1][2] * fMultiplier); 124 | data->m_lineArray.append(triangle[2][0] * fMultiplier, triangle[2][1] * fMultiplier, triangle[2][2] * fMultiplier); 125 | 126 | // line 3 127 | data->m_lineArray.append(triangle[2][0] * fMultiplier, triangle[2][1] * fMultiplier, triangle[2][2] * fMultiplier); 128 | data->m_lineArray.append(triangle[0][0] * fMultiplier, triangle[0][1] * fMultiplier, triangle[0][2] * fMultiplier); 129 | 130 | data->m_triangleArray.clear(); 131 | 132 | data->m_triangleArray.append(triangle[0][0] * fMultiplier, triangle[0][1] * fMultiplier, triangle[0][2] * fMultiplier); 133 | data->m_triangleArray.append(triangle[1][0] * fMultiplier, triangle[1][1] * fMultiplier, triangle[1][2] * fMultiplier); 134 | data->m_triangleArray.append(triangle[2][0] * fMultiplier, triangle[2][1] * fMultiplier, triangle[2][2] * fMultiplier); 135 | // per vertex normals 136 | 137 | MVector normal(0.0f, 0.0f, 1.0f); 138 | data->m_normalArray->clear(); 139 | data->m_normalArray->append(normal); 140 | data->m_normalArray->append(normal); 141 | data->m_normalArray->append(normal); 142 | data->m_colourArray->clear(); 143 | data->m_colourArray->append(MColor(1.0f, 0.0f, 0.0f)); 144 | data->m_colourArray->append(MColor(0.0f, 1.0f, 0.0f)); 145 | data->m_colourArray->append(MColor(0.0f, 0.0f, 1.0f)); 146 | 147 | 148 | // compute data and cache it 149 | // data->m_colour = MHWRender::MGeometryUtilities::wireframeColor(objPath); 150 | 151 | return data; 152 | } 153 | 154 | void TriLocatorNodeDrawOverride::addUIDrawables(const MDagPath& objPath,MHWRender::MUIDrawManager& drawManager,const MHWRender::MFrameContext& frameContext,const MUserData* data) 155 | { 156 | 157 | auto locatorData = static_cast< const TriLocatorNodeData *>(data); 158 | 159 | drawManager.beginDrawable(); 160 | 161 | // Draw the tri print solid/wireframe 162 | // drawManager.setColor( locatorData->m_colour ); 163 | drawManager.setDepthPriority(5); 164 | 165 | if (frameContext.getDisplayStyle() & MHWRender::MFrameContext::kGouraudShaded) 166 | { 167 | drawManager.mesh(MHWRender::MUIDrawManager::kTriangles, locatorData->m_triangleArray, locatorData->m_normalArray, locatorData->m_colourArray); 168 | } 169 | 170 | drawManager.mesh(MHWRender::MUIDrawManager::kLineStrip, locatorData->m_lineArray, locatorData->m_normalArray, locatorData->m_colourArray); 171 | 172 | 173 | drawManager.endDrawable(); 174 | } 175 | -------------------------------------------------------------------------------- /Lecture3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | #------------------------------------------------------------------------------------------- 3 | # I'm going to use vcpk in most cases for our install of 3rd party libs 4 | # this is going to check the environment variable for CMAKE_TOOLCHAIN_FILE and this must point to where 5 | # vcpkg.cmake is in the University this is set in your .bash_profile to 6 | # export CMAKE_TOOLCHAIN_FILE=/public/devel/2020/vcpkg/scripts/buildsystems/vcpkg.cmake 7 | #------------------------------------------------------------------------------------------- 8 | if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{CMAKE_TOOLCHAIN_FILE}) 9 | set(CMAKE_TOOLCHAIN_FILE $ENV{CMAKE_TOOLCHAIN_FILE}) 10 | endif() 11 | 12 | # Name of the project 13 | project(Lecture3BuildAll) 14 | 15 | add_subdirectory(${PROJECT_SOURCE_DIR}/DAGIt/ ) 16 | add_subdirectory(${PROJECT_SOURCE_DIR}/Frames/ ) 17 | add_subdirectory(${PROJECT_SOURCE_DIR}/GetSelection/ ) 18 | add_subdirectory(${PROJECT_SOURCE_DIR}/MComputation/ ) 19 | add_subdirectory(${PROJECT_SOURCE_DIR}/RibExporter/ ) 20 | 21 | -------------------------------------------------------------------------------- /Lecture3/DAGIt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | 4 | project(DAGIt) 5 | # include the project setting file 6 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 7 | 8 | # specify project name 9 | set(PROJECT_NAME DAGIt) 10 | 11 | # set SOURCE_FILES 12 | set(SOURCE_FILES DAGIt.cpp) 13 | 14 | # set linking libraries 15 | set(LIBRARIES OpenMaya Foundation) 16 | # Build plugin 17 | build_plugin() 18 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 19 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 21 | ) 22 | -------------------------------------------------------------------------------- /Lecture3/DAGIt/DAGIt.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // This is a macro to create a simple command 13 | // the compiler expands it to a bunch of code 14 | 15 | DeclareSimpleCommand( DAGit , "NCCA", "Any"); 16 | 17 | MStatus DAGit::doIt( const MArgList& ) 18 | { 19 | 20 | MItDag dagIt (MItDag::kBreadthFirst); 21 | MFnDagNode nodeFn; 22 | MObject node; 23 | uint count=0; 24 | MString message; 25 | // numeric arguments are harder to use, we can format using the following 26 | // MStringArray and set the arguments we need in the message string 27 | // using format. We have 4 arguments max with a matrix but we can use less if we want 28 | MStringArray args(4,""); 29 | MGlobal::displayInfo("traversing DAG"); 30 | for (dagIt.next(); !dagIt.isDone(); dagIt.next()) 31 | { 32 | // note item() is now deprecated so we use currentItem() 33 | node = dagIt.currentItem(); 34 | nodeFn.setObject( node ); 35 | if( node.hasFn(MFn::kNamedObject)) 36 | { 37 | // for simple string objects formatting can be done with + 38 | MGlobal::displayInfo("Object "+nodeFn.name()+ " type " + nodeFn.typeName()); 39 | MGlobal::displayInfo("fullPathName "+nodeFn.fullPathName()); 40 | MGlobal::displayInfo("partialPathName "+nodeFn.partialPathName()); 41 | MGlobal::displayInfo("Parent path "+nodeFn.partialPathName()); 42 | MGlobal::displayInfo("Parent namespace "+nodeFn.parentNamespace()); 43 | // numeric arguments need conversion so we use the args string array. 44 | args[0].set(nodeFn.childCount()); 45 | args[1].set(nodeFn.parentCount()); 46 | // followed by the format with the ^1s string marker for the first argument 47 | // and the ^2s string marker for the second argument etc. 48 | message.format("number of children= ^1s number of parents= ^2s ",args); 49 | MGlobal::displayInfo(message); 50 | 51 | args[0].set(nodeFn.attributeCount()); 52 | message.format("attribute count = ^1s ",args); 53 | MGlobal::displayInfo(message); 54 | MGlobal::displayInfo("Transformation Matrix"); 55 | MMatrix m=nodeFn.transformationMatrix(); 56 | // set row 1 args 57 | args[0].set(m(0,0)); 58 | args[1].set(m(0,1)); 59 | args[2].set(m(0,2)); 60 | args[3].set(m(0,3)); 61 | message.format("[^1s,^2s,^3s,^4s]",args); 62 | MGlobal::displayInfo(message); 63 | // set row 2 args 64 | args[0].set(m(1,0)); 65 | args[1].set(m(1,1)); 66 | args[2].set(m(1,2)); 67 | args[3].set(m(1,3)); 68 | message.format("[^1s,^2s,^3s,^4s]",args); 69 | MGlobal::displayInfo(message); 70 | // set row 3 args 71 | args[0].set(m(2,0)); 72 | args[1].set(m(2,1)); 73 | args[2].set(m(2,2)); 74 | args[3].set(m(2,3)); 75 | message.format("[^1s,^2s,^3s,^4s]",args); 76 | MGlobal::displayInfo(message); 77 | // set row 4 args 78 | args[0].set(m(3,0)); 79 | args[1].set(m(3,1)); 80 | args[2].set(m(3,2)); 81 | args[3].set(m(3,3)); 82 | message.format("[^1s,^2s,^3s,^4s]",args); 83 | MGlobal::displayInfo(message); 84 | 85 | MGlobal::displayInfo("*********************************************************************"); 86 | ++count; 87 | } 88 | 89 | } 90 | args[0].set(count); 91 | message.format("found ^1s objects",args); 92 | MGlobal::displayInfo(message); 93 | 94 | return MS::kSuccess; 95 | } 96 | 97 | -------------------------------------------------------------------------------- /Lecture3/Frames/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | 4 | project(TimeLine) 5 | # include the project setting file 6 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 7 | 8 | # specify project name 9 | set(PROJECT_NAME TimeLine) 10 | 11 | # set SOURCE_FILES 12 | set(SOURCE_FILES TimeLine.cpp) 13 | 14 | # set linking libraries 15 | set(LIBRARIES OpenMaya OpenMayaAnim Foundation) 16 | # Build plugin 17 | build_plugin() 18 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 19 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 21 | ) 22 | -------------------------------------------------------------------------------- /Lecture3/Frames/TimeLine.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | // This is a macro to create a simple command 8 | // the compiler expands it to a bunch of code 9 | 10 | DeclareSimpleCommand( TimeLine , "NCCA", "Any"); 11 | 12 | MStatus TimeLine::doIt( const MArgList& ) 13 | { 14 | 15 | MTime currframe; 16 | MAnimControl anim; 17 | // lets set the min time 18 | currframe.setValue(0); 19 | anim.setMinTime(currframe); 20 | // lets set the max time 21 | currframe.setValue(100); 22 | anim.setMaxTime(currframe); 23 | 24 | // now lets accesss this info 25 | char msg[100]; 26 | snprintf(msg,100,"start frame =%f",anim.animationStartTime().value() ); 27 | MGlobal::displayInfo(msg); 28 | snprintf(msg,100,"end frame =%f",anim.animationEndTime().value() ); 29 | MGlobal::displayInfo(msg); 30 | int end=anim.animationEndTime().value(); 31 | for(float i=0.0f; i ${PLUGINS_FOLDER}/${OutputName} 21 | ) 22 | -------------------------------------------------------------------------------- /Lecture3/GetSelection/GetSelection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // This is a macro to create a simple command 10 | // the compiler expands it to a bunch of code 11 | 12 | DeclareSimpleCommand( GetSelection , "NCCA", "Any"); 13 | 14 | MStatus GetSelection::doIt( const MArgList& ) 15 | { 16 | 17 | MDagPath node; 18 | MObject component; 19 | MSelectionList list; 20 | MFnDagNode nodeFn; 21 | // get the currently selected objects 22 | MGlobal::getActiveSelectionList( list ); 23 | // get the size of the list 24 | uint size=list.length(); 25 | for (uint i=0; i< size; ++i ) 26 | { 27 | // get the DAG path for this object and place the node and component values 28 | list.getDagPath( i, node, component ); 29 | // attach the found node to the DagNode function set 30 | nodeFn.setObject( node ); 31 | // now use this to get the object name and display 32 | MGlobal::displayInfo("Object "+nodeFn.name()+ " is selected"); 33 | } 34 | return MS::kSuccess; 35 | } 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Lecture3/GetSelection/get_selection.py: -------------------------------------------------------------------------------- 1 | import maya.api.OpenMaya as OM 2 | 3 | node_fn = OM.MFnDagNode() 4 | list = OM.MGlobal.getActiveSelectionList() 5 | for i in range(0, list.length()): 6 | path = list.getDagPath(i) 7 | node_fn.setObject(path) 8 | print(f"Object {node_fn.name()} is selected") 9 | 10 | 11 | dagIt = OM.MItDag(OM.MItDag.kBreadthFirst) 12 | while not dagIt.isDone(): 13 | node = dagIt.currentItem() 14 | node_fn.setObject(node) 15 | if node.hasFn(OM.MFn.kNamedObject): 16 | print(f"Object {node_fn.name()} type {node_fn.typeName}") 17 | m = node_fn.transformationMatrix() 18 | print(m) 19 | dagIt.next() 20 | -------------------------------------------------------------------------------- /Lecture3/MComputation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | 4 | project(MComputation) 5 | # include the project setting file 6 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 7 | 8 | # specify project name 9 | set(PROJECT_NAME MComputation) 10 | 11 | # set SOURCE_FILES 12 | set(SOURCE_FILES MComputation.cpp) 13 | 14 | # set linking libraries 15 | set(LIBRARIES OpenMaya Foundation) 16 | # Build plugin 17 | build_plugin() 18 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 19 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 21 | ) 22 | -------------------------------------------------------------------------------- /Lecture3/MComputation/MComputation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | // This is a macro to create a simple command 6 | // the compiler expands it to a bunch of code 7 | 8 | DeclareSimpleCommand( MComputationTest , "NCCA", "Any"); 9 | 10 | MStatus MComputationTest::doIt( const MArgList& ) 11 | { 12 | 13 | // create an MComputation class for heavy calculation type things 14 | MComputation Interupter; 15 | 16 | // set the start of the heavy computation 17 | Interupter.beginComputation(); 18 | 19 | while(1) 20 | { 21 | MGlobal::displayInfo("doing a computation press ESC to stop"); 22 | // just a little sleep loop so we don't thrash things too much 23 | for(int i=0; i<99999999; ++i){;} 24 | if( Interupter.isInterruptRequested() ) 25 | { 26 | Interupter.endComputation(); 27 | MGlobal::displayInfo("intrupted by escape"); 28 | return MS::kFailure; 29 | } 30 | 31 | } 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Lecture3/MComputation/mcomp.py: -------------------------------------------------------------------------------- 1 | import maya.OpenMaya as OM 2 | 3 | # as these can take time to process we have an interupter to allow for the process to be 4 | # stopped 5 | interupter = OM.MComputation() 6 | # set the start of the heavy computation 7 | interupter.beginComputation() 8 | 9 | while True: 10 | if interupter.isInterruptRequested(): 11 | print("intrupted by escape") 12 | interupter.endComputation() 13 | quit = True 14 | break 15 | print(".", end="") 16 | cmds.pause(seconds=1) 17 | -------------------------------------------------------------------------------- /Lecture3/RibExporter/.qmake.stash: -------------------------------------------------------------------------------- 1 | QMAKE_XCODE_DEVELOPER_PATH = /Applications/Xcode.app/Contents/Developer 2 | QMAKE_XCODE_VERSION = 5.0.2 3 | QMAKE_MAC_SDK.macosx.path = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk 4 | QMAKE_MAC_SDK.macx-clang.macosx.QMAKE_CC = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang 5 | QMAKE_MAC_SDK.macx-clang.macosx.QMAKE_CXX = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ 6 | QMAKE_MAC_SDK.macx-clang.macosx.QMAKE_FIX_RPATH = \ 7 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/install_name_tool \ 8 | -id 9 | QMAKE_MAC_SDK.macx-clang.macosx.QMAKE_AR = \ 10 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar \ 11 | cq 12 | QMAKE_MAC_SDK.macx-clang.macosx.QMAKE_RANLIB = \ 13 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib \ 14 | -s 15 | QMAKE_MAC_SDK.macx-clang.macosx.QMAKE_LINK = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ 16 | QMAKE_MAC_SDK.macx-clang.macosx.QMAKE_LINK_SHLIB = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ 17 | QMAKE_MAC_SDK.macosx.platform_name = macosx 18 | QMAKE_MAC_SDK.macosx.platform_path = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform 19 | QMAKE_MAC_SDK.macosx.version = 10.11 20 | QMAKE_MAC_SDK.macosx10.11.path = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk 21 | QMAKE_MAC_SDK.macosx10.11.platform_path = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform 22 | QMAKE_MAC_SDK.macosx10.11.version = 10.11 23 | QMAKE_MAC_SDK.macx-clang.macosx10.11.QMAKE_CC = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang 24 | QMAKE_MAC_SDK.macx-clang.macosx10.11.QMAKE_CXX = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ 25 | QMAKE_MAC_SDK.macx-clang.macosx10.11.QMAKE_FIX_RPATH = \ 26 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/install_name_tool \ 27 | -id 28 | QMAKE_MAC_SDK.macx-clang.macosx10.11.QMAKE_AR = \ 29 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar \ 30 | cq 31 | QMAKE_MAC_SDK.macx-clang.macosx10.11.QMAKE_RANLIB = \ 32 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib \ 33 | -s 34 | QMAKE_MAC_SDK.macx-clang.macosx10.11.QMAKE_LINK = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ 35 | QMAKE_MAC_SDK.macx-clang.macosx10.11.QMAKE_LINK_SHLIB = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ 36 | QMAKE_MAC_SDK.macosx10.11.platform_name = macosx 37 | QMAKE_DEFAULT_INCDIRS = \ 38 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1 \ 39 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.0.0/include \ 40 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include \ 41 | /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include \ 42 | "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks (framework directory)" 43 | QMAKE_DEFAULT_LIBDIRS = \ 44 | /lib \ 45 | /usr/lib 46 | QMAKE_MAC_SDK.macosx.Path = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk 47 | QMAKE_MAC_SDK.macosx.PlatformPath = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform 48 | QMAKE_MAC_SDK.macosx.SDKVersion = 10.12 49 | -------------------------------------------------------------------------------- /Lecture3/RibExporter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | 4 | project(RibExport) 5 | # include the project setting file 6 | include(${DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 7 | 8 | # specify project name 9 | set(PROJECT_NAME RibExport) 10 | include_directories(include) 11 | # set SOURCE_FILES 12 | set(SOURCE_FILES src/Plugin.cpp src/RibExport.cpp include/RibExport.h) 13 | 14 | # set linking libraries 15 | set(LIBRARIES OpenMaya OpenMayaAnim OpenMayaRender Foundation) 16 | # Build plugin 17 | build_plugin() 18 | # now copy the plugin to the local plugins folder set by PLUGINS_FOLDER 19 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy $ ${PLUGINS_FOLDER}/${OutputName} 21 | ) 22 | -------------------------------------------------------------------------------- /Lecture3/RibExporter/RibExportScript.mel: -------------------------------------------------------------------------------- 1 | 2 | 3 | global proc int RibExportScript ( string $parent, 4 | string $action, 5 | string $initialSettings, 6 | string $resultCallback 7 | ) 8 | // 9 | // Description: 10 | // This script posts the file translator options. 11 | // The optionsString is of the form: 12 | // varName1=value1;varName2=value2;... 13 | // 14 | // Parameters: 15 | // $parent - the elf parent layout for this options layout. It is 16 | // always a scrollLayout. 17 | // $action - the action that is to be performed with this invokation 18 | // of this proc. Valid options are: 19 | // "query" - construct the options string and pass it 20 | // to the resultCallback. 21 | // "post" - post all the elf controls. 22 | // $initialSettings - the current options string in effect at the 23 | // time this script is invoked. 24 | // $resultCallback - 25 | // This is the proc to be called with the result string. 26 | // resultCallback ( string $optionsString ) 27 | // 28 | // Returns: 29 | // 1 if successfull. 30 | // 0 otherwise. 31 | // 32 | 33 | { 34 | int $result; 35 | string $currentOptions; 36 | string $optionList[]; 37 | string $optionBreakDown[]; 38 | 39 | 40 | if ($action == "post") 41 | { 42 | setParent $parent; 43 | // options are "-normals=1;-uvs=1;-geo=1;-lights=1"; 44 | columnLayout -adj true objTypeCol; 45 | radioButtonGrp 46 | -l "normals" 47 | -nrb 2 -cw3 175 75 75 48 | -sl 1 49 | -la2 "On" "Off" rglNormals 50 | ; 51 | radioButtonGrp 52 | -l "uvs" 53 | -nrb 2 -cw3 175 75 75 54 | -sl 1 55 | -la2 "On" "Off" rglUVs 56 | ; 57 | radioButtonGrp 58 | -l "geo" 59 | -nrb 2 -cw3 175 75 75 60 | -sl 1 61 | -la2 "On" "Off" rglGeo 62 | ; 63 | radioButtonGrp 64 | -l "lights" 65 | -nrb 2 -cw3 175 75 75 66 | -sl 1 67 | -la2 "On" "Off" rglLights 68 | ; 69 | // Now to set the current settings. 70 | // 71 | $currentOptions = $initialSettings; 72 | if (size($currentOptions) > 0) 73 | { 74 | tokenize($currentOptions, ";", $optionList); 75 | for ($index = 0; $index < size($optionList); $index++) 76 | { 77 | if ($optionBreakDown[0] == "-normals") 78 | { 79 | if ($optionBreakDown[1] == "0") 80 | { 81 | radioButtonGrp -e -sl 2 rglNormals; 82 | } 83 | else 84 | { 85 | radioButtonGrp -e -sl 1 rglNormals; 86 | } 87 | } 88 | if ($optionBreakDown[0] == "-uvs") 89 | { 90 | if ($optionBreakDown[1] == "0") 91 | { 92 | radioButtonGrp -e -sl 2 rglUVs; 93 | } 94 | else 95 | { 96 | radioButtonGrp -e -sl 1 rglUVs; 97 | } 98 | } 99 | if ($optionBreakDown[0] == "-geo") 100 | { 101 | if ($optionBreakDown[1] == "0") 102 | { 103 | radioButtonGrp -e -sl 2 rglGeo; 104 | } 105 | else 106 | { 107 | radioButtonGrp -e -sl 1 rglGeo; 108 | } 109 | } 110 | if ($optionBreakDown[0] == "-lights") 111 | { 112 | if ($optionBreakDown[1] == "0") 113 | { 114 | radioButtonGrp -e -sl 2 rglLights; 115 | } 116 | else 117 | { 118 | radioButtonGrp -e -sl 1 rglLights; 119 | } 120 | } 121 | } 122 | } 123 | 124 | 125 | $result = 1; 126 | } 127 | else if ($action == "query") 128 | { 129 | if (`radioButtonGrp -q -sl rglNormals` == 1) 130 | { 131 | $currentOptions = $currentOptions + "-normals=1;"; 132 | } 133 | else 134 | { 135 | $currentOptions = $currentOptions + "-normals=0;"; 136 | } 137 | if (`radioButtonGrp -q -sl rglUVs` == 1) 138 | { 139 | $currentOptions = $currentOptions + "-uvs=1;"; 140 | } 141 | else 142 | { 143 | $currentOptions = $currentOptions + "-uvs=0;"; 144 | } 145 | if (`radioButtonGrp -q -sl rglGeo` == 1) 146 | { 147 | $currentOptions = $currentOptions + "-geo=1;"; 148 | } 149 | else 150 | { 151 | $currentOptions = $currentOptions + "-geo=0;"; 152 | } 153 | if (`radioButtonGrp -q -sl rglLights` == 1) 154 | { 155 | $currentOptions = $currentOptions + "-lights=1;"; 156 | } 157 | else 158 | { 159 | $currentOptions = $currentOptions + "-lights=0;"; 160 | } 161 | eval($resultCallback+" \""+$currentOptions+"\""); 162 | print $resultCallback; 163 | print "\n"; 164 | print $currentOptions; 165 | $result = 1; 166 | } 167 | else 168 | { 169 | $result = 0; 170 | } 171 | 172 | return $result; 173 | } 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /Lecture3/RibExporter/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "RibExport.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | //------------------------------------------------------------------- 9 | 10 | /// specifies a script to use for the user interface options box 11 | const char *g_OptionScript = "RibExportScript"; 12 | 13 | /// a set of default options for the exporter 14 | const char *g_DefaultOptions = ""; 15 | 16 | 17 | //------------------------------------------------------------------- initializePlugin 18 | /// \brief initializePlugin( MObject obj ) 19 | /// \param obj - the plugin handle 20 | /// \return MS::kSuccess if ok 21 | /// \note Registers all of the new commands, file translators and new 22 | /// node types. 23 | /// 24 | MStatus initializePlugin( MObject obj ) 25 | { 26 | MStatus status; 27 | 28 | MFnPlugin plugin( obj, "Jon Macey", "1.0", "Any"); 29 | 30 | // Register the translator with the system 31 | status = plugin.registerFileTranslator( "RibExport", "none", 32 | RibExport::creator, 33 | (char*)g_OptionScript, 34 | (char*)g_DefaultOptions 35 | ); 36 | 37 | if (status != MS::kSuccess) { 38 | status.perror("MayaExportCommand::registerFileTranslator"); 39 | } 40 | return status; 41 | 42 | } 43 | 44 | 45 | //------------------------------------------------------------------- uninitializePlugin 46 | /// \brief uninitializePlugin( MObject obj ) 47 | /// \param obj - the plugin handle to un-register 48 | /// \return MS::kSuccess if ok 49 | /// \note un-registers the plugin and destroys itself 50 | /// 51 | MStatus uninitializePlugin( MObject obj ) 52 | { 53 | MFnPlugin plugin( obj ); 54 | MStatus status = plugin.deregisterFileTranslator( "RibExport" ); 55 | if (status != MS::kSuccess) { 56 | status.perror("MayaExportCommand::deregisterFileTranslator"); 57 | } 58 | 59 | return status; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /PythonPlugins/CustomSphere.py: -------------------------------------------------------------------------------- 1 | import maya.api.OpenMaya as OpenMaya 2 | import maya.cmds as cmds 3 | import random 4 | 5 | ''' 6 | def maya_useNewAPI(): 7 | """ 8 | Can either use this function (which works on earlier versions) 9 | or we can set maya_useNewAPI = True 10 | """ 11 | pass 12 | ''' 13 | 14 | maya_useNewAPI = True 15 | 16 | 17 | class CustomSphere(OpenMaya.MPxCommand): 18 | 19 | CMD_NAME = "CustomSpherePy" 20 | 21 | def __init__(self): 22 | super(CustomSphere, self).__init__() 23 | 24 | def doIt(self, args): 25 | """ 26 | Called when the command is executed in script 27 | args is an MArgList object 28 | """ 29 | if len(args) == 0: 30 | OpenMaya.MGlobal.displayError("No radius specified") 31 | return False 32 | try: 33 | self.count = args.asInt(0) 34 | except: 35 | OpenMaya.MGlobal.displayError("Invalid radius") 36 | return False 37 | if self.count < 0: 38 | OpenMaya.MGlobal.displayError("argument must be greater than zero") 39 | return False 40 | return self.redoIt() 41 | 42 | def redoIt(self): 43 | """ 44 | Called when the command is executed in script 45 | """ 46 | for i in range(0, self.count): 47 | x = random.uniform(-10, 10) 48 | y = random.uniform(-10, 10) 49 | z = random.uniform(-10, 10) 50 | radius = random.uniform(0.8, 4.5) 51 | cmds.sphere(r=radius, n=f"sphere{i}") 52 | cmds.move(x, y, z) 53 | return True 54 | 55 | def undoIt(self): 56 | """ 57 | Called when the command is undone 58 | """ 59 | for i in range(0, self.count): 60 | cmds.delete(f"sphere{i}") 61 | 62 | def isUndoable(self): 63 | return True 64 | 65 | @classmethod 66 | def creator(cls): 67 | """ 68 | Think of this as a factory 69 | """ 70 | return CustomSphere() 71 | 72 | 73 | def initializePlugin(plugin): 74 | """ 75 | Load our plugin 76 | """ 77 | vendor = "NCCA" 78 | version = "1.0.0" 79 | 80 | plugin_fn = OpenMaya.MFnPlugin(plugin, vendor, version) 81 | 82 | try: 83 | plugin_fn.registerCommand(CustomSphere.CMD_NAME, CustomSphere.creator) 84 | except: 85 | OpenMaya.MGlobal.displayError( 86 | "Failed to register command: {0}".format(CustomSphere.CMD_NAME) 87 | ) 88 | 89 | 90 | def uninitializePlugin(plugin): 91 | """ 92 | Exit point for a plugin 93 | """ 94 | plugin_fn = OpenMaya.MFnPlugin(plugin) 95 | try: 96 | plugin_fn.deregisterCommand(CustomSphere.CMD_NAME) 97 | except: 98 | OpenMaya.MGlobal.displayError( 99 | "Failed to deregister command: {0}".format(CustomSphere.CMD_NAME) 100 | ) 101 | 102 | 103 | if __name__ == "__main__": 104 | """ 105 | So if we execute this in the script editor it will be a __main__ so we can put testing code etc here 106 | Loading the plugin will not run this 107 | As we are loading the plugin it needs to be in the plugin path. 108 | """ 109 | 110 | plugin_name = "CustomSphere.py" 111 | 112 | cmds.evalDeferred( 113 | 'if cmds.pluginInfo("f{plugin_name}", q=True, loaded=True): cmds.unloadPlugin(f"{plugin_name}")' 114 | ) 115 | 116 | cmds.evalDeferred( 117 | 'if not cmds.pluginInfo(f"{plugin_name}", q=True, loaded=True): cmds.loadPlugin(f"{plugin_name}")' 118 | ) 119 | 120 | cmds.CustromSpherePy(100) 121 | -------------------------------------------------------------------------------- /PythonPlugins/CustomSphereSolution.py: -------------------------------------------------------------------------------- 1 | import maya.api.OpenMaya as OpenMaya 2 | import maya.cmds as cmds 3 | import random 4 | 5 | 6 | maya_useNewAPI = True 7 | 8 | 9 | class CustomSphereSolution(OpenMaya.MPxCommand): 10 | 11 | CMD_NAME = "CustomSphereSolutionPy" 12 | 13 | def __init__(self): 14 | super(CustomSphereSolution, self).__init__() 15 | self.count=100 16 | self.xExtent=20 17 | self.yExtent=20 18 | self.zExtent=20 19 | self.minRadius=0.2 20 | self.maxRadius=2.5 21 | 22 | 23 | def postConstructor(self): 24 | """ 25 | This method is called after the object has been constructed 26 | """ 27 | self.count=100 28 | self.xExtent=20 29 | self.yExtent=20 30 | self.zExtent=20 31 | self.minRadius=0.2 32 | self.maxRadius=2.5 33 | 34 | 35 | @staticmethod 36 | def newSyntax() : 37 | """ 38 | Create a new syntax object registered as part of the 39 | 40 | """ 41 | syntax = OpenMaya.MSyntax() 42 | syntax.addFlag("-h", "-help") 43 | syntax.addFlag("-x","-xExtents", OpenMaya.MSyntax.kDouble) 44 | syntax.addFlag("-y","-yExtents", OpenMaya.MSyntax.kDouble) 45 | syntax.addFlag("-z","-zExtents", OpenMaya.MSyntax.kDouble) 46 | syntax.addFlag("-n","-number", OpenMaya.MSyntax.kUnsigned) 47 | syntax.addFlag("-mr","-minRadius", OpenMaya.MSyntax.kDouble) 48 | syntax.addFlag("-mm","-maxRadius", OpenMaya.MSyntax.kDouble) 49 | return syntax 50 | 51 | def doIt(self, args): 52 | """ 53 | Called when the command is executed in script 54 | :param args: 55 | :return: 56 | """ 57 | 58 | # now create a parser object 59 | parser=OpenMaya.MArgParser(self.syntax(),args) 60 | if parser.isFlagSet("-h"): 61 | OpenMaya.MGlobal.displayInfo("This command creates a number of spheres in the scene") 62 | OpenMaya.MGlobal.displayInfo("Flags:") 63 | OpenMaya.MGlobal.displayInfo("-x -xExtents: The x extents of the sphere") 64 | OpenMaya.MGlobal.displayInfo("-y -yExtents: The y extents of the sphere") 65 | OpenMaya.MGlobal.displayInfo("-z -zExtents: The z extents of the sphere") 66 | OpenMaya.MGlobal.displayInfo("-n -number: The number of spheres to create") 67 | OpenMaya.MGlobal.displayInfo("-mr -minRadius: The minimum radius of the sphere") 68 | OpenMaya.MGlobal.displayInfo("-mm -maxRadius: The maximum radius of the sphere") 69 | return True 70 | if parser.isFlagSet("-n"): 71 | self.count=parser.flagArgumentInt("-n",0) 72 | if parser.isFlagSet("-x"): 73 | self.xExtent=parser.flagArgumentDouble("-x",0) 74 | if parser.isFlagSet("-y"): 75 | self.yExtent=parser.flagArgumentDouble("-y",0) 76 | if parser.isFlagSet("-z"): 77 | self.zExtent=parser.flagArgumentDouble("-z",0) 78 | if parser.isFlagSet("-mr"): 79 | self.minRadius=parser.flagArgumentDouble("-mr",0) 80 | if parser.isFlagSet("-mm"): 81 | self.maxRadius=parser.flagArgumentDouble("-mm",0) 82 | return self.redoIt() 83 | 84 | def redoIt(self): 85 | """ 86 | Called when the command is executed in script 87 | """ 88 | for i in range(0, self.count): 89 | x = random.uniform(-self.xExtent, self.xExtent) 90 | y = random.uniform(-self.yExtent, self.yExtent) 91 | z = random.uniform(-self.zExtent, self.zExtent) 92 | radius = random.uniform(self.minRadius, self.maxRadius) 93 | cmds.sphere(r=radius, n=f"sphere{i}") 94 | cmds.move(x, y, z) 95 | return True 96 | 97 | def undoIt(self): 98 | """ 99 | Called when the command is undone 100 | """ 101 | 102 | for i in range(0, self.count): 103 | cmds.delete(f"sphere{i}") 104 | 105 | def isUndoable(self): 106 | return True 107 | 108 | @classmethod 109 | def creator(cls): 110 | """ 111 | Think of this as a factory 112 | """ 113 | return CustomSphereSolution() 114 | 115 | 116 | def initializePlugin(plugin): 117 | """ 118 | Load our plugin 119 | """ 120 | vendor = "NCCA" 121 | version = "1.0.0" 122 | 123 | plugin_fn = OpenMaya.MFnPlugin(plugin, vendor, version, "Any") 124 | 125 | try: 126 | plugin_fn.registerCommand(CustomSphereSolution.CMD_NAME, CustomSphereSolution.creator, CustomSphereSolution.newSyntax) 127 | except: 128 | OpenMaya.MGlobal.displayError(f"Failed to register command: {CustomSphereSolution.CMD_NAME}") 129 | 130 | 131 | def uninitializePlugin(plugin): 132 | """ 133 | Exit point for a plugin 134 | """ 135 | plugin_fn = OpenMaya.MFnPlugin(plugin) 136 | try: 137 | plugin_fn.deregisterCommand(CustomSphereSolution.CMD_NAME) 138 | except: 139 | OpenMaya.MGlobal.displayError(f"Failed to deregister command: {CustomSphereSolution.CMD_NAME}") 140 | 141 | -------------------------------------------------------------------------------- /PythonPlugins/HelloMaya.py: -------------------------------------------------------------------------------- 1 | import maya.api.OpenMaya as om 2 | import maya.cmds as cmds 3 | 4 | ''' 5 | def maya_useNewAPI(): 6 | """ 7 | Can either use this function (which works on earlier versions) 8 | or we can set maya_useNewAPI = True 9 | """ 10 | pass 11 | ''' 12 | 13 | maya_useNewAPI = True 14 | 15 | 16 | class HelloMaya(om.MPxCommand): 17 | 18 | CMD_NAME = "HelloMayaPy" 19 | 20 | def __init__(self): 21 | super(HelloMaya, self).__init__() 22 | 23 | def doIt(self, args): 24 | """ 25 | Called when the command is executed in script 26 | """ 27 | print("This should come from the python shell") 28 | om.MGlobal.displayWarning("This should be a warning") 29 | om.MGlobal.displayError("This should be an error") 30 | om.MGlobal.displayInfo("This should be an info message") 31 | 32 | @classmethod 33 | def creator(cls): 34 | """ 35 | Think of this as a factory 36 | """ 37 | return HelloMaya() 38 | 39 | 40 | def initializePlugin(plugin): 41 | """ 42 | Load our plugin 43 | """ 44 | vendor = "NCCA" 45 | version = "1.0.0" 46 | 47 | plugin_fn = om.MFnPlugin(plugin, vendor, version) 48 | 49 | try: 50 | plugin_fn.registerCommand(HelloMaya.CMD_NAME, HelloMaya.creator) 51 | except: 52 | om.MGlobal.displayError( 53 | "Failed to register command: {0}".format(HelloMaya.CMD_NAME) 54 | ) 55 | 56 | 57 | def uninitializePlugin(plugin): 58 | """ 59 | Exit point for a plugin 60 | """ 61 | plugin_fn = om.MFnPlugin(plugin) 62 | try: 63 | plugin_fn.deregisterCommand(HelloMaya.CMD_NAME) 64 | except: 65 | om.MGlobal.displayError( 66 | "Failed to deregister command: {0}".format(HelloMaya.CMD_NAME) 67 | ) 68 | 69 | 70 | if __name__ == "__main__": 71 | """ 72 | So if we execute this in the script editor it will be a __main__ so we can put testing code etc here 73 | Loading the plugin will not run this 74 | As we are loading the plugin it needs to be in the plugin path. 75 | """ 76 | 77 | plugin_name = "HelloMaya.py" 78 | 79 | cmds.evalDeferred( 80 | 'if cmds.pluginInfo("{0}", q=True, loaded=True): cmds.unloadPlugin("{0}")'.format( 81 | plugin_name 82 | ) 83 | ) 84 | cmds.evalDeferred( 85 | 'if not cmds.pluginInfo("{0}", q=True, loaded=True): cmds.loadPlugin("{0}")'.format( 86 | plugin_name 87 | ) 88 | ) 89 | -------------------------------------------------------------------------------- /PythonPlugins/IPNodeDemo.py: -------------------------------------------------------------------------------- 1 | index = 0 2 | y = 0.0 3 | for z in range(-5, 5): 4 | for x in range(-5, 5): 5 | cmds.polyCube(name=f"cube{index}", w=0.4, h=0.4, d=0.4) 6 | cmds.move(x, y, z, f"cube{index}") 7 | index += 1 8 | 9 | cmds.createNode("InputNodePy", name="ipNode1") 10 | cmds.setAttr("ipNode1.dataSize", 100) 11 | cmds.connectAttr("time1.outTime", "ipNode1.time") 12 | 13 | for i in range(0, 100): 14 | cmds.connectAttr(f"ipNode1.output[{i}]", f"cube{i}.ty") 15 | -------------------------------------------------------------------------------- /PythonPlugins/InputNode.py: -------------------------------------------------------------------------------- 1 | import maya.api.OpenMaya as OpenMaya 2 | import sys 3 | import math 4 | import random 5 | 6 | # Set this flag to show we are using api 2.0 7 | maya_useNewAPI = True 8 | 9 | 10 | class InputNode(OpenMaya.MPxNode): 11 | # class attributes are placed here maya will use these to create the node 12 | # and to determine the type of node which is setup during initialize method 13 | # Older plugins used to set these to OpenMaya.MObject() (see api1 demos) 14 | # however we now just set to None and use the FunctionSets to create the attributes 15 | id = OpenMaya.MTypeId(0x00105489) 16 | output = None 17 | time = None 18 | data_size = None 19 | 20 | def __init__(self): 21 | OpenMaya.MPxNode.__init__(self) 22 | 23 | # factory to create the node 24 | @staticmethod 25 | def creator(): 26 | return InputNode() 27 | 28 | @staticmethod 29 | def initialize(): 30 | # Create the attributes 31 | numeric_attrib_fn = OpenMaya.MFnNumericAttribute() 32 | # add enum attribute 33 | 34 | # Time attribute 35 | time_attribute_fn = OpenMaya.MFnUnitAttribute() 36 | InputNode.time = time_attribute_fn.create( 37 | "time", "t", OpenMaya.MFnUnitAttribute.kTime, 0.0 38 | ) 39 | time_attribute_fn.storable = False 40 | time_attribute_fn.keyable = False 41 | OpenMaya.MPxNode.addAttribute(InputNode.time) 42 | 43 | # Data size attribute 44 | InputNode.data_size = numeric_attrib_fn.create( 45 | "dataSize", "ds", OpenMaya.MFnNumericData.kInt, 10 46 | ) 47 | numeric_attrib_fn.storable = True 48 | numeric_attrib_fn.keyable = True 49 | numeric_attrib_fn.readable = True 50 | numeric_attrib_fn.writable = True 51 | OpenMaya.MPxNode.addAttribute(InputNode.data_size) 52 | 53 | # output set by the compute 54 | 55 | array_data_fn = OpenMaya.MFnNumericAttribute() 56 | InputNode.output = array_data_fn.create( 57 | "output", "o", OpenMaya.MFnNumericData.kFloat, 0.0 58 | ) 59 | array_data_fn.array = True 60 | array_data_fn.usesArrayDataBuilder = True 61 | 62 | array_data_fn.storable = False 63 | array_data_fn.keyable = False 64 | array_data_fn.readable = True 65 | array_data_fn.writable = False 66 | OpenMaya.MPxNode.addAttribute(InputNode.output) 67 | 68 | # Set the attribute dependencies 69 | OpenMaya.MPxNode.attributeAffects(InputNode.time, InputNode.output) 70 | OpenMaya.MPxNode.attributeAffects(InputNode.data_size, InputNode.output) 71 | return True 72 | 73 | def compute(self, plug, data): 74 | """ 75 | Description 76 | 77 | When input attributes are dirty this method will be called to 78 | recompute the output attributes. 79 | 80 | Arguments 81 | 82 | plug - the attribute that triggered the compute 83 | data - the nodes data 84 | 85 | we only need to compute if the plug is the output node changing 86 | """ 87 | if plug == InputNode.output: 88 | # get the input data and convert to python types 89 | time_data = data.inputValue(InputNode.time) 90 | time = time_data.asTime() 91 | array_data_handle = data.outputArrayValue(InputNode.output) 92 | data_size = data.inputValue(InputNode.data_size).asInt() 93 | 94 | data_block = self.forceCache() 95 | array_builder = OpenMaya.MArrayDataBuilder(data_block, self.output, data_size) 96 | 97 | output_array_builder = array_data_handle.builder() 98 | OpenMaya.MGlobal.displayInfo("Data size: " + str(data_size)) 99 | for i in range(0, data_size): 100 | child = array_builder.addLast() 101 | child.setFloat(random.uniform(0, 10)) 102 | array_data_handle.set(array_builder) 103 | array_data_handle.setAllClean() 104 | data.setClean(plug) 105 | 106 | return True 107 | return False 108 | 109 | def postConstructor(self): 110 | """ 111 | Description 112 | 113 | When instances of this node are created internally, the MObject associated 114 | with the instance is not created until after the constructor of this class 115 | is called. This means that no member functions of Node can 116 | be called in the constructor. 117 | The postConstructor solves this problem. Maya will call this function 118 | after the internal object has been created. 119 | As a general rule do all of your initialization in the postConstructor. 120 | Not used here but for info 121 | """ 122 | print("postConstructor called") 123 | 124 | 125 | def initializePlugin(obj): 126 | plugin = OpenMaya.MFnPlugin(obj) 127 | try: 128 | plugin.registerNode( 129 | "InputNodePy", InputNode.id, InputNode.creator, InputNode.initialize 130 | ) 131 | except: 132 | sys.stderr.write("Failed to register node\n") 133 | raise 134 | 135 | 136 | def uninitializePlugin(obj): 137 | plugin = OpenMaya.MFnPlugin(obj) 138 | 139 | try: 140 | plugin.deregisterNode(InputNode.id) 141 | except: 142 | sys.stderr.write("Failed to deregister node\n") 143 | raise 144 | -------------------------------------------------------------------------------- /PythonPlugins/QtUI/QtUIMaya.mod: -------------------------------------------------------------------------------- 1 | + QtUiMaya 1.0 /home/jmacey/MayaAPICode/PythonPlugins/QtUI 2 | MAYA_PLUG_IN_PATH +:= plug-ins 3 | 4 | QTUIPLUGIN_ROOT=/home/jmacey/MayaAPICode/PythonPlugins/QtUI 5 | -------------------------------------------------------------------------------- /PythonPlugins/QtUI/README.md: -------------------------------------------------------------------------------- 1 | # Module Example 2 | 3 | This demo demonstrates a lot of principles when developing with maya. In particular this is designed to be a module and we need to setup this to be installed in the correct location. 4 | 5 | 6 | ## MacOS 7 | we copy the mod file into this directory 8 | 9 | ~/Library/Preferences/Autodesk/maya/modules 10 | 11 | -------------------------------------------------------------------------------- /PythonPlugins/QtUI/installModule.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import platform 4 | import sys 5 | from pathlib import Path 6 | 7 | maya_locations = { 8 | "Linux": "/maya", 9 | "Darwin": "/Library/Preferences/Autodesk/maya", 10 | "Windows": "\\Documents\\maya\\version", 11 | } 12 | 13 | 14 | def install_module(location): 15 | print(f"installing to {location}") 16 | # first write the module file 17 | current_dir = Path.cwd() 18 | if not Path(location + "modules/QtUIMaya.mod").is_file(): 19 | print("writing module file") 20 | with open(location + "modules/QtUIMaya.mod", "w") as file: 21 | file.write(f"+ QtUiMaya 1.0 {current_dir}\n") 22 | file.write("MAYA_PLUG_IN_PATH +:= plug-ins\n") 23 | file.write(f"QTUIPLUGIN_ROOT={current_dir}\n\n") 24 | # if we are using Maya 2022 and above this will be ok 25 | # however if using 2020 and below we need to set Maya.env 26 | folders = os.listdir(location) 27 | for folder in folders: 28 | # nobody use maya begining with 19 do they? :-) 29 | if folder.startswith("20") and int(folder) < 2022: 30 | with open(f"{location}/{folder}/Maya.env", "a+") as env_file: 31 | if "QTUIPLUGIN_ROOT" not in env_file.read(): 32 | env_file.write(f"QTUIPLUGIN_ROOT={current_dir}\n\n") 33 | 34 | 35 | def check_maya_installed(op_sys): 36 | mloc = f"{Path.home()}{maya_locations.get(op_sys)}/" 37 | if not os.path.isdir(mloc): 38 | raise 39 | return mloc 40 | 41 | 42 | if __name__ == "__main__": 43 | op_sys = platform.system() 44 | try: 45 | m_loc = check_maya_installed(op_sys) 46 | except: 47 | print("Error can't find maya install") 48 | sys.exit(os.EX_CONFIG) 49 | 50 | install_module(m_loc) 51 | -------------------------------------------------------------------------------- /PythonPlugins/QtUI/plug-ins/QtUIMaya.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import maya.api.OpenMaya as OpenMaya 5 | import maya.api.OpenMayaUI as OpenMayaUI 6 | import maya.cmds as cmds 7 | import maya.OpenMayaMPx as OpenMayaMPx 8 | import maya.OpenMayaUI as omui 9 | from PySide2 import QtCore, QtWidgets 10 | from PySide2.QtCore import QFile 11 | from PySide2.QtGui import QColor, QFont 12 | from PySide2.QtUiTools import QUiLoader 13 | from shiboken2 import wrapInstance 14 | 15 | 16 | def maya_useNewAPI(): 17 | """ 18 | Can either use this function (which works on earlier versions) 19 | or we can set maya_useNewAPI = True 20 | """ 21 | pass 22 | 23 | 24 | maya_useNewAPI = True 25 | 26 | 27 | def get_main_window(): 28 | """this returns the maya main window for parenting""" 29 | window = omui.MQtUtil.mainWindow() 30 | return wrapInstance(int(window), QtWidgets.QWidget) 31 | 32 | 33 | class SimpleDialog(QtWidgets.QWidget): 34 | def __init__(self, parent=get_main_window()): 35 | """init the class and setup dialog""" 36 | # Python 3 does inheritance differently to 2 so support both 37 | # as Maya 2020 is still Python 2 38 | if sys.version_info.major == 3: 39 | super().__init__(parent) 40 | # python 2 41 | else: 42 | super(SimpleDialog, self).__init__(parent) 43 | 44 | app_root = os.environ.get("QTUIPLUGIN_ROOT") 45 | # uic.loadUi(app_root + "ui/form.ui", self) # Load the .ui file 46 | loader = QUiLoader() 47 | file = QFile(app_root + "/ui/form.ui") 48 | print(app_root + "/ui/form.ui") 49 | file.open(QFile.ReadOnly) 50 | self.ui = loader.load(file, parentWidget=self) 51 | file.close() 52 | # This should make the window stay on top 53 | self.ui.setWindowFlags(QtCore.Qt.Tool) 54 | 55 | self.ui.show() 56 | self.ui.show_colour.clicked.connect(self.set_colour) 57 | self.ui.confirm_button.clicked.connect(self.confirm) 58 | self.ui.progress_window.clicked.connect(self.do_progress) 59 | self.ui.prompt_dialog.clicked.connect(self.prompt_dialog) 60 | self.ui.select_font.clicked.connect(self.select_font) 61 | 62 | def set_colour(self): 63 | cmds.colorEditor() 64 | if cmds.colorEditor(query=True, result=True): 65 | values = cmds.colorEditor(query=True, rgb=True) 66 | print(values) 67 | colour = QColor() 68 | colour.setRedF(values[0]) 69 | colour.setGreenF(values[1]) 70 | colour.setBlueF(values[2]) 71 | self.ui.colour_label.setStyleSheet( 72 | "QLabel {{ background-color : rgb({},{},{} ) ; border :5px; }}".format( 73 | colour.red(), colour.green(), colour.blue() 74 | ) 75 | ) 76 | 77 | def confirm(self): 78 | result = cmds.confirmDialog( 79 | title="Confirm", 80 | message="Are you sure?", 81 | button=["Yes", "No"], 82 | defaultButton="Yes", 83 | cancelButton="No", 84 | dismissString="No", 85 | ) 86 | self.ui.confirm_result.setText(result) 87 | 88 | def do_progress(self): 89 | amount = 0 90 | cmds.progressWindow( 91 | title="Doing Nothing", 92 | progress=amount, 93 | status="Sleeping: 0%", 94 | isInterruptable=True, 95 | ) 96 | while True: 97 | # Check if the dialog has been cancelled 98 | if cmds.progressWindow(query=True, isCancelled=True): 99 | break 100 | 101 | # Check if end condition has been reached 102 | if cmds.progressWindow(query=True, progress=True) >= 100: 103 | break 104 | amount += 25 105 | # can use f string in new maya 106 | message = "Sleeping {} %".format(amount) 107 | cmds.progressWindow(edit=True, progress=amount, status=message) 108 | cmds.pause(seconds=1) 109 | cmds.progressWindow(endProgress=1) 110 | 111 | def prompt_dialog(self): 112 | result = cmds.promptDialog( 113 | title="Prompt dialog", 114 | message="Enter some Text:", 115 | button=["OK", "Cancel"], 116 | defaultButton="OK", 117 | cancelButton="Cancel", 118 | dismissString="Cancel", 119 | ) 120 | 121 | if result == "OK": 122 | text = cmds.promptDialog(query=True, text=True) 123 | self.ui.prompt_text.setText(text) 124 | 125 | def select_font(self): 126 | font = cmds.fontDialog() 127 | # font elements are seperated by | 128 | values = font.split("|") 129 | # first value is the font name 130 | print(values) 131 | # sz is value[2] need to split 132 | size = values[2].split(":") 133 | font = QFont(values[0], int(size[1])) 134 | self.ui.font_text.setFont(font) 135 | 136 | 137 | class QtUIMaya(OpenMaya.MPxCommand): 138 | 139 | CMD_NAME = "QtUIMaya" 140 | ui = None 141 | 142 | def __init__(self): 143 | super(QtUIMaya, self).__init__() 144 | ui = None 145 | 146 | @classmethod 147 | def doIt(cls, args): 148 | """ 149 | Called when the command is executed in script 150 | """ 151 | if QtUIMaya.ui is None: 152 | QtUIMaya.ui = SimpleDialog() 153 | QtUIMaya.ui.showNormal() 154 | else: 155 | QtUIMaya.ui.showNormal() 156 | 157 | @classmethod 158 | def creator(cls): 159 | """ 160 | Think of this as a factory 161 | """ 162 | return QtUIMaya() 163 | 164 | @classmethod 165 | def cleanup(cls): 166 | # cleanup the UI and call the destructors 167 | QtUIMaya.ui.deleteLater() 168 | 169 | 170 | def initializePlugin(plugin): 171 | """ 172 | Load our plugin 173 | """ 174 | vendor = "NCCA" 175 | version = "1.0.0" 176 | if os.environ.get("QTUIPLUGIN_ROOT") is None: 177 | OpenMaya.MGlobal.displayError( 178 | "Module Environment not set ensure QTUIPLUGIN_ROOT is set in module file" 179 | ) 180 | # throw exception and let maya deal with it 181 | raise 182 | 183 | plugin_fn = OpenMaya.MFnPlugin(plugin, vendor, version) 184 | try: 185 | plugin_fn.registerCommand(QtUIMaya.CMD_NAME, QtUIMaya.creator) 186 | cmds.evalDeferred("cmds.QtUIMaya()") 187 | except: 188 | OpenMaya.MGlobal.displayError( 189 | "Failed to register command: {0}".format(QtUIMaya.CMD_NAME) 190 | ) 191 | 192 | 193 | def uninitializePlugin(plugin): 194 | """ 195 | Exit point for a plugin 196 | """ 197 | # cleanup the dialog first 198 | QtUIMaya.cleanup() 199 | plugin_fn = OpenMaya.MFnPlugin(plugin) 200 | try: 201 | plugin_fn.deregisterCommand(QtUIMaya.CMD_NAME) 202 | except: 203 | OpenMaya.MGlobal.displayError( 204 | "Failed to deregister command: {0}".format(QtUIMaya.CMD_NAME) 205 | ) 206 | 207 | 208 | if __name__ == "__main__": 209 | """ 210 | So if we execute this in the script editor it will be a __main__ so we can put testing code etc here 211 | Loading the plugin will not run this 212 | As we are loading the plugin it needs to be in the plugin path. 213 | """ 214 | 215 | plugin_name = "QtUIMaya.py" 216 | 217 | cmds.evalDeferred( 218 | 'if cmds.pluginInfo("{0}", q=True, loaded=True): cmds.unloadPlugin("{0}")'.format( 219 | plugin_name 220 | ) 221 | ) 222 | cmds.evalDeferred( 223 | 'if not cmds.pluginInfo("{0}", q=True, loaded=True): cmds.loadPlugin("{0}")'.format( 224 | plugin_name 225 | ) 226 | ) 227 | -------------------------------------------------------------------------------- /PythonPlugins/QtUI/ui/form.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 242 10 | 400 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 19 | 20 | 21 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 22 | 23 | 24 | 25 | 26 | Select Colour 27 | 28 | 29 | 30 | 31 | 32 | 33 | Colour 34 | 35 | 36 | 37 | 38 | 39 | 40 | result 41 | 42 | 43 | 44 | 45 | 46 | 47 | Confirm Dialog 48 | 49 | 50 | 51 | 52 | 53 | 54 | Progress Window 55 | 56 | 57 | 58 | 59 | 60 | 61 | Prompt Dialog 62 | 63 | 64 | 65 | 66 | 67 | 68 | false 69 | 70 | 71 | 72 | 73 | 74 | 75 | Select Font 76 | 77 | 78 | 79 | 80 | 81 | 82 | Font Sample 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /PythonPlugins/SineNode.py: -------------------------------------------------------------------------------- 1 | import maya.api.OpenMaya as OpenMaya 2 | import sys 3 | import math 4 | 5 | # Set this flag to show we are using api 2.0 6 | maya_useNewAPI = True 7 | 8 | 9 | class SineNode(OpenMaya.MPxNode): 10 | # class attributes are placed here maya will use these to create the node 11 | # and to determine the type of node which is setup during initialize method 12 | # Older plugins used to set these to OpenMaya.MObject() (see api1 demos) 13 | # however we now just set to None and use the FunctionSets to create the attributess 14 | id = OpenMaya.MTypeId(0x00105481) 15 | amplitude = None 16 | frequency = None 17 | time = None 18 | output = None 19 | function_type = None 20 | 21 | def __init__(self): 22 | OpenMaya.MPxNode.__init__(self) 23 | 24 | # factory to create the node 25 | @staticmethod 26 | def creator(): 27 | return SineNode() 28 | 29 | @staticmethod 30 | def initialize(): 31 | # Create the attributes 32 | numeric_attrib_fn = OpenMaya.MFnNumericAttribute() 33 | SineNode.amplitude = numeric_attrib_fn.create( 34 | "amplitude", "a", OpenMaya.MFnNumericData.kDouble, 1.0 35 | ) 36 | numeric_attrib_fn.storable = True 37 | numeric_attrib_fn.keyable = True 38 | numeric_attrib_fn.readable = True 39 | numeric_attrib_fn.writable = True 40 | OpenMaya.MPxNode.addAttribute(SineNode.amplitude) 41 | # add frequency attribute 42 | SineNode.frequency = numeric_attrib_fn.create( 43 | "frequency", "f", OpenMaya.MFnNumericData.kDouble, 1.0 44 | ) 45 | numeric_attrib_fn.storable = True 46 | numeric_attrib_fn.keyable = True 47 | numeric_attrib_fn.readable = True 48 | numeric_attrib_fn.writable = True 49 | OpenMaya.MPxNode.addAttribute(SineNode.frequency) 50 | # add enum attribute 51 | enum_attribute_fn = OpenMaya.MFnEnumAttribute() 52 | SineNode.function_type = enum_attribute_fn.create("functionType", "ft") 53 | enum_attribute_fn.addField("sine", 0) 54 | enum_attribute_fn.addField("cosine", 1) 55 | enum_attribute_fn.addField("complex", 2) 56 | 57 | enum_attribute_fn.storable = True 58 | enum_attribute_fn.keyable = True 59 | enum_attribute_fn.readable = True 60 | OpenMaya.MPxNode.addAttribute(SineNode.function_type) 61 | 62 | # Time attribute 63 | time_attribute_fn = OpenMaya.MFnUnitAttribute() 64 | SineNode.time = time_attribute_fn.create( 65 | "time", "t", OpenMaya.MFnUnitAttribute.kTime, 0.0 66 | ) 67 | time_attribute_fn.storable = False 68 | time_attribute_fn.keyable = False 69 | OpenMaya.MPxNode.addAttribute(SineNode.time) 70 | # output set by the compute 71 | SineNode.output = numeric_attrib_fn.create( 72 | "output", "o", OpenMaya.MFnNumericData.kDouble, 0.0 73 | ) 74 | numeric_attrib_fn.storable = False 75 | numeric_attrib_fn.keyable = False 76 | numeric_attrib_fn.readable = True 77 | numeric_attrib_fn.writable = False 78 | OpenMaya.MPxNode.addAttribute(SineNode.output) 79 | # Set the attribute dependencies 80 | OpenMaya.MPxNode.attributeAffects(SineNode.amplitude, SineNode.output) 81 | OpenMaya.MPxNode.attributeAffects(SineNode.frequency, SineNode.output) 82 | OpenMaya.MPxNode.attributeAffects(SineNode.time, SineNode.output) 83 | OpenMaya.MPxNode.attributeAffects(SineNode.function_type, SineNode.output) 84 | return True 85 | 86 | def compute(self, plug, data): 87 | """ 88 | Description 89 | 90 | When input attributes are dirty this method will be called to 91 | recompute the output attributes. 92 | 93 | Arguments 94 | 95 | plug - the attribute that triggered the compute 96 | data - the nodes data 97 | """ 98 | # we only need to compute if the plug is the output node changing 99 | if plug == SineNode.output: 100 | # get the input data and convert to python types 101 | time_data = data.inputValue(SineNode.time) 102 | time = time_data.asTime() 103 | amplitude = data.inputValue(SineNode.amplitude).asDouble() 104 | frequency = data.inputValue(SineNode.frequency).asDouble() 105 | function_type = data.inputValue(SineNode.function_type).asShort() 106 | # compute the result 107 | if function_type == 0: 108 | result = amplitude * math.sin( 109 | math.radians(frequency * math.pi * time.asUnits(OpenMaya.MTime.kSeconds)) 110 | ) 111 | elif function_type == 1: 112 | result = amplitude * math.cos( 113 | math.radians(frequency * math.pi * time.asUnits(OpenMaya.MTime.kSeconds)) 114 | ) 115 | else: 116 | result = amplitude * math.sin( 117 | math.radians(frequency * math.pi * time.asUnits(OpenMaya.MTime.kSeconds)) 118 | ) + amplitude * math.sin( 119 | math.radians( 120 | 2 * frequency * math.pi * time.asUnits(OpenMaya.MTime.kSeconds) 121 | ) 122 | ) 123 | # set the output 124 | output_data = data.outputValue(SineNode.output) 125 | output_data.setDouble(result) 126 | data.setClean(plug) 127 | return True 128 | return False 129 | 130 | def postConstructor(self): 131 | """ 132 | Description 133 | 134 | When instances of this node are created internally, the MObject associated 135 | with the instance is not created until after the constructor of this class 136 | is called. This means that no member functions of Node can 137 | be called in the constructor. 138 | The postConstructor solves this problem. Maya will call this function 139 | after the internal object has been created. 140 | As a general rule do all of your initialization in the postConstructor. 141 | Not used here but for info 142 | """ 143 | print("postConstructor called") 144 | 145 | 146 | def initializePlugin(obj): 147 | plugin = OpenMaya.MFnPlugin(obj) 148 | try: 149 | plugin.registerNode( 150 | "SineNodePy", SineNode.id, SineNode.creator, SineNode.initialize 151 | ) 152 | except: 153 | sys.stderr.write("Failed to register node\n") 154 | raise 155 | 156 | 157 | def uninitializePlugin(obj): 158 | plugin = OpenMaya.MFnPlugin(obj) 159 | 160 | try: 161 | plugin.deregisterNode(SineNode.id) 162 | except: 163 | sys.stderr.write("Failed to deregister node\n") 164 | raise 165 | -------------------------------------------------------------------------------- /PythonPlugins/tests/test_CustomSphere.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import sys, os 4 | import maya.standalone 5 | import CustomSphere 6 | 7 | 8 | def setup_module(module): 9 | maya.standalone.initialize(name="python") 10 | import maya.cmds as cmds 11 | 12 | # add the current directory to the MAYA_PLUG_IN_PATH 13 | print("initializing maya-standalone") 14 | 15 | 16 | def teardown_module(module): 17 | print("closing down maya-standalone") 18 | maya.standalone.uninitialize() 19 | 20 | 21 | def test_CustomSphere(): 22 | # Create a sphere 23 | # 24 | import maya.cmds as cmds 25 | 26 | print("loading plugin") 27 | cmds.loadPlugin("CustomSphere.py") 28 | cmds.CustomSpherePy(100) 29 | results = cmds.ls("sphere*", type="shape") 30 | assert len(results) == 100 31 | cmds.undo() 32 | results = cmds.ls("sphere*", type="shape") 33 | assert len(results) == 0 34 | -------------------------------------------------------------------------------- /PythonPlugins/tests/test_CustomSphereSolution.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import sys, os 4 | import maya.standalone 5 | import CustomSphereSolution 6 | 7 | 8 | def setup_module(module): 9 | maya.standalone.initialize(name="python") 10 | import maya.cmds as cmds 11 | 12 | # add the current directory to the MAYA_PLUG_IN_PATH 13 | print("initializing maya-standalone") 14 | 15 | 16 | def teardown_module(module): 17 | print("closing down maya-standalone") 18 | maya.standalone.uninitialize() 19 | 20 | 21 | def test_CustomSphere(): 22 | # Create a sphere 23 | # 24 | import maya.cmds as cmds 25 | 26 | print("loading plugin") 27 | cmds.loadPlugin("CustomSphereSolution.py") 28 | cmds.CustomSphereSolutionPy(n=100, x=20, y=20, z=20, mr=0.2, mm=2.5) 29 | # results = cmds.ls("sphere*", type="shape") 30 | # assert len(results) == 100 31 | # cmds.undo() 32 | # results = cmds.ls("sphere*", type="shape") 33 | # assert len(results) == 0 34 | -------------------------------------------------------------------------------- /PythonPlugins/tests/test_SineNode.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import sys, os 4 | import maya.standalone 5 | import SineNode 6 | 7 | node_name = "TestSineNode" 8 | 9 | 10 | def setup_module(module): 11 | maya.standalone.initialize(name="python") 12 | import maya.cmds as cmds 13 | 14 | print("Loading plugin for testing") 15 | cmds.loadPlugin("SineNode.py") 16 | cmds.createNode("SineNodePy", name=node_name) 17 | 18 | 19 | def teardown_module(module): 20 | print("closing down maya-standalone") 21 | maya.standalone.uninitialize() 22 | 23 | 24 | def test_SineNode(): 25 | # Create a sphere 26 | import maya.cmds as cmds 27 | 28 | results = cmds.ls(node_name) 29 | assert len(results) == 1 30 | 31 | 32 | def test_setAttrib(): 33 | import maya.cmds as cmds 34 | 35 | # Set the amplitude attribute 36 | cmds.setAttr(f"{node_name}.amplitude", 2.0) 37 | results = cmds.getAttr(f"{node_name}.amplitude") 38 | assert results == pytest.approx(2.0) 39 | 40 | cmds.setAttr(f"{node_name}.frequency", 200.0) 41 | results = cmds.getAttr(f"{node_name}.frequency") 42 | assert results == pytest.approx(200.0) 43 | -------------------------------------------------------------------------------- /PythonScripts/ImageMapExample.py: -------------------------------------------------------------------------------- 1 | import maya.OpenMaya as om 2 | import maya.cmds as cmds 3 | 4 | 5 | 6 | basicFilter = "*.*" 7 | 8 | imageFile=cmds.fileDialog2(caption="Please select imagefile", 9 | fileFilter=basicFilter, fm=1) 10 | 11 | 12 | img=MayaImage(str(imageFile[0])) 13 | print img.width() 14 | print img.height() 15 | xoffset=-img.width()/2 16 | yoffset=-img.height()/2 17 | 18 | for y in range (0,img.height()) : 19 | for x in range(0,img.width()) : 20 | r,g,b,a=img.getPixel(x,y) 21 | if r > 10 : 22 | cmds.polyCube(h=float(r/10)) 23 | cmds.move(xoffset+x,float(r/10)/2,yoffset+y) 24 | -------------------------------------------------------------------------------- /PythonScripts/MayaImage.py: -------------------------------------------------------------------------------- 1 | ######################################## 2 | # A simple wrapper for the MImage class 3 | # Author Jon Macey 4 | # Date 22/3/2011 5 | # Version 1.0 6 | # Sample Usage Below 7 | # img=MayaImage('/Users/jmacey/Road.tiff') 8 | # print img.width() 9 | # print img.height() 10 | # print img.hasAlpha() 11 | # 12 | # print img.getPixel(20,30) 13 | # print img.getRGB(20,50) 14 | ######################################## 15 | import sys 16 | 17 | import maya.OpenMaya as om 18 | 19 | class MayaImage : 20 | """ The main class, needs to be constructed with a filename """ 21 | def __init__(self,filename) : 22 | """ constructor pass in the name of the file to load (absolute file name with path) """ 23 | # create an MImage object 24 | self.image=om.MImage() 25 | # read from file MImage should handle errors for us so no need to check 26 | self.image.readFromFile(filename) 27 | # as the MImage class is a wrapper to the C++ module we need to access data 28 | # as pointers, to do this use the MScritUtil helpers 29 | scriptUtilWidth = om.MScriptUtil() 30 | scriptUtilHeight = om.MScriptUtil() 31 | 32 | # first we create a pointer to an unsigned in for width and height 33 | widthPtr = scriptUtilWidth.asUintPtr() 34 | heightPtr = scriptUtilHeight.asUintPtr() 35 | # now we set the values to 0 for each 36 | scriptUtilWidth.setUint( widthPtr, 0 ) 37 | scriptUtilHeight.setUint( heightPtr, 0 ) 38 | # now we call the MImage getSize method which needs the params passed as pointers 39 | # as it uses a pass by reference 40 | self.image.getSize( widthPtr, heightPtr ) 41 | # once we get these values we need to convert them to int so use the helpers 42 | self.m_width = scriptUtilWidth.getUint(widthPtr) 43 | self.m_height =scriptUtilHeight.getUint(heightPtr) 44 | # now we grab the pixel data and store 45 | self.charPixelPtr = self.image.pixels() 46 | # query to see if it's an RGB or RGBA image, this will be True or False 47 | self.m_hasAlpha=self.image.isRGBA() 48 | # if we are doing RGB we step into the image array in 3's 49 | # data is always packed as RGBA even if no alpha present 50 | self.imgStep=4 51 | # finally create an empty script util and a pointer to the function 52 | # getUcharArrayItem function for speed 53 | scriptUtil = om.MScriptUtil() 54 | self.getUcharArrayItem=scriptUtil.getUcharArrayItem 55 | 56 | 57 | def width(self) : 58 | """ return the width of the image """ 59 | return self.m_width 60 | 61 | def height(self) : 62 | """ return the height of the image """ 63 | return self.m_height 64 | 65 | def hasAlpha(self) : 66 | """ return True is the image has an Alpha channel """ 67 | return self.m_hasAlpha 68 | 69 | def getPixel(self,x,y) : 70 | """ get the pixel data at x,y and return a 3/4 tuple depending upon type """ 71 | # check the bounds to make sure we are in the correct area 72 | if x<0 or x>self.m_width : 73 | print "error x out of bounds\n" 74 | return 75 | if y<0 or y>self.m_height : 76 | print "error y our of bounds\n" 77 | return 78 | # now calculate the index into the 1D array of data 79 | index=(y*self.m_width*4)+x*4 80 | # grab the pixels 81 | red = self.getUcharArrayItem(self.charPixelPtr,index) 82 | green = self.getUcharArrayItem(self.charPixelPtr,index+1) 83 | blue = self.getUcharArrayItem(self.charPixelPtr,index+2) 84 | alpha=self.getUcharArrayItem(self.charPixelPtr,index+3) 85 | return (red,green,blue,alpha) 86 | 87 | def getRGB(self,x,y) : 88 | r,g,b,a=getPixel(x,y) 89 | return (r,g,b) -------------------------------------------------------------------------------- /PythonScripts/UnitTestMaya/MayaImage.py: -------------------------------------------------------------------------------- 1 | import maya.api.OpenMaya as OpenMaya 2 | import ctypes 3 | 4 | 5 | class MayaImage : 6 | def __init__(self,filename=None,width=None,height=None,channels=4,imagetype=OpenMaya.MImage.kByte) : 7 | """ constructor pass in the name of the file to load (absolute file name with path) 8 | or width and height to create and image 9 | """ 10 | if filename != None and isinstance(filename,str) : 11 | self.filename=filename 12 | elif (width !=None and height !=None) : 13 | try : 14 | self.width=int(width) 15 | self.height=int(height) 16 | except ValueError : 17 | print('error converting values to int') 18 | exit() 19 | print('creating empty image {}x{}'.format(width,height)) 20 | else : 21 | raise ValueError('ImageError trouble converting arguments') 22 | # create an MImage object 23 | self.image=OpenMaya.MImage() 24 | self.channels=channels 25 | self.type=imagetype 26 | if filename !=None : 27 | self.readImage(filename) 28 | else : 29 | self.image.create(self.width,self.height,self.channels,self.type) 30 | self.charPixelPtr = ctypes.cast(self.image.pixels(), ctypes.POINTER(ctypes.c_char) ) 31 | self.width,self.height=self.image.getSize() 32 | 33 | 34 | def readImage(self,filename) : 35 | # read from file MImage should handle errors for us so no need to check 36 | self.image.readFromFile(filename) 37 | self.width,self.height=self.image.getSize() 38 | # get the pixel data 39 | self.charPixelPtr = ctypes.cast(self.image.pixels(), ctypes.POINTER(ctypes.c_char) ) 40 | # query to see if it's an RGB or RGBA image, this will be True or False 41 | self.hasAlpha=self.image.isRGBA() 42 | 43 | def setPixel(self,x,y,r,g,b,a=255) : 44 | # check the bounds to make sure we are in the correct area 45 | if x<0 or x>self.width : 46 | raise IndexError('x value out of range') 47 | if y<0 or y>self.height : 48 | raise IndexError('y value out of range') 49 | # now calculate the index into the 1D array of data 50 | index=(y*self.width*self.channels)+x*self.channels 51 | # grab the pixels we need to convert from char to integer ordinal type so use ord 52 | self.charPixelPtr[index]=ctypes.c_char(chr(r)) 53 | self.charPixelPtr[index+1]=ctypes.c_char(chr(g)) 54 | self.charPixelPtr[index+2]=ctypes.c_char(chr(b)) 55 | self.charPixelPtr[index+3]=ctypes.c_char(chr(a)) 56 | 57 | def save(self,filename,outputFormat='png') : 58 | self.image.writeToFile(filename+'.'+outputFormat,outputFormat=outputFormat) 59 | 60 | def getPixel(self,x,y) : 61 | """ get the pixel data at x,y and return a 3/4 tuple depending upon type """ 62 | # check the bounds to make sure we are in the correct area 63 | if x<0 or x>self.width : 64 | print "error x out of bounds\n" 65 | return 66 | if y<0 or y>self.height : 67 | print "error y our of bounds\n" 68 | return 69 | # now calculate the index into the 1D array of data 70 | index=(y*self.width*self.channels)+x*self.channels 71 | # grab the pixels we need to convert from char to integer ordinal type so use ord 72 | red = ord(self.charPixelPtr[index]) 73 | green = ord(self.charPixelPtr[index+1]) 74 | blue = ord(self.charPixelPtr[index+2]) 75 | alpha = ord(self.charPixelPtr[index+3]) 76 | #print type(red),green,blue,alpha 77 | return (red,green,blue,alpha) 78 | 79 | def getRGB(self,x,y) : 80 | r,g,b,_=self.getPixel(x,y) 81 | return (r,g,b) 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /PythonScripts/UnitTestMaya/TestMayaImage.py: -------------------------------------------------------------------------------- 1 | #!/Applications/Autodesk/maya2019/Maya.app/Contents/bin/mayapy 2 | 3 | import maya.standalone 4 | import unittest 5 | import MayaImage as mi 6 | 7 | class TestMayaImage(unittest.TestCase): 8 | @classmethod 9 | def setUpClass(self): 10 | print('doing setup') 11 | 12 | def testConstructFromWidthHeight(self) : 13 | image=mi.MayaImage(width=100,height=200) 14 | self.assertEqual(image.width,100) 15 | self.assertEqual(image.height,200) 16 | 17 | def testThrowFromBadFilename(self) : 18 | with self.assertRaises(ValueError) : 19 | _=mi.MayaImage(200) 20 | def testThrowFromIndexError(self) : 21 | with self.assertRaises(IndexError) : 22 | img=mi.MayaImage(width=10,height=10) 23 | img.setPixel(200,100,255,255,0) 24 | def testSaveImage(self) : 25 | image=mi.MayaImage(width=100,height=100) 26 | for h in range(0,100) : 27 | for w in range(0,100) : 28 | image.setPixel(w,h,255,0,0) 29 | image.save("test","png") 30 | 31 | def testOpenImage(self) : 32 | self.testSaveImage() 33 | image=mi.MayaImage("test.png") 34 | r,g,b,_=image.getPixel(2,2) 35 | self.assertEqual(r,255) 36 | self.assertEqual(g,0) 37 | self.assertEqual(b,0) 38 | 39 | 40 | if __name__ == '__main__' : 41 | maya.standalone.initialize(name='python') 42 | unittest.main() 43 | print('closing down maya-standalone') 44 | maya.standalone.uninitialize() -------------------------------------------------------------------------------- /PythonScripts/UnitTestMaya/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NCCA/MayaAPICode/f0ecc9f9559913be642d7d38069d33b915de2d60/PythonScripts/UnitTestMaya/test.png -------------------------------------------------------------------------------- /PythonScripts/bounce.py: -------------------------------------------------------------------------------- 1 | import maya.cmds as cmds 2 | 3 | cmds.polySphere(name='ball', radius=2) 4 | 5 | g = -0.04 6 | yVelocity = 0.8 7 | xVelocity = 0.4 8 | initialYPos = 6 9 | initialXPos = 0 10 | cmds.move(initialXPos,initialYPos,0,"ball") 11 | posy=initialYPos 12 | timeMult=4 13 | for bounces in range(0,6): 14 | time=0 15 | posy=initialYPos 16 | while posy>0 : 17 | posy = initialYPos + yVelocity*(time-1) + g*(time-1)*(time-1)/2 18 | posx = initialXPos + xVelocity*((time) + time-1) 19 | cmds.setKeyframe( 'ball', attribute='translateY', value=posy, t=time*timeMult ) 20 | cmds.setKeyframe( 'ball', attribute='translateX', value=posx, t=time*timeMult ) 21 | time+=1 22 | 23 | yVelocity-=0.1 -------------------------------------------------------------------------------- /PythonScripts/imageMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NCCA/MayaAPICode/f0ecc9f9559913be642d7d38069d33b915de2d60/PythonScripts/imageMap.png -------------------------------------------------------------------------------- /PythonScripts/imageMap.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NCCA/MayaAPICode/f0ecc9f9559913be642d7d38069d33b915de2d60/PythonScripts/imageMap.tif -------------------------------------------------------------------------------- /PythonScripts/mayaExample1.py: -------------------------------------------------------------------------------- 1 | import maya.cmds as cmds 2 | 3 | # let's create a sphere 4 | cmds.sphere(radius=2, name='Sphere1') 5 | 6 | # query the radius of the sphere named Sphere1 7 | radius = cmds.sphere('Sphere1', query=True, radius=True) 8 | print('The sphere radius =',radius) 9 | 10 | # modify the radius 11 | print('Increment the sphere\'s radius by one unit') 12 | cmds.sphere('Sphere1', edit=True, radius=radius+1) 13 | radius = cmds.sphere('Sphere1', query=True, radius=True) 14 | print ('The new sphere radius =', radius) 15 | -------------------------------------------------------------------------------- /PythonScripts/mayaExample2.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function,division 2 | import maya.cmds as cmds 3 | 4 | # let's delete all objects 5 | cmds.select(all=True) 6 | cmds.delete() 7 | #let's create a new torus 8 | cmds.torus(r=4, hr=0.5, name='Torus1') 9 | cmds.move(0, 0, 0, 'Torus1') 10 | cmds.scale(0.5, 0.5, 0.5, 'Torus1') 11 | cmds.rotate(0, '45deg', 0, 'Torus1') 12 | 13 | -------------------------------------------------------------------------------- /PythonScripts/mayaExample3.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function,division 2 | import maya.cmds as cmds 3 | 4 | # lets delete all objects 5 | cmds.select(all=True) 6 | cmds.delete() 7 | #lets create a nurb's cube 8 | cmds.nurbsCube(w=3, name='Cube1') 9 | cmds.getAttr('Cube1.tx') 10 | cmds.getAttr('Cube1.ty') 11 | cmds.getAttr('Cube1.tz') 12 | 13 | # let's make sure 'Cube1' is selected 14 | cmds.select('Cube1', replace=True) 15 | #let's change its translation attributes 16 | cmds.setAttr('Cube1.tx', 1) 17 | cmds.setAttr('Cube1.ty', 2) 18 | cmds.setAttr('Cube1.tz', 3) 19 | 20 | -------------------------------------------------------------------------------- /PythonScripts/output.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NCCA/MayaAPICode/f0ecc9f9559913be642d7d38069d33b915de2d60/PythonScripts/output.jpeg -------------------------------------------------------------------------------- /PythonScripts/pymelExample1.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function,division 2 | import pymel.core as pm 3 | 4 | pm.sphere(radius=2, name='Sphere1') 5 | 6 | # query the radius of the sphere named Sphere1 7 | radius = pm.sphere('Sphere1', query=True, radius=True) 8 | print ('The sphere radius =',radius) 9 | 10 | # modify the radius 11 | print ('Increment the sphere\'s radius by one unit') 12 | pm.sphere('Sphere1', edit=True, radius=radius+1) 13 | radius = pm.sphere('Sphere1', query=True, radius=True) 14 | print ('The new sphere radius =', radius) 15 | -------------------------------------------------------------------------------- /QtGuiC++/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(QtGUICPP) 3 | # include the project setting file 4 | include($ENV{DEVKIT_LOCATION}/cmake/pluginEntry.cmake) 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | 7 | 8 | set(CMAKE_AUTOUIC ON) 9 | set(CMAKE_AUTOMOC ON) 10 | set(CMAKE_AUTORCC ON) 11 | 12 | set(CMAKE_CXX_STANDARD 17) 13 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 14 | find_package(Qt5 COMPONENTS OpenGL Widgets REQUIRED) 15 | 16 | 17 | # set SOURCE_FILES 18 | set(SOURCE_FILES src/QtGui.cpp src/Plugin.cpp include/QtGui.h ) 19 | include_directories(include) 20 | # set linking libraries 21 | set(LIBRARIES OpenMaya Foundation Qt5::Widgets ) 22 | # Build plugin 23 | build_plugin() 24 | -------------------------------------------------------------------------------- /QtGuiC++/QtGuiCpp.pro: -------------------------------------------------------------------------------- 1 | include(qtconfig) 2 | 3 | QT += widgets 4 | unix|macx { 5 | CONFIG += no_plugin_name_prefix 6 | } 7 | CONFIG+=Release 8 | INCLUDEPATH+=include 9 | TARGET = QtGuiCPP 10 | HEADERS += include/QtGui.h 11 | SOURCES += src/QtGui.cpp \ 12 | src/Plugin.cpp 13 | -------------------------------------------------------------------------------- /QtGuiC++/include/QtGui.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOMSPHERE_H_ 2 | #define CUSTOMSPHERE_H_ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class SimpleDialog : public QWidget 10 | { 11 | Q_OBJECT 12 | 13 | public : 14 | SimpleDialog()=default; 15 | SimpleDialog(QWidget* parent); 16 | virtual ~SimpleDialog()=default; 17 | private : 18 | }; 19 | 20 | 21 | //---------------------------------------------------------------------------------------------------------------------- 22 | // @brief a simple maya command class 23 | //---------------------------------------------------------------------------------------------------------------------- 24 | 25 | class QtGui : public MPxCommand 26 | { 27 | public: 28 | //---------------------------------------------------------------------------------------------------------------------- 29 | // virtual dtor 30 | //---------------------------------------------------------------------------------------------------------------------- 31 | virtual ~QtGui(); 32 | //---------------------------------------------------------------------------------------------------------------------- 33 | /// @brief the doIt command is called everytime the command is executed in the maya shell 34 | /// @param _args the command arguments passed when command is run 35 | /// @note from the maya docs 36 | /// The doIt method should collect whatever information is 37 | /// required to do the task, and store it in local class data. It 38 | /// should finally call redoIt to make the command happen. The 39 | /// redoIt method should do the actual work, using only the local 40 | /// class data. The undoIt method should undo the actual work 41 | /// again using only the local class data. 42 | //---------------------------------------------------------------------------------------------------------------------- 43 | MStatus doIt( const MArgList& _args ); 44 | //---------------------------------------------------------------------------------------------------------------------- 45 | /// @brief our creator function called when the class is created 46 | /// the returns a new instance of this class 47 | //---------------------------------------------------------------------------------------------------------------------- 48 | static void* creator(); 49 | 50 | private: 51 | static QPointer s_widget; 52 | 53 | }; 54 | 55 | //---------------------------------------------------------------------------------------------------------------------- 56 | 57 | #endif 58 | 59 | -------------------------------------------------------------------------------- /QtGuiC++/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "QtGui.h" 2 | #include 3 | 4 | //---------------------------------------------------------------------------------------------------------------------- 5 | 6 | MStatus initializePlugin( MObject obj ) 7 | { 8 | MStatus status; 9 | MFnPlugin plugin( obj, "", "NCCA" , "Any" ); 10 | 11 | status = plugin.registerCommand( "QtGuiCPP", QtGui::creator ); 12 | if ( !status ) 13 | { 14 | status.perror( "Unable to register command \"customCreateSphere\"" ); 15 | return status; 16 | } 17 | 18 | return status; 19 | } 20 | 21 | //---------------------------------------------------------------------------------------------------------------------- 22 | 23 | MStatus uninitializePlugin( MObject obj ) 24 | { 25 | MStatus status; 26 | MFnPlugin plugin( obj ); 27 | 28 | status = plugin.deregisterCommand( "QtGuiCPP" ); 29 | if ( !status ) 30 | { 31 | status.perror( "Unable to register command \"customCreateSphere\"" ); 32 | return status; 33 | } 34 | 35 | return status; 36 | } 37 | 38 | //---------------------------------------------------------------------------------------------------------------------- 39 | 40 | -------------------------------------------------------------------------------- /QtGuiC++/src/QtGui.cpp: -------------------------------------------------------------------------------- 1 | #include "QtGui.h" 2 | #include 3 | //---------------------------------------------------------------------------------------------------------------------- 4 | /// @brief simple macro to check status and return if error 5 | /// originally written by Sola Aina 6 | //---------------------------------------------------------------------------------------------------------------------- 7 | #define CHECK_STATUS_AND_RETURN_IF_FAIL( status , message ) \ 8 | if( !status ) \ 9 | { \ 10 | MString errorString = status.errorString() + MString( message); \ 11 | MGlobal::displayError( errorString ); \ 12 | return MStatus::kFailure; \ 13 | } \ 14 | 15 | //---------------------------------------------------------------------------------------------------------------------- 16 | 17 | 18 | SimpleDialog::SimpleDialog(QWidget* parent) 19 | { 20 | 21 | } 22 | 23 | 24 | QPointer QtGui::s_widget; 25 | 26 | 27 | QtGui::~QtGui() 28 | { 29 | // as we have no dynamic allocation we don't need to do anything here 30 | } 31 | 32 | //---------------------------------------------------------------------------------------------------------------------- 33 | 34 | MStatus QtGui::doIt( const MArgList& _args ) 35 | { 36 | return MStatus::kFailure; 37 | } 38 | 39 | 40 | 41 | void* QtGui::creator() 42 | { 43 | return new QtGui(); 44 | } 45 | 46 | //---------------------------------------------------------------------------------------------------------------------- 47 | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Maya API demos used in Lectures 2 | 3 | The lecture notes to accompany this code are here https://nccastaff.bournemouth.ac.uk/jmacey/MayaAPI/ 4 | 5 | 6 | 7 | The dev kits are located here https://www.autodesk.com/developer-network/platform-technologies/maya and are now not part of the default maya install. 8 | 9 | 10 | Once the DevKit has been extracted note the location and add the following environment variable to your system 11 | 12 | ``` 13 | DEVKIT_LOCATION = [location where extracted] 14 | 15 | // Currently in the labs you need to do the following 16 | 17 | export DEVKIT_LOCATION=/public/devel/24-25/maya2023_dev/ 18 | ``` 19 | 20 | This is used in the CMakeLists.txt to locate the devkit include files and libraries and CMake will be used to build all projects on Mac, Linux and Windows. 21 | 22 | For each of the projects you will need to create a build directory and run cmake to generate the build files. For example on Linux or Mac 23 | 24 | ``` 25 | mkdir build 26 | cd build 27 | cmake -G Ninja .. 28 | ninja 29 | ``` 30 | 31 | If you use the top level CMakeLists.txt then all the projects will be built and the resultant plugins will be placed in the plugins folder for ease. 32 | 33 | You can set the MAYA_PLUG_IN_PATH in the Maya.env file to the plugins directory and then the plugins will be loaded when maya starts 34 | 35 | ``` 36 | MAYA_PLUG_IN_PATH = [location of plugins folder] 37 | ``` 38 | 39 | 40 | -------------------------------------------------------------------------------- /Sequence/TestNode.pro: -------------------------------------------------------------------------------- 1 | #################################################################################### 2 | # This file is split into Three sections 3 | # The first configures Qt and the source files for all platforms 4 | # The second is the linux build 5 | # The third the mac build 6 | # (if your using windows you will need to add a fourth one!) 7 | # first lets remove Qt core and gui not going to need it 8 | #################################################################################### 9 | QT -= core gui 10 | #################################################################################### 11 | # This is the name of the plugin / final lib file 12 | #################################################################################### 13 | TARGET = testNode 14 | #################################################################################### 15 | # for for mac we need a bundle so change the name 16 | #################################################################################### 17 | macx:TARGET=testNode.bundle 18 | #################################################################################### 19 | # here we add the source files (and headers if required) 20 | #################################################################################### 21 | SOURCES+=src/TestNode.cpp \ 22 | src/Plugin.cpp 23 | HEADERS+=include/TestNode.h 24 | 25 | 26 | OBJECTS_DIR = obj 27 | INCLUDEPATH+=include 28 | DEPENDPATH+=include 29 | # these are defines required by Maya to re-define some C++ 30 | # stuff, we will add some more later to tell what platform 31 | # we are on as well 32 | DEFINES+=REQUIRE_IOSTREAM \ 33 | _BOOL 34 | #################################################################################### 35 | # These are the maya libs we need to link to, this will change depending 36 | # upon which maya framework we use, just add them to the end of 37 | # this list as required and they will be added to the build 38 | #################################################################################### 39 | MAYALIBS=-lOpenMaya \ 40 | -lFoundation \ 41 | -lOpenMayaUI 42 | #################################################################################### 43 | # these are all the libs usually included by mayald in case you need 44 | # them just add them to the list above and make sure you escape 45 | #################################################################################### 46 | #-lOpenMayalib \ 47 | #-lOpenMaya \ 48 | #-lAnimSlice \ 49 | #-lDeformSlice \ 50 | #-lModifiers \ 51 | #-lDynSlice \ 52 | #-lKinSlice \ 53 | #-lModelSlice \ 54 | #-lNurbsSlice \ 55 | #-lPolySlice \ 56 | #-lProjectSlice \ 57 | #-lImage \ 58 | #-lShared \ 59 | #-lTranslators \ 60 | #-lDataModel \ 61 | #-lRenderModel \ 62 | #-lNurbsEngine \ 63 | #-lDependEngine \ 64 | #-lCommandEngine \ 65 | #-lFoundation \ 66 | #-lIMFbase \ 67 | #-lm -ldl 68 | #################################################################################### 69 | # now tell linux we need to build a lib 70 | #################################################################################### 71 | linux-g++-64:TEMPLATE = lib 72 | #################################################################################### 73 | # this tells qmake where maya is 74 | #################################################################################### 75 | linux-g++-64:MAYALOCATION=/usr/autodesk/maya2011-x64/ 76 | #################################################################################### 77 | # under linux we need to use the version of g++ used to build maya 78 | # in this case g++412 79 | #################################################################################### 80 | linux-g++-64:QMAKE_CXX = g++412 81 | #################################################################################### 82 | # set the include path for linux 83 | #################################################################################### 84 | linux-g++-64:INCLUDEPATH += $$MAYALOCATION/include \ 85 | /usr/X11R6/include 86 | #################################################################################### 87 | # set which libs we need to include 88 | #################################################################################### 89 | linux-g++-64:LIBS += -L$$MAYALOCATION/lib \ 90 | $$MAYALIBS 91 | #################################################################################### 92 | # tell maya we're building for linux 93 | #################################################################################### 94 | linux:DEFINES+=linux 95 | #################################################################################### 96 | # tell maya we're building for Mac 97 | #################################################################################### 98 | macx:DEFINES+=OSMac_ 99 | macx:MAYALOCATION=/Applications/Autodesk/maya2011 100 | macx:CONFIG -= app_bundle 101 | macx:INCLUDEPATH+=$$MAYALOCATION/devkit/include 102 | macx:LIBS+= -framework OpenGL 103 | #################################################################################### 104 | # under mac we need to build a bundle, to do this use 105 | # the -bundle flag but we also need to not use -dynamic lib so 106 | # remove this 107 | #################################################################################### 108 | macx:LIBS +=-bundle 109 | mac:LIBS -=-dynamiclib 110 | #################################################################################### 111 | 112 | #################################################################################### 113 | macx:LIBS += -L$$MAYALOCATION/Maya.app/Contents/MacOS \ 114 | $$MAYALIBS 115 | #################################################################################### 116 | -------------------------------------------------------------------------------- /Sequence/include/TestNode.h: -------------------------------------------------------------------------------- 1 | #ifndef __TESTNODE_H__ 2 | #define __TESTNODE_H__ 3 | 4 | #define LINUX 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | //---------------------------------------------------------------------------------------------------------------------- 16 | /// @brief this class implements a perlin noise node with one output, it has 3 different noise 17 | /// access types noise, turbulance and complex descriptions below 18 | //---------------------------------------------------------------------------------------------------------------------- 19 | 20 | class TestNode : public MPxNode 21 | { 22 | 23 | public: 24 | //---------------------------------------------------------------------------------------------------------------------- 25 | /// @brief the creator node is called when the plugin is created 26 | //---------------------------------------------------------------------------------------------------------------------- 27 | static void *creator(); 28 | //---------------------------------------------------------------------------------------------------------------------- 29 | /// @brief initialize called when plug is created 30 | //---------------------------------------------------------------------------------------------------------------------- 31 | static MStatus initialize(); 32 | //---------------------------------------------------------------------------------------------------------------------- 33 | /// @brief compute is called every time the attributes of the class change 34 | /// @param [in] _plug plug representing the attribute that needs to be recomputed 35 | /// @param [in] _block data block containing storage for the node'_scale attributes 36 | //---------------------------------------------------------------------------------------------------------------------- 37 | virtual MStatus compute( 38 | const MPlug &_plug , 39 | MDataBlock &_block 40 | ); 41 | 42 | 43 | //---------------------------------------------------------------------------------------------------------------------- 44 | /// @brief the id of this plugin must be public so we can set outside of class 45 | //---------------------------------------------------------------------------------------------------------------------- 46 | static MTypeId m_id; 47 | //---------------------------------------------------------------------------------------------------------------------- 48 | /// @brief the unique type name of our custom node. Mainly for mel purposes. 49 | /// must be public so maya can access 50 | //---------------------------------------------------------------------------------------------------------------------- 51 | static const MString typeName; 52 | //---------------------------------------------------------------------------------------------------------------------- 53 | /// @brief ctor this will dynamically allocate a Noise class 54 | //---------------------------------------------------------------------------------------------------------------------- 55 | TestNode(); 56 | //---------------------------------------------------------------------------------------------------------------------- 57 | /// @brief dtor this will destroy the noise class created in the ctor 58 | //---------------------------------------------------------------------------------------------------------------------- 59 | ~TestNode(); 60 | 61 | private : 62 | //---------------------------------------------------------------------------------------------------------------------- 63 | /// @brief the zpos of our node input 64 | //---------------------------------------------------------------------------------------------------------------------- 65 | static MObject m_tx; 66 | //---------------------------------------------------------------------------------------------------------------------- 67 | /// @brief the output to our node 68 | //---------------------------------------------------------------------------------------------------------------------- 69 | static MObject m_output; 70 | 71 | //---------------------------------------------------------------------------------------------------------------------- 72 | 73 | }; 74 | //---------------------------------------------------------------------------------------------------------------------- 75 | 76 | #endif 77 | 78 | 79 | -------------------------------------------------------------------------------- /Sequence/src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "TestNode.h" 2 | #include 3 | 4 | //---------------------------------------------------------------------------------------------------------------------- 5 | 6 | MStatus initializePlugin( MObject obj ) 7 | { 8 | MStatus status; 9 | MFnPlugin plugin( obj, "", "NCCA" , "Any" ); 10 | 11 | // register our nodes and the commands which will be called 12 | status = plugin.registerNode( "TestNode", TestNode::m_id, &TestNode::creator, &TestNode::initialize, MPxNode::kDependNode ); 13 | if (!status) 14 | { 15 | status.perror("Unable to register TestNode" ); 16 | return status; 17 | } 18 | 19 | MGlobal::displayInfo("initialize plugin called"); 20 | return status; 21 | } 22 | 23 | //---------------------------------------------------------------------------------------------------------------------- 24 | 25 | MStatus uninitializePlugin( MObject obj ) 26 | { 27 | MStatus status; 28 | MFnPlugin plugin( obj ); 29 | 30 | 31 | status = plugin.deregisterNode( TestNode::m_id ); 32 | if (!status) 33 | { 34 | status.perror( "unable to deregister TestNode" ); 35 | return status; 36 | } 37 | MGlobal::displayInfo("uninitialize plugin called"); 38 | 39 | return status; 40 | } 41 | //---------------------------------------------------------------------------------------------------------------------- 42 | 43 | 44 | -------------------------------------------------------------------------------- /Sequence/src/TestNode.cpp: -------------------------------------------------------------------------------- 1 | #include "TestNode.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | //---------------------------------------------------------------------------------------------------------------------- 9 | /// @brief simple macro to check status and return if error 10 | /// originally written by Sola Aina 11 | //---------------------------------------------------------------------------------------------------------------------- 12 | #define CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( stat , message ) \ 13 | if( !status ) \ 14 | { \ 15 | MString errorString = status.errorString() + " -- " + MString( message ); \ 16 | MGlobal::displayError( errorString ); \ 17 | return MStatus::kFailure; \ 18 | } \ 19 | //---------------------------------------------------------------------------------------------------------------------- 20 | /// @brief simple macro to check status and return if error 21 | /// originally written by Sola Aina 22 | //---------------------------------------------------------------------------------------------------------------------- 23 | 24 | #define CHECK_STATUS_AND_RETURN_IF_FAIL( stat , message ) \ 25 | if( !status ) \ 26 | { \ 27 | MString errorString = status.errorString() + " -- " + MString( message ); \ 28 | MGlobal::displayError( errorString ); \ 29 | } \ 30 | 31 | /// @brief macro to get rid of compiler warnings 32 | #define UNUSED(arg) (void)arg; 33 | 34 | //---------------------------------------------------------------------------------------------------------------------- 35 | // as these are static class attributes we need to set them here so the static methods can see them 36 | // in particular see http://www.tutorialspoint.com/cplusplus/cpp_static_members.htm 37 | // "By declaring a function member as static, you make it independent of any particular object of the class. 38 | // A static member function can be called even if no objects of the class exist and the static functions are accessed using only 39 | // the class name and the scope resolution operator ::. 40 | // A static member function can only access static data member, other static member 41 | // functions and any other functions from outside the class. 42 | // Static member functions have a class scope and they do not have access 43 | // to the this pointer of the class. You could use a static member function to determine whether some 44 | // objects of the class have been created or not." 45 | //---------------------------------------------------------------------------------------------------------------------- 46 | MTypeId TestNode::m_id( 0x70003 ); // numeric Id of node 47 | const MString TestNode::typeName( "TestNode" ); 48 | MObject TestNode::m_output; 49 | MObject TestNode::m_tx; 50 | 51 | 52 | //---------------------------------------------------------------------------------------------------------------------- 53 | 54 | TestNode::~TestNode() 55 | { 56 | 57 | MGlobal::displayInfo("Node dtor called"); 58 | 59 | } 60 | 61 | //---------------------------------------------------------------------------------------------------------------------- 62 | // this method creates the attributes for our node and sets some default values etc 63 | //---------------------------------------------------------------------------------------------------------------------- 64 | MStatus TestNode::initialize() 65 | { 66 | 67 | MStatus status; 68 | MGlobal::displayInfo("Node initialize called"); 69 | 70 | 71 | // now we are going to add several number attributes 72 | MFnNumericAttribute numAttr; 73 | 74 | // now the position inputs 75 | m_tx = numAttr.create( "translateX", "tx", MFnNumericData::kDouble, 1.0, &status ); 76 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to create \"x pos \" attribute" ); 77 | // Add attribute to node 78 | status = addAttribute( m_tx ); 79 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to add \"tx\" attribute to TestNode" ); 80 | 81 | 82 | // create the output attribute 83 | m_output = numAttr.create( "output", "out",MFnNumericData::kDouble,0.0f); 84 | 85 | // make this attribute a read only output value 86 | numAttr.setReadable(true); 87 | numAttr.setKeyable(false); 88 | status = addAttribute( m_output ); 89 | CHECK_STATUS_AND_RETURN_MSTATUS_IF_FAIL( status , "Unable to add \"output\" attribute to TestNode" ); 90 | attributeAffects(m_tx,m_output); 91 | // report all was good 92 | return MStatus::kSuccess; 93 | } 94 | 95 | //---------------------------------------------------------------------------------------------------------------------- 96 | // This method should be overridden in user defined nodes. 97 | // Recompute the given output based on the nodes inputs. 98 | // The plug represents the data value that needs to be recomputed, and the data block holds the storage 99 | // for all of the node'_scale attributes. 100 | //---------------------------------------------------------------------------------------------------------------------- 101 | MStatus TestNode::compute( const MPlug &_plug , MDataBlock &_data ) 102 | { 103 | MStatus status; 104 | MGlobal::displayInfo("Node compute called"); 105 | 106 | // see if we get the output plug 107 | if( _plug == m_output) 108 | { 109 | 110 | return MStatus::kSuccess; 111 | } 112 | 113 | 114 | return MStatus::kUnknownParameter; 115 | } 116 | 117 | 118 | //---------------------------------------------------------------------------------------------------------------------- 119 | TestNode::TestNode() 120 | { 121 | MGlobal::displayInfo("Node ctor called"); 122 | 123 | } 124 | 125 | 126 | //---------------------------------------------------------------------------------------------------------------------- 127 | void* TestNode::creator() 128 | { 129 | MGlobal::displayInfo("Node creator called"); 130 | 131 | return new TestNode(); 132 | } 133 | //---------------------------------------------------------------------------------------------------------------------- 134 | 135 | 136 | --------------------------------------------------------------------------------