Optimizing MPxLocator draw method


#1

I created an MPxLocatorNode with Maya api in c++. The node doesn’t have a compute method, it just has different styles of shapes that is drawn with open gl in the draw method.

One style it draws is the cross, exactly the same as a locator. So I went to take some benchmarks to compare it to a native locator and noticed that my node’s performance is way slower. Maya’s locator was roughly 4x faster! Both benchmarks were done in the legacy viewport since I’m not drawing to viewport 2.0.

I started to comment out portions of the code to figure out what is causing the slowdown, and pinpointed it to glVertex3f. When I call this gl method performance nose dives. Why is this so much slower than Maya’s native locator drawing? Is there anything I can do to improve performance?

My cpp file is below.

#include "point.h"

MTypeId Point::id(0x00000900);

MObject Point::input_display;
MObject Point::input_box;
MObject Point::input_cross;
MObject Point::input_tick;
MObject Point::input_axis;
MObject Point::input_color;

MColorArray Point::colors;


Point::Point() {
}

void Point::postConstructor() {
	MObject self = thisMObject();
	MFnDependencyNode fn_node(self);
	fn_node.setName("pointShape#");
}

Point::~Point() {
}

void* Point::creator() {
	return new Point();
}

MStatus Point::initialize() {
	MFnNumericAttribute nAttr;

	input_display = nAttr.create("display", "display", MFnNumericData::kInt, 1);
	nAttr.setKeyable(true);
	nAttr.setMin(0);
	nAttr.setMax(1);
	addAttribute(input_display);

	input_box = nAttr.create("box", "box", MFnNumericData::kInt, 0);
	nAttr.setKeyable(true);
	nAttr.setMin(0);
	nAttr.setMax(1);
	addAttribute(input_box);
	
	input_cross = nAttr.create("cross", "cross", MFnNumericData::kInt, 1);
	nAttr.setKeyable(true);
	nAttr.setMin(0);
	nAttr.setMax(1);
	addAttribute(input_cross);

	input_tick = nAttr.create("tick", "tick", MFnNumericData::kInt, 0);
	nAttr.setKeyable(true);
	nAttr.setMin(0);
	nAttr.setMax(1);
	addAttribute(input_tick);

	input_axis = nAttr.create("axis", "axis", MFnNumericData::kInt, 0);
	nAttr.setKeyable(true);
	nAttr.setMin(0);
	nAttr.setMax(1);
	addAttribute(input_axis);
	
	MFnEnumAttribute eAttr;

	input_color = eAttr.create("color", "color", MFnData::kNumeric);
	
	eAttr.addField("Black", 0);
	eAttr.addField("Grey", 1);
	eAttr.addField("White", 2);
	eAttr.addField("Red", 3);
	eAttr.addField("Light red", 4);
	eAttr.addField("Dark red", 5);
	eAttr.addField("Green", 6);
	eAttr.addField("Light green", 7);
	eAttr.addField("Dark green", 8);
	eAttr.addField("Blue", 9);
	eAttr.addField("Light blue", 10);
	eAttr.addField("Dark blue", 11);
	eAttr.addField("Purple", 12);
	eAttr.addField("Magenta", 13);
	eAttr.addField("Brown", 14);
	eAttr.addField("Yellow", 15);
	eAttr.addField("Dark yellow", 16);
	eAttr.addField("Orange", 17);

	eAttr.setDefault(8);
	eAttr.setKeyable(true);
	eAttr.setStorable(true);
	addAttribute(input_color);

	colors.append(MColor(0.0f, 0.0f, 0.0f)); // black
	colors.append(MColor(0.5f, 0.5f, 0.5f)); // grey
	colors.append(MColor(1.0f, 1.0f, 1.0f)); // white
	colors.append(MColor(1.0f, 0.0f, 0.0f)); // red
	colors.append(MColor(1.0f, 0.6899999976158142f, 0.6899999976158142f)); // light_red
	colors.append(MColor(0.5f, 0.0f, 0.0f)); // dark_red
	colors.append(MColor(0.0f, 1.0f, 0.0f)); // green
	colors.append(MColor(0.5f, 1.0f, 0.5f)); // light_green
	colors.append(MColor(0.0f, 0.25f, 0.0f)); // dark_green
	colors.append(MColor(0.1889999955892563f, 0.6299999952316284f, 0.6299999952316284f)); // blue
	colors.append(MColor(0.3919999897480011f, 0.8629999756813049f, 1.0f)); // light_blue
	colors.append(MColor(0.0f, 0.01600000075995922f, 0.37599998712539673f)); // dark_blue
	colors.append(MColor(0.25f, 0.0f, 0.25f)); // purple
	colors.append(MColor(1.0f, 0.0f, 1.0f)); // magenta
	colors.append(MColor(0.75f, 0.2f, 0.0f)); // brown
	colors.append(MColor(1.0f, 1.0f, 0.0f)); // yellow
	colors.append(MColor(0.62117999792099f, 0.6299999952316284f, 0.1889999955892563f)); // dark_yellow
	colors.append(MColor(1.0f, 0.5f, 0.0f)); // orange

	return MS::kSuccess;
}

void Point::draw(M3dView& view, const MDagPath& mdag_path, 
				 M3dView::DisplayStyle display_style, 
				 M3dView::DisplayStatus display_status) {
	
	MObject self = thisMObject();
	
	int display = MPlug(self, input_display).asInt();
	if (display == 0) {
		return;
	}

	int use_box = MPlug(self, input_box).asInt();
	int use_cross = MPlug(self, input_cross).asInt();
	int use_tick = MPlug(self, input_tick).asInt();
	int use_axis = MPlug(self, input_axis).asInt();
	int color_index = MPlug(self, input_color).asInt();
	
	float tx = MPlug(self, Point::localPositionX).asFloat();
	float ty = MPlug(self, Point::localPositionY).asFloat();
	float tz = MPlug(self, Point::localPositionZ).asFloat();

	float sx = MPlug(self, Point::localScaleX).asFloat();
	float sy = MPlug(self, Point::localScaleY).asFloat();
	float sz = MPlug(self, Point::localScaleZ).asFloat();
	
	MColor color;
	switch (display_status) {
	case M3dView::kActive:
		color = MColor(1.0f, 1.0f, 1.0f);
		break;
	case M3dView::kLead:
		color = MColor(0.26f, 1.0f, 0.64f);
		break;
	case M3dView::kActiveAffected:
		color = MColor(0.783999979496f, 0.0f, 0.783999979496f);
		break;
	case M3dView::kTemplate:
		color = MColor(0.469999998808f, 0.469999998808f, 0.469999998808f);
		break;
	case M3dView::kActiveTemplate:
		color = MColor(1.0f, 0.689999997616f, 0.689999997616f);
		break;
	default:
		color = colors[color_index];
	}
	
	view.beginGL();
	
	if (use_axis == 1) {
		view.setDrawColor(MColor(1.0, 0, 0));
		view.drawText("x", MPoint(sx + tx, ty, tz), M3dView::kCenter);

		view.setDrawColor(MColor(0, 1.0, 0));
		view.drawText("y", MPoint(tx, sy + ty, tz), M3dView::kCenter);

		view.setDrawColor(MColor(0, 0, 1.0));
		view.drawText("z", MPoint(tx, ty, sz + tz), M3dView::kCenter);
	}

	glPushAttrib(GL_CURRENT_BIT);
	glEnable(GL_BLEND);
	glBegin(GL_LINES);

	if (use_box == 1) {
		glColor3f(color.r, color.g, color.b);

		// Top
		glVertex3f(-sx + tx, sy + ty, -sz + tz);
		glVertex3f(sx + tx, sy + ty, -sz + tz);

		glVertex3f(sx + tx, sy + ty, -sz + tz);
		glVertex3f(sx + tx, sy + ty, sz + tz);

		glVertex3f(sx + tx, sy + ty, sz + tz);
		glVertex3f(-sx + tx, sy + ty, sz + tz);

		glVertex3f(-sx + tx, sy + ty, sz + tz);
		glVertex3f(-sx + tx, sy + ty, -sz + tz);

		// Bottom
		glVertex3f(-sx + tx, -sy + ty, -sz + tz);
		glVertex3f(sx + tx, -sy + ty, -sz + tz);

		glVertex3f(sx + tx, -sy + ty, -sz + tz);
		glVertex3f(sx + tx, -sy + ty, sz + tz);

		glVertex3f(sx + tx, -sy + ty, sz + tz);
		glVertex3f(-sx + tx, -sy + ty, sz + tz);

		glVertex3f(-sx + tx, -sy + ty, sz + tz);
		glVertex3f(-sx + tx, -sy + ty, -sz + tz);

		// Left
		glVertex3f(-sx + tx, -sy + ty, -sz + tz);
		glVertex3f(-sx + tx, sy + ty, -sz + tz);

		glVertex3f(-sx + tx, sy + ty, -sz + tz);
		glVertex3f(-sx + tx, sy + ty, sz + tz);

		glVertex3f(-sx + tx, sy + ty, sz + tz);
		glVertex3f(-sx + tx, -sy + ty, sz + tz);

		glVertex3f(-sx + tx, -sy + ty, sz + tz);
		glVertex3f(-sx + tx, -sy + ty, -sz + tz);

		// Right
		glVertex3f(sx + tx, -sy + ty, -sz + tz);
		glVertex3f(sx + tx, sy + ty, -sz + tz);

		glVertex3f(sx + tx, sy + ty, -sz + tz);
		glVertex3f(sx + tx, sy + ty, sz + tz);

		glVertex3f(sx + tx, sy + ty, sz + tz);
		glVertex3f(sx + tx, -sy + ty, sz + tz);

		glVertex3f(sx + tx, -sy + ty, sz + tz);
		glVertex3f(sx + tx, -sy + ty, -sz + tz);
	}

	if (use_cross == 1) {
		glColor3f(color.r, color.g, color.b);

		glVertex3f(tx, -sy + ty, tz);
		glVertex3f(tx, sy + ty, tz);

		glVertex3f(-sx + tx, ty, tz);
		glVertex3f(sx + tx, ty, tz);

		glVertex3f(tx, ty, -sz + tz);
		glVertex3f(tx, ty, sz + tz);
	}

	if (use_tick == 1) {
		glColor3f(color.r, color.g, color.b);

		glVertex3f((-sx*0.05f) + tx, (sy*0.05f) + ty, tz);
		glVertex3f((sx*0.05f) + tx, (-sy*0.05f) + ty, tz);

		glVertex3f((sx*0.05f) + tx, (sy*0.05f) + ty, tz);
		glVertex3f((-sx*0.05f) + tx, (-sy*0.05f) + ty, tz);

		glVertex3f(tx, (sy*0.05f) + ty, (-sz*0.05f) + tz);
		glVertex3f(tx, (-sy*0.05f) + ty, (sz*0.05f) + tz);

		glVertex3f(tx, (sy*0.05f) + ty, (sz*0.05f) + tz);
		glVertex3f(tx, (-sy*0.05f) + ty, (-sz*0.05f) + tz);

		glVertex3f((sx*0.05f) + tx, ty, (-sz*0.05f) + tz);
		glVertex3f((-sx*0.05f) + tx, ty, (sz*0.05f) + tz);

		glVertex3f((sx*0.05f) + tx, ty, (sz*0.05f) + tz);
		glVertex3f((-sx*0.05f) + tx, ty, (-sz*0.05f) + tz);
	}

	if (use_axis == 1) {
		glColor3f(color.r, color.g, color.b);

		if (display_status == M3dView::kDormant) {
			glColor3f(1.0f, 0.0f, 0.0f);
		}
		glVertex3f(tx, ty, tz);
		glVertex3f(sx + tx, ty, tz);

		if (display_status == M3dView::kDormant) {
			glColor3f(0.0f, 1.0f, 0.0f);
		}
		glVertex3f(tx, ty, tz);
		glVertex3f(tx, sy + ty, tz);

		if (display_status == M3dView::kDormant) {
			glColor3f(0.0f, 0.0f, 1.0f);
		}
		glVertex3f(tx, ty, tz);
		glVertex3f(tx, ty, sz + tz);
	}

	glEnd();
	glDisable(GL_BLEND);
	glPopAttrib();

	view.endGL();
}

#2

Answer on Stack Overflow:
http://stackoverflow.com/questions/36223078/optimizing-mpxlocatornode-draw/36382742#36382742


#3

Thanks for posting your findings. :thumbsup:

David


#4

No problem. The next level to make this faster would be to draw to viewport 2.0, so if anyone has any good examples to share then please do. All the ones I found on the web or documentation are overwhelming to understand.


#5

For VP2 and simple locator drawing, have a look at MPxDrawOverride and MUIDrawManager.

You’ll probably want to look at the footPrintNode.cpp example. Yes it can be confusing, but just focus on the MPxDrawOverride implementation which should be a pretty close map to what you’re doing already. Just look at the MUIDrawManager as your portal for drawing simple primitives in VP2.

At a high level, what an override means is that VP2 has a base drawing implementation for all nodes in Maya. There’s an implementation to render a standard mesh or NURBS. However the default implementation for all plug-in shapes is a no-op. Overrides are registered to override the base VP2 implementation. So you need to create your override (DrawOverride) and register against your locator implementation.

Have a look and post questions as you go. That’s probably the best way to get started.