Tool spikey

From The Foundry MODO SDK wiki
Revision as of 19:06, 16 September 2013 by Adissid (Talk | contribs)

Jump to: navigation, search

Tool_spikey is a basic example plugin. This wiki page is intended as a walkthrough of the code in order to help you better understand the SDK.

This plugin adds the spikey tool to the suite of tools modo has. The tool takes surfaces and makes them more "spikey", as illustrated in screenshots below

Sphereb4spikey.png Sphereafterspikey.png

Code Walkthrough

Class Declarations

We want this class to display structured data in the log. In this case it's used to show feedback on the current spikey factor, and as such will be called whenever the spikey tool is activate. To that end, we inherit from CLxImpl_LogInfoBlock as we are going to want our data to be in the form of a log info block. The first method here indicates the name of the command, which in this case is test.spikey. The next three walk the list of fields, setting the count as 1(there can only be one tool), the name of the data as 'factor', and the data type as percent. These are all put into arrays that we can later access.

 class CSpikeyLogBlock :
                public CLxImpl_LogInfoBlock
 {
       public:
                LxResult
        lb_Name (
                const char	       **name)		LXx_OVERRIDE
        {
                name[0] = "test.spikey";
                return LXe_OK;
        }
 
                LxResult
        lb_FieldCount (
                unsigned int		*count)		LXx_OVERRIDE
        {
                count[0] = 1;
                return LXe_OK;
        }
 
                LxResult
        lb_FieldName (
                unsigned int		 index,
                const char	       **name)		LXx_OVERRIDE
        {
                name[0] = "factor";
                return LXe_OK;
        }
 
                LxResult
        lb_FieldType (
                unsigned int		 index,
                const char	       **type)		LXx_OVERRIDE
        {
                type[0] = LXsTYPE_PERCENT;
                return LXe_OK;
        }
 
 };

The Spikey tool. In order to have our class create a tool, we inherit from CLxImpl_Tool and CLxImpl_ToolModel. The attributes interface is inherited from the utility class; we inherit from it in order to be able to display the attributes of our tool. First off, the tmod_Initialize function is called whenever the tool is activated/deactivated. Next, the tmod_Flags function sets flags that indicate certain attributes about the tool. Because we have chosen to use hauling for this tool, we have a tmod_Haul method that indicates which attribute we want to haul. Next, we start implementing our basic tool methods. The tool_Reset function resets all of our tool's attributes to their original values. tool_VectorType returns the tool vector type, describing the vector packets required for processing. tool_Order specifies the order in the pipe by returning an Ordinal string. tool_Task describes the type of task performed by the tool. tool_Evaluate takes all these methods and uses them to actually perform the tool's action(s).

 class CSpikeyTool :
                public CLxImpl_Tool,
                public CLxImpl_ToolModel,
                public CLxDynamicAttributes
 {
    public:
                        CSpikeyTool ();
 
        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;
 
        CLxUser_LogService	 s_log;
        CLxUser_LayerService	 s_layer;
        CLxUser_VectorType	 v_type;
        unsigned		 offset_falloff, offset_view;
        unsigned		 mode_select;
 };

This class is a map visitor that collects the maps in vectors based on type. In order to create a map visitor, we are going to need to inherit from CLxImpl_AbstractVisitor, which allows us to build our custom visitor.

 class CSpikeMapListVisitor : public CLxImpl_AbstractVisitor
 {
        ...
 };

This class does evaluation. It does so with a polygon visitor which holds the current state of the action. The main function here is the Evaluate function, which in this class is written to replace the polygon with a fan of triangles. All of the rest of the functions perform utilities that assist the Evaluate function in performing its function.

 class CSpikePolygonVisitor : public CLxImpl_AbstractVisitor
 {
    public:
        LxResult		 Evaluate ();
        bool			 Normalize (LXtVector);
        void			 VertexPos (unsigned index, LXtFVector pos);
        bool			 VertexCross (unsigned i0, unsigned i1, unsigned i2, LXtVector dir);
        bool			 GoodNormal (LXtVector dir);
        bool			 GetSpike (LXtVector pos);
 
        LXtMeshMapID		 map_id;
        LXtID4			 map_type;
        unsigned		 vrt_count;
        double			 pol_weight;
 
        CSpikeMapListVisitor	 e_maps;
        CLxUser_Mesh		 e_mesh;
        CLxUser_Polygon		 e_poly, e_dest;
        CLxUser_Point		 e_vert;
        CLxUser_FalloffPacket	 e_falloff;
        double			 e_factor;
        bool			 edit_any;
 };

Initialize

This initialize method exports two servers. The first is dependent on the CSpikeyTool class and includes the interface for all the classes that CSpikeyTool inherited from. The same is done for the second server, with the exception that the class in question here is CSpikeyLogBlock. We want the server dependent on the CSpikeyLogBlock class to be activated at the same time as the one that activates the Spikey tool because it writes information about the Spikey tool to the log so we give them the same name.

        void
 initialize ()
 {
      CLxGenericPolymorph		*srv;
 
        srv = new CLxPolymorph<CSpikeyTool>;
        srv->AddInterface (new CLxIfc_Tool      <CSpikeyTool>);
        srv->AddInterface (new CLxIfc_ToolModel <CSpikeyTool>);
        srv->AddInterface (new CLxIfc_Attributes<CSpikeyTool>);
        thisModule.AddServer ("test.spikey", srv);
 
        srv = new CLxPolymorph<CSpikeyLogBlock>;
        srv->AddInterface (new CLxIfc_LogInfoBlock<CSpikeyLogBlock>);
        thisModule.AddServer ("test.spikey", srv);
 }


Implementations

The CSpikeyTool constructor adds one tool attribute. This causes the plugin to query us for a value for this attribute whenever it is constructed, which happens on activation. To us, this appears as the Spike Strength value. It also allocates a vector type (which doesn't seem to need anything in it!), the falloff packet offset and the select mode mask.

 CSpikeyTool::CSpikeyTool ()
 {
        CLxUser_PacketService	 sPkt;
        CLxUser_MeshService	 sMesh;
 
        dyna_Add (ATTRs_FACTOR, LXsTYPE_PERCENT);
 
        sPkt.NewVectorType (LXsCATEGORY_TOOL, v_type);
        sPkt.AddPacket (v_type, LXsP_TOOL_FALLOFF,    LXfVT_GET);
        sPkt.AddPacket (v_type, LXsP_TOOL_VIEW_EVENT, LXfVT_GET);
 
        offset_falloff = sPkt.GetOffset (LXsCATEGORY_TOOL, LXsP_TOOL_FALLOFF);
        offset_view    = sPkt.GetOffset (LXsCATEGORY_TOOL, LXsP_TOOL_VIEW_EVENT);
        mode_select    = sMesh.SetMode ("select");
 }

Reset sets the attributes back to defaults, in this case 0.

        void
 CSpikeyTool::tool_Reset ()
 {
         ...
 }

Boilerplate methods that identify this as an action (state altering) tool.

        LXtObjectID
 CSpikeyTool::tool_VectorType ()
 {
        ...
 }
 
        const char *
 CSpikeyTool::tool_Order ()
 {
       ...
 }
 
         LXtID4
 CSpikeyTool::tool_Task ()
 {
       ...
 }

We employ the simplest possible tool model -- default hauling. We indicate that we want to haul one attribute, we name the attribute, and we implement Initialize() which is what to do when the tool activates or re-activates. In this case set the factor back to zero.

        unsigned
 CSpikeyTool::tmod_Flags ()
 {
       ...
 }
 
        void
 CSpikeyTool::tmod_Initialize (
        ILxUnknownID		 vts,
        ILxUnknownID		 adjust,
        unsigned int		 flags)
 {
        ...
 }
 
        const char *
 CSpikeyTool::tmod_Haul (
        unsigned		 index)
 {
        ...
 }

Get a vertex position. This gets the position of the given vertex of the polygon relative to the current map. This allows us to evaluate the shape of polygons in morphs.

        void
 CSpikePolygonVisitor::VertexPos (
        unsigned		 index,
        LXtFVector		 pos)
 {
        ...
 }

Normalize a vector, if possible.

        bool
 CSpikePolygonVisitor::Normalize (
        LXtVector		 v)
 {
        ...
 }

Get the normalized cross-product of edge vectors defined by the three points of the polygon.

        bool
 CSpikePolygonVisitor::VertexCross (
        unsigned		 i0,
        unsigned		 i1,
        unsigned		 i2,
        LXtVector		 dir)
 {
        ...
 }

Compute a good normal (one that's symmetry safe, which the basic polygon normal is not). This takes the cross product of the 0'th point, then adds in the rest flipping as needed. Final result is renormalized.

        bool
 CSpikePolygonVisitor::GoodNormal (
        LXtVector		 dir)
 {
        ...
 }

Compute the position of the spike for the current polygon in the current morph map. We compute the average position and the perimeter and normal of the polygon, and compute a position along the normal with an offset given by average edge length modulated by weight. Returns false if it cannot be computed.

        bool
 CSpikePolygonVisitor::GetSpike (
        LXtVector		 pos)
 {
        ...
 }

Evaluation replaces the polygon with a fan of triangles. The central point is given a spike position in all morphs, and other maps are interpolated.

        LxResult
 CSpikePolygonVisitor::Evaluate ()
 {
        ...
 }

Tool evaluation uses layer scan interface to walk through all the active meshes and visit all the selected polygons.

        void
 CSpikeyTool::tool_Evaluate (
        ILxUnknownID		 vts)
 {
        ...
 }