Example cmd sy partifymesh

From The Foundry MODO SDK wiki
Revision as of 06:27, 30 May 2012 by Synide (Talk | contribs) (Code: Forgot the stub...)

Jump to: navigation, search


This article details in laymans terms adding a 'new' command to modo. It has been written by a sdk end user, so may or may not be representative of 'good' or even accurate information.


It's good practice to note down a few details describing the scope of what your command does, when it does it and how it may be activated so as to give yourself some guidelines to work too. So...

    1. What should this command do?
      This command should 'apply a unique part tag to each polygon in the currently selected mesh'.

    2. What should this command be called?
      Based on what the command is supposed to do and whom has made it a good name for this command would be 'sy.partifymesh'.

    3. When should this command be allowed to run?
      This command should only be 'enabled' when the user has a 'mesh' item selected AND that mesh item has some polygons.

    4. Is the command going to possibly run from a button?
      For this particular example we'll make the command suitable for running from a button. So, we'll detail help information and images that will automatically apply when the command is mapped to a button.



A basic 'code flow' summary follows, it's not in any particular order of operation :-

  • The module's initialize method is called which in turn calls the namespace's initialize method.
  • The namespace initialize registers the 'server' with appropriate interfaces for this plugin.
  • When the command is activated within modo the constructor sets a CLxUser_Log.
  • The runtime acquires the flags from this plugin via the basic_CmdFlags method.
  • The runtime calls the basic_Enable method to see if this command can be run or not.
  • The runtime calls into basic_AddNotifiers, which in turn essentially registers what events this plugin will recieve notification for.
  • Finally, the plugin's cmd_Execute method is called.


/* synide@rocketmail.com */
#include <lx_plugin.hpp>
namespace sy_partifymesh_cmd			{	extern void initialize();	extern void cleanup();	};
void initialize() {
	sy_partifymesh_cmd			::initialize();
void cleanup() {
	sy_partifymesh_cmd			::cleanup();


             April, 2012 - Revised for Liki example.
             May, 2012 - A More comprehensive implementation.
#include <time.h>
#include <string>
#include <sstream>
#include <lxidef.h>
#include <lx_plugin.hpp>
#include <lx_layer.hpp>
#include <lx_mesh.hpp>
#include <lx_log.hpp>
#include <lxu_command.hpp>
#include <lxu_select.hpp>
namespace sy_partifymesh_cmd
static char *SERVER_NAME              = "sy.partifymesh";
static char *SERVER_USERNAME       = "Sy PartifyMesh";
static char *LOG_NAME                   = "sy/cmds/partifymesh";
static CLxItemType                         iType_Mesh         (LXsITYPE_MESH);
std::string fnI2S(unsigned value, bool notnullterminated = true, unsigned padding = 0, bool asHexadecimal = false, bool withHexPrefix = false) {
          Integer to string, a variant of David Vasquez's Integer2String
    std::ostringstream os;
    std::string result;
    if (asHexadecimal) {os << std::hex;}
    if (value < 10 && padding) {os << 0;}
    if (value < 100 && padding > 1) {os << 0;}
    if (value < 1000 && padding > 2) {os << 0;}
    if (notnullterminated)
        os << value;
        os << value << std::ends;
    if (asHexadecimal && withHexPrefix)
        result = std::string("0x");
    result += os.str();
    return result;
/* 'sy.partifymesh' command is derived from 'CLxBasicCommand'. */
class CSyPartifyMesh : public CLxBasicCommand {
	static LXtTagInfoDesc           descInfo[];
	CLxUser_LayerService            LayerService;
	CLxUser_Log                        PartifyMeshLog;
	CSyPartifyMesh() {
	void basic_Notifiers() override {
		/* Will be notified when Item selection changes */
		basic_AddNotify(LXsNOTIFIER_SELECT, "item +d");
	bool basic_Enable(CLxUser_Message &msg) override {
			Utilize the new (601) CLxItemSelectionType to find the first mesh item and then check if it
			has any polygons.
		bool result(false);
		CLxItemSelectionType          TypedItemSelector(LXsITYPE_MESH);
		CLxUser_Item                      thisItem;
		if (TypedItemSelector.GetFirst(thisItem) && thisItem.test())
			if (thisItem.Type() == iType_Mesh.Type())
				CLxUser_Mesh			thisMesh;
				CLxUser_ChannelRead		aChanReader;
				if (aChanReader.from(thisItem))
					if (aChanReader.Object(thisItem, LXsICHAN_MESH_MESH, thisMesh))
						result = thisMesh.NPolygons() > 0;
		return result;
	int basic_CmdFlags() override {
		return LXfCMD_UNDO;
	void cmd_Execute(unsigned int flags) override {
		    We restrict the LayerScan's results to the 'Primary' selected mesh only so that we
		    guard against the user having multiple 'selected' meshs which could result in overload.
		CLxUser_LayerScan               LayerScan;
		CLxUser_Item                    theItem;
		CLxUser_Mesh                    theMesh;
		CLxUser_Polygon                 thePolygon;
		CLxUser_StringTag               theTag;
		const char                      *ItemName;
		if (LayerScan.ItemByIndex(0, theItem))
		/* As there should only ever be 1 mesh found by the LayerScan it's index should be zero. */
		if (LayerScan.EditMeshByIndex(0, theMesh))
			int NoOfPolygons = theMesh.NPolygons();
			/* Only do work if there are polygons. */
			if (NoOfPolygons > 0)
				/* Set 'thePolygon' object as the polygon accessor for the 'theMesh' object. */
				/* Set 'theTag' object as the 'tags' accessor for the polygon object. */
				std::string tag;
				unsigned msg_stride = NoOfPolygons / 5;
				unsigned msg_counter = 1;
				clock_t timerStart = clock();
				for (int i1 = 0; i1 < NoOfPolygons; i1++)
					/* Ask the polygon accessor to populate itself based on the poly index. */
					/* Construct a unique 'part' tag name. */
					tag = "poly_" + fnI2S(i1);
					/* Ask the 'tags' accessor to set the 'PART' tag. */
					theTag.Set(LXi_PTAG_PART, tag.c_str());
					/* logging at 20%, 40%, 60%, 80% */
					if (PartifyMeshLog && (i1 == (msg_stride * msg_counter)))
						clock_t timerDiff = clock() - timerStart;
						double duration = (double)timerDiff / (double)CLOCKS_PER_SEC;
						case 1:
							PartifyMeshLog.Message(LXe_INFO, "20\%%, %u polygons in %.6f seconds.", i1, duration);
						case 2:
							PartifyMeshLog.Message(LXe_INFO, "40\%%, %u polygons in %.6f seconds.", i1, duration);
						case 3:
							PartifyMeshLog.Message(LXe_INFO, "60\%%, %u polygons in %.6f seconds.", i1, duration);
						case 4:
							PartifyMeshLog.Message(LXe_INFO, "80\%%, %u polygons in %.6f seconds.", i1, duration);
						msg_counter += 1;
				/* Tell the LayerScan that we are only 'editing' polygon tags. */
				LayerScan.SetMeshChange(0, LXf_MESHEDIT_POL_TAGS);
				/* Apply the mesh changes. */
				clock_t timerDiff = clock() - timerStart;
				double duration = (double)timerDiff / (double)CLOCKS_PER_SEC;
				PartifyMeshLog.Message(LXe_INFO, "100\%%, %u polygons in %.6f seconds.", NoOfPolygons, duration);
				if (ItemName)
					PartifyMeshLog.Message(LXe_WARNING, "'%s' has zero polygons.", ItemName);
			PartifyMeshLog.Message(LXe_NOTFOUND, "No valid mesh found!");
LXtTagInfoDesc CSyPartifyMesh::descInfo[] = {
void initialize() {
		CLxGenericPolymorph *srv = new CLxPolymorph<CSyPartifyMesh>;
		srv->AddInterface(new CLxIfc_Command    <CSyPartifyMesh>);
		srv->AddInterface(new CLxIfc_StaticDesc <CSyPartifyMesh>);
		lx::AddServer(SERVER_NAME, srv);
void cleanup() {};
};//namespace sy_partifymesh_cmd


The following xml file is what's referred to as a config and would form part of your plugin's release files for your kit. This particular config has the following features :-

  • A new command category is created to apportion this particular command under 'Sy/Cmds' in the modo command tree listing.
  • We specify command help details regarding what the command does.
  • We specify a help url for this command. Note: kit relative filespecs in HelpURLs do not currently work as at build 49611. Has been bugged for future release fix.
  • We detail some UI elements relating to icons. See icon resources for more details.
  • Finally, we tell modo to enable the log sub-system that this command plugin is registering itself under because by default the displaying of the majority of log sub-systems is disabled within the logging system.

<?xml version="1.0" encoding="UTF-8"?>
  <atom type="Categories">
    <hash type="Category" key="Commands">
      <hash type="C" key="sy.partifymesh">Sy/Cmds</hash>
  <atom type="CommandHelp">
    <hash type="Command" key="sy.partifymesh@en_US">
      <atom type="UserName">PartifyMesh</atom>
      <atom type="Desc">Apply unique 'part' tag to each polygon in selected mesh.</atom>
      <atom type="Tooltip">Apply unique 'part' tag to each polygon in selected mesh.</atom>
      <atom type="ButtonName">PartifyMesh</atom>
      <atom type="Example">sy.partifymesh</atom>
  <atom type="HelpURLs">
    <hash type="HelpURL" key="command:sy.partifymesh">kit_SyCmds:help\index.html#partifymesh</hash>
  <atom type="UIElements">
    <hash type="Image" key="sy_cmd_icons_20">sy_cmds_20.png</hash>
    <hash type="Image" key="sy_cmd_icons_32">sy_cmds_32.png</hash>
    <hash type="Icon" key="sy.partifymesh_20">         <atom type="Source">sy_cmd_icons_20</atom>     <atom type="Location">0 0 20 20</atom>  </hash>
    <hash type="Icon" key="sy.partifymesh_32">         <atom type="Source">sy_cmd_icons_32</atom>     <atom type="Location">0 0 32 32</atom>  </hash>
  <atom type="StartupCommands">
    <list type="Command">log.subEnable "sy/cmds/partifymesh" true</list>

Icon Resources

The following two .png files are provided as examples of the icon images that commands mapped to UI buttons may implement. The display of which is an automated process. That is when the command returns 'false' from basic_Enable the system automatically applies a desaturated version of the icon image to the button. See icon resources for more details.

File:Sy cmds 20.png - 20 x 20 sy.partifymesh icon
File:Sy cmds 32.png - 32 x 32 sy.partifymesh icon