How can I use CPP algorithm library when writing a Maya CPP plugin

112 Views Asked by At

I am writing a C++ plugin for Maya using CLion and CMake. When I add the C++ algorithm library to my code I get an error saying "No member named 'clamp' in namespace 'std'".

As soon I add the build_plugin line to the CMakeLists.txt that error pops up.

How can I add the C++ algorithm library?

Here is a simple test:

CMakeLists.txt

cmake_minimum_required(VERSION 3.26)
project(clamp_test)

set(CMAKE_CXX_STANDARD 17)

# define where Maya's devkitBase and its lib and include directories are
set(ENV{DEVKIT_LOCATION} "/user_data/Dev/devkitBase")

set(DEVKIT_INCLUDE_DIR $ENV{DEVKIT_LOCATION}/include)
set(DEVKIT_LIBRARY_DIR $ENV{DEVKIT_LOCATION}/lib)

# include the project setting file
include($ENV{DEVKIT_LOCATION}/cmake/pluginEntry.cmake)

set(SOURCE_FILES main.cpp)

# set linking libraries
set(LIBRARIES OpenMaya Foundation)

# build plugin
build_plugin()

main.cpp

#include <algorithm>
#include <cstdint>
#include <iomanip>
#include <iostream>

int main()
{
  std::cout << " raw   clamped to int8_t   clamped to uint8_t\n";
  for (const int v : {-129, -128, -1, 0, 42, 127, 128, 255, 256})
  {
    std::cout
        << std::setw(04) << v
        << std::setw(20) << std::clamp(v, INT8_MIN, INT8_MAX)
        << std::setw(21) << std::clamp(v, 0, UINT8_MAX) << '\n';
  }
}

Below is the project settings file pluginEntry.cmake

# This cmake file builds plugins both in Maya and devkit

# The option to determian the plugin is application or library
option(IS_APPLICATION "is Application not plugin" OFF)

# Plugin flags on different platforms
if(WIN32)
    add_definitions(-DNT_PLUGIN)
elseif(APPLE)
    add_definitions(-DMAC_PLUGIN)
endif()

set(PLUGIN_CMAKE_ROOT ${CMAKE_CURRENT_LIST_DIR})

if (INSIDE_MAYA)
    # for CMakeLists.txt for devkit, the flag is LINUX
    if (PEPTIDE_IS_LINUX)
        set(LINUX TRUE)
    else()
        set(LINUX FALSE)
    endif()

    if (PEPTIDE_IS_OSX)
        maya_get_build_path(frameworkDir frameworks)
    endif()
    
    if(WANT_DUAL_PYTHON OR WANT_PYTHON_3)
        set(IS_PYTHON_3_BUILD ON)
    else()
        set(IS_PYTHON_3_BUILD OFF)
    endif()

else() 
    # Include the cmake file for the compilatin options to build devkit
    include(${CMAKE_CURRENT_LIST_DIR}/devkit.cmake)
    option(IS_PYTHON_3_BUILD "Target the Python 3 devkit files" ON)
endif()

if(IS_PYTHON_3_BUILD)
    set(PY_VER_A 3)
    set(PY_VER_AB 37)
else()
    set(PY_VER_A 2)
    set(PY_VER_AB 27)
endif()

# Find libxml2
macro(find_libxml2)
    if(WIN32)
        if (NOT INSIDE_MAYA)
            set(INCLUDE_DIRS $ENV{DEVKIT_LOCATION}/include/libxml)
        endif()
        set(LIBRARIES ${LIBRARIES} awxml2) 
    else()
        find_package(LibXml2 REQUIRED)
        if (LIBXML2_FOUND)
            include_directories(${LIBXML2_INCLUDE_DIR})
            set(PACKAGE_LIBS ${PACKAGE_LIBS} ${LIBXML2_LIBRARIES})
        endif()
    endif()
endmacro()

# Find OpenGL
macro(find_opengl)
    find_package(OpenGL REQUIRED) 
    if (OPENGL_FOUND) 
        set(INCLUDE_DIRS ${INCLUDE_DIRS} ${OPENGL_INCLUDE_DIR})
        set(PACKAGE_LIBS ${PACKAGE_LIBS} ${OPENGL_LIBRARIES})
    endif()
endmacro()

# Find glew
macro(find_glew)
    if (INSIDE_MAYA)
        set(GLEW_LIBRARIES glew )
        set(INCLUDE_DIRS ${INCLUDE_DIRS} $ENV{DEVKIT_LOCATION}/include/glew)
        set(PACKAGE_LIBS ${PACKAGE_LIBS} ${GLEW_LIBRARIES})
        add_definitions( -DMAYA_PLUGIN_USES_GLEW )
        set( GLEW_FOUND ON )
    else()
        find_package(GLEW)
        if (GLEW_FOUND)
            set(INCLUDE_DIRS ${INCLUDE_DIRS} ${GLEW_INCLUDE_DIRS})
            set(PACKAGE_LIBS ${PACKAGE_LIBS} ${GLEW_LIBRARIES})
            add_definitions( -DMAYA_PLUGIN_USES_GLEW )
        else()
            remove_definitions( -DMAYA_PLUGIN_USES_GLEW )
        endif()
    endif()
endmacro()

# Find zlib
macro(find_zlib)
    if(WIN32)
        set(LIBRARIES ${LIBRARIES} zlib)
    else()
        find_package(ZLIB REQUIRED)
        if (ZLIB_FOUND)
            include_directories(${ZLIB_INCLUDE_DIR})
            set(PACKAGE_LIBS ${PACKAGE_LIBS} ${ZLIB_LIBRARIES})
        endif()
    endif()
endmacro()

# Find Alembic
macro(find_alembic)
    if (INSIDE_MAYA)
            set(ALEMBIC_LIBRARIES Alembic AlembicHalf AlembicIex AlembicImath)
            if (PEPTIDE_IS_OSX)
                set(ALEMBIC_LIBRARIES ${ALEMBIC_LIBRARIES} hdf5_hl hdf5)
            endif()
            set(LIBRARIES ${LIBRARIES} ${ALEMBIC_LIBRARIES})
    else()
        find_package(Alembic REQUIRED)
        if (ALEMBIC_FOUND)
            set(INCLUDE_DIRS ${INCLUDE_DIRS} ${ALEMBIC_INCLUDE_DIRS})
            set(PACKAGE_LIBS ${PACKAGE_LIBS} ${ALEMBIC_LIBRARIES})
        endif()
    endif()
endmacro()

# Find Arnold
macro(find_arnold)
    find_package(Arnold REQUIRED)
    if (ARNOLD_FOUND)
        include_directories(${ARNOLD_INCLUDE_DIR})
        if (INSIDE_MAYA)
            set(PACKAGE_LIBS ${PACKAGE_LIBS} ai)
        else()
            set(PACKAGE_LIBS ${PACKAGE_LIBS} ${ARNOLD_LIBRARY})
        endif()
    endif()
endmacro()

# Find TBB
macro(find_tbb)
    if (INSIDE_MAYA)
        set(PACKAGE_LIBS ${PACKAGE_LIBS} ${MAYA_TBB_IMPORTLIB})
        set(LIBRARIES ${LIBRARIES} TBB)
    else()
        set(LIBRARIES ${LIBRARIES} tbb)
    endif()
endmacro()

# Find DirectX libraries
macro(find_directX libs)
    find_package(DirectXSDK REQUIRED)

    if(DIRECTXSDK_FOUND)
        set(INCLUDE_DIRS ${INCLUDE_DIRS} ${DIRECTXSDK_INCLUDE_DIR})
        foreach(lib ${libs})
            set(PACKAGE_LIBS ${PACKAGE_LIBS} DirectXSDK::${lib})
        endforeach()
    endif()
endmacro()

# Add cg and CgGL libraries
macro(find_cg)
    if (WIN32)
        set(LIBRARIES ${LIBRARIES} cg cgGL)
    elseif(LINUX)
        set(LIBRARIES ${LIBRARIES} Cg CgGL)
        set(PACKAGE_LIBS ${PACKAGE_LIBS} "-lX11 -lXext -DGLX_GLXEXT_PROTOTYPES")
    elseif(APPLE)
        if (INSIDE_MAYA)
            maya_get_build_path(frameworkDir frameworks)
        else()
            set(frameworkDir ${DEVKIT_LIBRARY_DIR})
        endif()
        set(INCLUDE_DIRS ${frameworkDir}/Cg.framework)
        set(PACKAGE_LIBS ${PACKAGE_LIBS} "-F${frameworkDir} -framework Cg")
    endif()
endmacro()

# Find boost
macro(find_boost)
    if (INSIDE_MAYA)
        #TODO: PEPTIDE boost include
        set(INCLUDE_DIRS ${INCLUDE_DIRS} ${MAYA_BOOST_INCLUDE_DIR})
    else()
        #TODO: PEPTIDE boost include for the devkit
    endif()
endmacro()


# Find D3DX11Effects lib
macro(find_D3DX11Effects)
    find_package(D3DX11Effects REQUIRED)

    if(D3DX11EFFECTS_FOUND)
        set(INCLUDE_DIRS ${INCLUDE_DIRS} ${D3DX11EFFECTS_INCLUDE_DIR})
        set(PACKAGE_LIBS ${PACKAGE_LIBS} DirectXSDK::D3DX11Effects)
        
        add_definitions(-DUSE_D3DX11EFFECTS=1)
        if(DIRECTXSDK_D3DX11EFFECTS_FOUND)
            add_definitions(-DUSE_DIRECTXSDK_D3DX11EFFECTS=1)
        endif()
    endif()
endmacro()

# Function to build plugin application
function(build_application)
    set(IS_APPLICATION ON)
    build_plugin()
endfunction()

    
# Function to build plugin
function(build_plugin)
    # Info:
    # INCLUDE_DIRS  -   header search directories
    # LIBRARY_DIRS  -   library search directories
    # LIBRARIES     -   library names to link
    # PACKAGE_LIBS  -   libraries in the packages of different platforms
    # Most recent project name will be used for target name
    # Maya libraries and headers are automatically included
    
    set(project ${PROJECT_NAME})
    
    
    if (INSIDE_MAYA)
        # The cmake file to build plugin in maya
        include(${PLUGIN_CMAKE_ROOT}/buildPlugin.cmake)
    else()
        # Build plugin in devkit
        list(APPEND INCLUDE_DIRS $ENV{DEVKIT_LOCATION}/include $ENV{DEVKIT_LOCATION}/include/Python${PY_VER_AB})
        
        include_directories(${INCLUDE_DIRS})

        if (IS_APPLICATION)
            if(LINUX)
                include(${PLUGIN_CMAKE_ROOT}/mayald.cmake)
            endif()
            add_executable(${PROJECT_NAME} ${SOURCE_FILES})
        else()
            add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
        endif()
        
        set(LIBRARY_DIRS ${LIBRARY_DIRS} ${DEVKIT_LIBRARY_DIR})
        foreach(MAYA_LIB ${LIBRARIES})
            find_library(${MAYA_LIB}_PATH NAMES ${MAYA_LIB} PATHS ${LIBRARY_DIRS} NO_DEFAULT_PATH)
            set(MAYA_LIBRARIES ${MAYA_LIBRARIES} ${${MAYA_LIB}_PATH})
        endforeach(MAYA_LIB)
        
        if (PACKAGE_LIBS)
            set(MAYA_LIBRARIES ${MAYA_LIBRARIES} ${PACKAGE_LIBS})
        endif()
        
        target_link_libraries(${PROJECT_NAME} ${MAYA_LIBRARIES})
        
        if(NOT IS_APPLICATION)
            set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
            set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX "${SUFFIX_NAME}")
        endif()
    
    endif()

    
endfunction()
1

There are 1 best solutions below

0
robthebloke On

First a couple of questions....

  1. What version of Maya are you using?
  2. What OS are you using?
  3. Which compiler are you using?

For Maya plugin development, you should in an ideal world be making sure your dev environment matches the VFX reference platform specs, see here: https://vfxplatform.com

Given that Maya is behind a DSO/DLL boundary, if you don't match the reference platform, there is a danger you can cause a data structure mismatch which can cause some nasty crashes. (That being said, Maya is pretty resilient to these problems - e.g. they use MIntArray instead of std::vector)

In theory Maya 2021 and above should be using C++17 by default (2020 would be locked at C++14). So long as you have the correct compiler version, you should be good??

Next up, this is not going to work with Maya. Your license server will crap out pretty damned quickly!!

#include <algorithm>
#include <cstdint>
#include <iomanip>
#include <iostream>

int main()
{
  std::cout << " raw   clamped to int8_t   clamped to uint8_t\n";
  for (const int v : {-129, -128, -1, 0, 42, 127, 128, 255, 256})
  {
    std::cout
        << std::setw(04) << v
        << std::setw(20) << std::clamp(v, INT8_MIN, INT8_MAX)
        << std::setw(21) << std::clamp(v, 0, UINT8_MAX) << '\n';
  }
}

If you want to have your own main() entry point, you're going to need to link against MLibrary (which gives you access to the Maya internals, without any MEL/python or UI support), however that's probably not what you want.

Personally when I've done Maya API development in the past, I've completely ignored their cmake stuff. It pretty much goes through and links every single library that you don't need, into your plugins. This is typically pointless for 99% of use cases.

Just set your compiler path to maya/devkit/include, and set your library path to maya/devkit/lib (Depending on where you have installed it). For most plugins you only need to link to Foundation and OpenMaya (possibly OpenMayaUI and GL/GLU for any locator node that renders in the viewport, and possibly OpenMayaFx if you are adding custom particle emitters/fields). For standalone CLI apps, you'll be wanting to also link to MLibrary

An extremely minimal plugin would look like this:

// plugin.cpp

// IIRC, this file must only be included once... 
#include <maya/MFnPlugin.h>
#include <Maya/MGlobal.h>

#ifdef WIN32
    #define EXPORT __declspec(dllexport)
#else
    #define EXPORT
#endif

/// this is called when your plugin is loaded
EXPORT MStatus initializePlugin(MObject obj) {
    MStatus status = MS::kSuccess;

    MFnPlugin plugin(obj, "MyPluginName", "1.0", "Any");

    // now register you custom nodes/commands/etc with Maya.
    // Checkout the Maya docs for MFnPlugin.

    MGlobal::displayInfo("Hello World!"); 

    return status;
}

/// this is called when your plugin is unloaded
EXPORT MStatus uninitializePlugin(MObject obj)
{
    MFnPlugin plugin(obj);

    // now unregister you custom nodes/commands/etc with Maya.

    MGlobal::displayInfo("Bye bye World!"); 

    return status;
}

Make sure you compile the library using -shared and -fPIC on linux/Mac, and make sure the output file extension is *.mll. e.g. something like this:

g++ -Imaya/devkit/include/ -Lmaya/devkit/lib/ 
    -lFoundation -lOpenMaya 
    -std=c++17
    -shared -fPIC
    -o plugin.mll
    plugin.cpp

You should be able to load the plugin at that point (although given that it doesn't register anything, it won't be that much use!)

A simple command line app would look like:

#include<iostream>
#include<maya/MItDependencyNodes.h>
#include<Maya/MFnDependencyNode.h>
#include<maya/MLibrary.h>
#include<maya/MFileIO.h>

int main(int argc,char** argv)
{
   // initialise the maya library
   MLibrary::initialize(argv[0]);

   // use the second arg as the .mb/.ma to open.
   if(MFileIO::open(argv[1]) == MS::kSuccess ) {

      // create an iterator to go through all nodes
      MItDependencyNodes it(MFn::kInvalid);
      while(!it.isDone()) {
          // get a handle to this node
          MObject obj = it.item();

          // attach function set to get useful info... 
          MFnDependencyNode fn(obj);

          // write the node type found
          std::cout << obj.apiTypeStr() << ' ' 
                    << fn.name().asChar() << std::endl;

          // move on to next node
          it.next();
      }
   }

   // cleanup maya
   MLibrary::cleanup();
   return 0;
}
g++ -Imaya/devkit/include/ -Lmaya/devkit/lib/ 
    -lFoundation -lOpenMaya -lLibrary
    -std=c++17
    -o my_tool
    main.cpp

.... and finally.

I wrote this doc - https://nccastaff.bournemouth.ac.uk/jmacey/OldWeb/RobTheBloke/www/ - about 20 years ago, but it's all still relevant. I'd recommend starting with an exporter, just to familiarise yourself with how the Scene data is structured. Once you have a rough idea, move onto adding a basic MPxNode (e.g. one that adds two numbers, or something boring). My old notes won't help too much in that regards (The only things that have changed in maya over the last 20 years are the way nodes are computed, and the way that nodes get rendered in the viewport - you'd probably want to hunt through the devkit samples for the newer approaches - although the old stuff will still work if you disable viewport 2)

Hope that helps....