Your First Tool in Modo

Basic Tool

This is a guide to creating your very first tool in Modo and will go in depth to explaining the necessary components required to create a functioning tool. Without adding unnecessary functionality to complicate things. More complicated examples will be added later on in the wiki.

Code Walkthrough

This is is the most basic required to setup a tool within modo to demonstrate how it works.

Use - Once activated using “tool.set tool.basic on” once the user has clicked in the viewport it will output a variable called factor to the I/O output of the event log.

BasicTool.h

Here we add the required packages to inherit from for our basic tool

*<lx_tool.hpp> Gives us access to the basic tool methods. *<lx_vmodel.hpp> Presents a interface which allows them to participate in mouse input and direct manipulation *<lx_vector> Gives us access to modo vector struct. *<lxu_attributes.hpp> Packages the internal state of the object into a common format. *<lx_plugin.hpp> Allows us to create our tool as a plugin. *<lxu_log.hpp> Allows us to write to the Event Log.

#include <lx_tool.hpp> #include <lx_vmodel.hpp>

#include <lx_vector.hpp> #include <lxu_attributes.hpp> #include <lx_plugin.hpp>

#include <lxu_log.hpp>

using namespace lx_err; </pre>

Class for printing to the IO Log - Go to Event Logs cog wheel scrool down and click I/O Output

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class MyMessage : public CLxLogMessage
{
public:
        const char * GetFormat()
        {
                return "Hello";
        }

        const char * GetVersion()
        {
                return "0.1";
        }
};

Just a Utility class to allow us to convert different data types to strings

1
2
3
4
5
6
template <class T> std::string toString(const T & t)
{
        std::ostringstream oss; // create a stream
        oss << t; // insert value to stream
        return oss.str(); // extract value and return
}

In order to create our tool we inherit from <CLxImpl_Tool> and <CLxImpl_ToolModel> To give attributes to our tool we inherit from <CLxDynamicAttributes>

We also define al the methods we wish to use within our tool here.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BasicTool : public CLxImpl_Tool, public CLxImpl_ToolModel, public CLxDynamicAttributes
{
public:
        //Output log - Created to output data from our tool
        MyMessage             my_log;

        // Class Methods
        void              tool_Reset() LXx_OVERRIDE;
        LXtObjectID       tool_VectorType() LXx_OVERRIDE;
        const char *  tool_Order() LXx_OVERRIDE;
        LXtID4            tool_Task() LXx_OVERRIDE;
        void              tool_Evaluate(ILxUnknownID vts) LXx_OVERRIDE;

        unsigned          tmod_Flags() LXx_OVERRIDE;
        void              tmod_Initialize(ILxUnknownID vts, ILxUnknownID adjust, unsigned flags) LXx_OVERRIDE;
        const char *  tmod_Haul(unsigned index) LXx_OVERRIDE;

        // Variables
        CLxUser_VectorType       v_type;

        BasicTool();
};

BasicTool.cpp

We define our tool attributes here

1
2
3
4
#include "BasicTool.h"

#define ATTRs_FACTORX "factorX"
#define ATTRs_FACTORY "factorY"
*Inside of our constructer we first do the following.

*Define a vector type for our tool - This is the minmum required, in future tools I will show how to add vector stacks like input and symmetry to the tool. *Add a Dynamic attribute to out tool to show the output and set its value

*We define and access the attributes in the order that they are added.

BasicTool::BasicTool() {

1
        CLxUser_PacketService    sPkt;
1
        sPkt.NewVectorType(LXsCATEGORY_TOOL, v_type);
1
2
3
4
        dyna_Add(ATTRs_FACTORX, LXsTYPE_FLOAT);
        attr_SetFlt(0, 1.0);
        dyna_Add(ATTRs_FACTORY, LXsTYPE_FLOAT);
        attr_SetFlt(1, 1.0);

} </pre>

Tool Reset is called to reset the attributes of our tool.

1
2
3
4
5
void BasicTool::tool_Reset()
{
        attr_SetFlt(0, 0.0);
        attr_SetFlt(1, 0.0);
}

The next three methods are used to define what type of tool, in this case a state altering tool.

Returns the tool vector type, describing the vector packets required for processing

1
2
3
4
LXtObjectID BasicTool::tool_VectorType()
{
        return v_type.m_loc;    // peek method; does not add-ref
}

Specifies the order in the pipe by returning an Ordinal string

1
2
3
4
const char *BasicTool::tool_Order()
{
        return LXs_ORD_ACTR;
}

Simply defines the type of task performed by this tool. Set this to an Action tool, which basically means it will alter the state of modo.

1
2
3
4
LXtID4 BasicTool::tool_Task()
{
        return LXi_TASK_ACTR;
}

Sets flags that indicate certain attributes about the tool. *In this case we are using <LXfTMOD_I0_ATTRHAUL> as we simply want to haul in the viewport, similar to channel hauling.

unsigned BasicTool::tmod_Flags() {

1
        return  LXfTMOD_I0_ATTRHAUL;

} </pre>

Called whenever the tool is activated/deactivated

1
2
3
4
5
6
void BasicTool::tmod_Initialize(ILxUnknownID vts, ILxUnknownID adjust, unsigned int     flags)
{
        CLxUser_AdjustTool       at(adjust);
        at.SetFlt(0, 0.0);
        my_log.Info("Initialize");
}

Indicates which attribute we want to haul

1
2
3
4
5
6
7
8
9
const char *BasicTool::tmod_Haul(unsigned index)
{
        if (index == 0) // For hauling Left to right in the viewport
                return ATTRs_FACTORX;
        if (index == 1) // For hauling Top to bottom in the viewport
                return ATTRs_FACTORY;
        else
                return 0;
}

This is where the tools is evaluated and executes what it is intended to do.

1
2
3
4
5
6
7
8
9
void BasicTool::tool_Evaluate(ILxUnknownID vts)
{
        double factorX, factorY;

        attr_GetFlt(0, &factorX);
        attr_GetFlt(1, &factorY);

        my_log.Info("Factor X: " + std::to_string(factorX) + " Factor Y: " + toString(factorY));
}

This initialize method exports our servers. It is dependent on the BasicTool class and includes the interface for all the classes that BasicTool inherited from.

1
2
3
4
5
6
7
8
9
void initialize()
{
        CLxGenericPolymorph             *srv;
        srv = new CLxPolymorph<BasicTool>;
        srv->AddInterface(new CLxIfc_Tool      <BasicTool>);
        srv->AddInterface(new CLxIfc_ToolModel <BasicTool>);
        srv->AddInterface(new CLxIfc_Attributes<BasicTool>);
        thisModule.AddServer("tool.basic", srv);
}