Example cmd sy partifymesh

From The Foundry MODO SDK wiki
Revision as of 15:05, 12 April 2012 by Synide (Talk | contribs) (Initial version.)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Introduction

This article is a an example of a adding a 'new' command to modo.

...TODO: more info req'd... eg. style, coding do's & dont's etc., why one should have used a PreExecute() method to prompt the user with continue or not if there were a ginormous amount of polygons in the given mesh, etc. etc.


Code

... TODO: Discuss the code ...


//Copyright Synide(Sy) 2011
//synide@rocketmail.com
//=====================================
// Notes:
//            April, 2012 - Revised for Liki example.
 
#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>
 
std::string fnI2S(unsigned value, bool notnullterminated = true, unsigned padding = 0, bool asHexadecimal = false, bool withHexPrefix = false) {
    //'fnI2S'
    //    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;
    else
        os << value << std::ends;
    if (asHexadecimal && withHexPrefix)
        result = std::string("0x");
    result += os.str();
    return result;
};
 
namespace sy_partifymesh
{
 
static char *SERVER_NAME                = "sy.partifymesh";
static char *LOG_NAME                    = "sy/cmds/partifymesh";
 
/*
 * 'sy.partifymesh' command is derived from 'CLxBasicCommand'.
 *
 * Was going to have a parameter passed to the command (name of the mesh to process) but decided against it in the end.
 *
 * We won't bother with utilizing any of the system called pre-work methods for doing
 * sanity checks etc. We'll just set the 'flags' for this command and let the system flow
 * straight through to 'cmd_Execute()' where we'll just go ahead and do the work regardless.
 * Not recommended coding... :)
 *
 * As I'm not sure if commands can utlize a 'CLxUser_Monitor'. We might have to
 * fire progress reports through to the log system. - TODO.
 */
class CSyPartifyMesh : public CLxBasicCommand {
    public:
        CSyPartifyMesh();
        int                             basic_CmdFlags      () LXx_OVERRIDE;
        void                            cmd_Execute         (unsigned int flags) LXx_OVERRIDE;
        CLxUser_LayerService            LayerService;
        CLxUser_LogService              LogService;
        CLxUser_Log                     PartifyMeshLog;
        static LXtTagInfoDesc           descInfo[];
 
};
 
LXtTagInfoDesc CSyPartifyMesh::descInfo[] = {{LXsSRV_LOGSUBSYSTEM,LOG_NAME}, {0}};
 
CSyPartifyMesh::CSyPartifyMesh() {
    LogService.GetSubSystem(LOG_NAME, PartifyMeshLog);
};
 
int CSyPartifyMesh::basic_CmdFlags() {
    return LXfCMD_UNDO;
};
 
void CSyPartifyMesh::cmd_Execute(unsigned int flags) {
/*
*    We restrict the LayerScan's results to the 'Primary' selected mesh 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;
 
    LayerService.BeginScan(LXf_LAYERSCAN_PRIMARY | LXf_LAYERSCAN_WRITEMESH, LayerScan);
 
    if (LayerScan.ItemByIndex(0, theItem))
        theItem.Name(&ItemName);
 
    // 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.
            thePolygon.fromMeshObj(theMesh);
 
            // Set 'theTag' object as the 'tags' accessor for the polygon object.
            theTag.set(thePolygon);
 
            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.
                thePolygon.SelectByIndex(i1);
 
                // 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;
                    switch(msg_counter)
                    {
                        case 1:
                            PartifyMeshLog.Message(LXe_INFO, "20\%%, %u polygons in %.6f seconds.", i1, duration);
                            break;
                        case 2:
                            PartifyMeshLog.Message(LXe_INFO, "40\%%, %u polygons in %.6f seconds.", i1, duration);
                            break;
                        case 3:
                            PartifyMeshLog.Message(LXe_INFO, "60\%%, %u polygons in %.6f seconds.", i1, duration);
                            break;
                        case 4:
                            PartifyMeshLog.Message(LXe_INFO, "80\%%, %u polygons in %.6f seconds.", i1, duration);
                            break;
                    }
                    msg_counter += 1;
                }
            }
 
            // Tell the LayerScan that we are only 'editing' polygon tags.
            LayerScan.SetMeshChange(0, LXf_MESHEDIT_POL_TAGS);
 
            // Apply the mesh changes.
            LayerScan.Apply();
 
            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);
        }
        else
        {
            if (ItemName)
                PartifyMeshLog.Message(LXe_WARNING, "'%s' has zero polygons.", ItemName);
        }
    }
    else
    {
        PartifyMeshLog.Message(LXe_NOTFOUND, "No valid mesh found!");
    }
 
};
 
//NOTE: Read the code carefully, this is a sy_partifymesh::initialize() method, NOT the global initialize() method.
void initialize() {
        CLxGenericPolymorph    *srv;
        srv = new CLxPolymorph<CSyPartifyMesh>;
        srv->AddInterface(new CLxIfc_Command <CSyPartifyMesh>);
        srv->AddInterface(new CLxIfc_StaticDesc <CSyPartifyMesh>);
        thisModule.AddServer(sy_partifymesh::SERVER_NAME, srv); //Just to illustrate thisModule usage instead of lx::AddServer
};
 
/*
//NOTE: Read the code carefully, this is a sy_partifymesh::cleanup() method, NOT the global cleanup() method.
void cleanup()
{
 
};
*/
 
};//namespace sy_partifymesh
 
 
// I like the new layout of utilizing namespaces with an initialize() in each and then
// calling those from the global initialize() method. Keeps things tidy!
// FYI. Don't need to extern in this instance 'cause we're only 1 file...
 
void initialize()
{
    sy_partifymesh::initialize();
};
 
/* No need for a cleanup() but if we did they'd go here...
// NOTE: cleanup() should rarely need to be used if things a destructed well.
void cleanup()
{
    sy_partifymesh::cleanup();
};
*/


Config

In the following xml file a new commands category is created to apportion this particular command under 'Sy/Cmds' in modo. Also, we specify command help details regarding what the command does. It should be noted that one would also include help & examples for the command and/or command parameters within the 'CommandHelp' element. Finally, we tell modo to enable the log sub-system that this command plugin is registering itself under because by default they are disabled within the logging system.


<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <atom type="Categories">
    <hash type="Category" key="Commands">
      <hash type="C" key="sy.partifymesh">Sy/Cmds</hash>
    </hash>
  </atom>
 
  <atom type="CommandHelp">
    <hash type="Command" key="sy.partifymesh@en_US">
      <atom type="UserName">sy.partifymesh</atom>
      <atom type="Desc">Give each polygon in selected mesh a unique 'part' tag.</atom>
      <atom type="Tooltip"></atom>
    </hash>
  </atom>
  <atom type="StartupCommands">
    <list type="Command">log.subEnable "sy/cmds/partifymesh" true</list>
  </atom>
</configuration>