top of page
Writer's pictureDilen Shah

Viewport 2.0 and Drawing with OpenGL in Maya

Updated: Jul 7, 2021

Hey guys,


Its been a while since I have posted something here as a lot has happened in the past few months for me. Moved to another country, started a new job and all that stuff.


So let’s get down to business. I have always been interested in drawing in Viewport 2.0 especially in Maya using OpenGL, but when i tried to find some references on things they were kind of limited especially with Maya using Viewport 2.0 as default renderer. So I thought of making this post to see what has changed and how to draw in Legacy and Viewport 2.0 using OpenGL.


Firstly let’s see this in Legacy Viewport in Maya (I will be showing the code in Python but this could be duplicated into C++ as well):


So before in python you could use the OpenMaya modules the following way


import maya.OpenMaya as OpenMaya
import maya.OpenMayaUI as OpenMayaUI
import maya.OpenMayaAnim as OpenMayaAnim
import maya.OpenMayaRender as OpenMayaRender

But now for Viewport 2.0 and newer version of Maya, i think from 2016(may be 2015 not sure as I have never used it) we need to use the above as well this new import system and it looks something like this:


import maya.api.OpenMaya as OpenMaya
import maya.api.OpenMayaUI as OpenMayaUI
import maya.api.OpenMayaAnim as OpenMayaAnim
import maya.api.OpenMayaRender as OpenMayaRender

This way you can use the older OpenMaya and the newer version as well in the same node, plugin that you are writing. Best of both worlds, i guess.

So now to the drawing part, firstly lets see which functions you will use all the time:


#Node implementation with standard viewport draw
class TestNode(OpenMayaUI.MPxLocatorNode):
    id = OpenMaya.MTypeId( 0x82307 )
    drawDbClassification = "drawdb/geometry/TestNode"
    drawRegistrantId = "TestNodePlugin"
 
    @staticmethod
    def creator():
        return TestNode()
 
    @staticmethod
    def initialize():
        pass
 
    def __init__(self):
        OpenMayaUI.MPxLocatorNode.__init__(self)
 
    def compute(self, plug, data):
        return None
 
    def draw(self, view, path, style, status):
        return None

Many of the API classes have changed their API modules, for example MPxLocatorNode used to in OpenMayaMPx but now is in OpenMayaUI and many more. They have removed the whole OpenMayaMPx class i think as I couldn’t find any documentation on it. Anyways lets move on.


Lets see the same in Viewport 2.0 Integeration now:


## Viewport 2.0 override implementation
 
def maya_useNewAPI():
 """
 The presence of this function tells Maya that the plugin produces, and
 expects to be passed, objects created using the Maya Python API 2.0.
 """
 pass
class TestNodeData(OpenMaya.MUserData):
    def __init__(self):
        OpenMaya.MUserData.__init__(self, False) ## don't delete after draw
 
class TestNodeDrawOverride(OpenMayaRender.MPxDrawOverride):
    @staticmethod
    def creator(obj):
        return TestNodeDrawOverride(obj)
 
    @staticmethod
    def draw(context, data):
        return
 
    def __init__(self, obj):
        OpenMayaRender.MPxDrawOverride.__init__(self, obj, TestNodeDrawOverride.draw)
 
    def supportedDrawAPIs(self):
        ## this plugin supports both GL and DX
        return OpenMayaRender.MRenderer.kOpenGL | OpenMayaRender.MRenderer.kDirectX11 | OpenMayaRender.MRenderer.kOpenGLCoreProfile
 
    def prepareForDraw(self, objPath, cameraPath, frameContext, oldData):
        ## Retrieve data cache (create if does not exist)
        data = oldData
        if not isinstance(data, NodenameData):
            data = TestNodeData()
 
        return data
 
    def hasUIDrawables(self):
        return True
 
    def addUIDrawables(self, objPath, drawManager, frameContext, data):
        locatordata = data
        if not isinstance(locatordata, TestNodeData):
            return

As you can see the functions are completely different, the introduction of MPxDrawOverride which draws stuff in Viewport 2.0. Go through the code as I have added comments for functions and why they are used. Do comment if you have questions.


Alright now lets get down and dirty and start drawing stuff in Viewport. First of course lets draw in the Legacy Viewport using the “draw” function. I am writing all the classes, modules and functions that I usually use in OpenGL drawing as well, you don’t need to use all of them but use them correctly to your specifications:


# Legacy Viewport Draw Function
def draw(self, view, path, style, status):
 
    # Getting the OpenGL renderer
    glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer()
    # Getting all classes from the renderer
    glFT = glRenderer.glFunctionTable()
 
     # Pushed current state
    glFT.glPushAttrib( OpenMayaRender.MGL_CURRENT_BIT )
    # Enabled Blend mode (to enable transparency)
    glFT.glEnable( OpenMayaRenderv1.MGL_BLEND )
    # Defined Blend function
    glFT.glBlendFunc( OpenMayaRender.MGL_SRC_ALPHA, OpenMayaRender.MGL_ONE_MINUS_SRC_ALPHA )
    # create x-ray view and will be seen always
    glFT.glDisable( OpenMayaRender.MGL_DEPTH_TEST )
 
    # Starting the OpenGL drawing
    view.beginGL()
 
    # Getting the active viewport
    activeView = view.active3dView()
 
    # Setting a color for Viewport draw
    view.setDrawColor( OpenMaya.MColor( (1.0, 1.0, 1.0, 1.0) ) )
 
    # Writing text on 3D space
    view.drawText("3D SPACE TEXT", OpenMaya.MPoint(0,1,0), OpenMayaUI.M3dView.kCenter )
 
    # Getting the near and far plane for the viewport
    textPositionNearPlane = OpenMaya.MPoint()
    textPositionFarPlane = OpenMaya.MPoint()
 
    # Setting a color for Viewport draw
    view.setDrawColor( OpenMaya.MColor( (0.5, 0.3, 0.4, 1.0) ) )
 
    # Writing text in 2D space(drawing on the viewport plane)
    activeView.viewToWorld(500, 500, textPositionNearPlane, textPositionFarPlane )
    activeView.drawText("2D SPACE TEXT", textPositionNearPlane, OpenMayaUI.M3dView.kLeft )
 
    # Drawing Image on Viewport
    image = OpenMaya.MImage()
    # Import image from a path
    image.readFromFile(IMAGEPATH)
    # Writing the image on viewport with the given x, y coordinates
    view.writeColorBuffer(image, 100, 100)
 
    # Disable Blend mode
    glFT.glDisable( OpenMayaRender.MGL_BLEND )
    glFT.glEnable( OpenMayaRender.MGL_DEPTH_TEST )
    # Restore the state
    glFT.glPopAttrib()
    # Ending the OpenGL drawing
    view.endGL()

In the above example, you can learn how to get the render information, set color, write text and draw images, the same can be done for drawing lines and shapes(check documentation for their functions). Here is an image of what it looks like.


So as you can see the “3D SPACE TEXT” is appearing location (0, 1, 0) and “2D SPACE TEXT” is appearing in 2D space of coordinates of (500, 500) and also an alpha image due to the use of the MGL.BLEND.


Now the same thing we need to do for Viewport 2.0 as follows:


# Viewport 2.0 addUIDrawables(Draw) Function
def addUIDrawables(self, objPath, drawManager, frameContext, data):
        locatordata = data
        if not isinstance(locatordata, TestNodeData):
            return
        drawManager.beginDrawable()
 
        textColor = OpenMaya.MColor((1.0,1.0,1.0, 1.0))
        drawManager.setColor( textColor )
 
        drawManager.text( OpenMaya.MPoint(0, 1, 0), "3D SPACE TEXT", OpenMayaRender.MUIDrawManager.kLeft )
 
        textColor = OpenMaya.MColor((0.5, 0.3, 0.4, 1.0))
        drawManager.setColor( textColor )
 
        drawManager.text2d( OpenMaya.MPoint(500, 500), "2D SPACE TEXT", OpenMayaRender.MUIDrawManager.kLeft )
 
        drawManager.endDrawable()

And here is the image of how it looks as well:


The one thing I haven’t been able to figure out is drawing images in Viewport 2.0 as the “writeColorBuffer” function is obsolete in Viewport 2.0 and if you even try doing it, Maya crashes so no point forcing it. Will update about it once I figure that out.


(UPDATE: I had sent an email to Autodesk and have got the reply that there is a problem with writeColorBuffer and they have sent the information to the Engineering team and will get back to me with work-around or a Solution)


Lastly the initializePlugin and uninitializePlugin functions which also is little different and looks like this:


def initializePlugin(obj):
    plugin = OpenMaya.MFnPlugin(obj, "Dilen Shah", "1.0", "Any")
 
    try:
        plugin.registerNode("TestNode", TestNode.id, TestNode.creator, TestNode.initialize, OpenMaya.MPxNode.kLocatorNode, TestNode.drawDbClassification)
    except:
        sys.stderr.write("Failed to register node\n")
        raise
 
    try:
        OpenMayaRender.MDrawRegistry.registerDrawOverrideCreator(TestNode.drawDbClassification, TestNode.drawRegistrantId, TestNodeDrawOverride.creator)
    except:
        sys.stderr.write("Failed to register override\n")
        raise
 
def uninitializePlugin(obj):
    plugin = OpenMaya.MFnPlugin(obj)
 
    try:
        plugin.deregisterNode(TestNode.id)
    except:
        sys.stderr.write("Failed to deregister node\n")
        pass
 
    try:
        OpenMayaRender.MDrawRegistry.deregisterDrawOverrideCreator(TestNode.drawDbClassification, TestNode.drawRegistrantId)
    except:
        sys.stderr.write("Failed to deregister override\n")
        pass

So here is a brief overview of what you can achieve in writing and drawing on the Legacy and Viewport 2.0 in Maya. If you have questions or comments, please do let me know.


Thanks and until next time.


Recent Posts

See All

Contact me

so we can create creative & technical projects together
  • Github
  • LinkedIn
  • Instagram
  • Vimeo
  • YouTube
Join my mailing list

Thanks for submitting!

© 2023 by Dilen Shah

bottom of page