Difference between revisions of "PlaneTool example"

From The Foundry MODO SDK wiki
Jump to: navigation, search
Line 10: Line 10:
 
<syntaxhighlight lang="python">
 
<syntaxhighlight lang="python">
 
#!python
 
#!python
 
+
 
'''
 
'''
   
+
 
     A simple tool plugin to demonstrate geometry creation. A plane is
 
     A simple tool plugin to demonstrate geometry creation. A plane is
 
     created on the active mesh layer, with the size controlled by hauling
 
     created on the active mesh layer, with the size controlled by hauling
 
     in the viewport.
 
     in the viewport.
   
+
 
     Tools are rather advanced and there is a lot of potential for taking
 
     Tools are rather advanced and there is a lot of potential for taking
 
     this example further. For example, drawing tool handles, defining the
 
     this example further. For example, drawing tool handles, defining the
 
     origin of the geometry creation, support for falloffs, generating UVs...etc.
 
     origin of the geometry creation, support for falloffs, generating UVs...etc.
   
+
 
     Author: Matt Cox
 
     Author: Matt Cox
   
+
 
'''
 
'''
 
+
 
import lx
 
import lx
 
import lxifc
 
import lxifc
 
import lxu.attributes
 
import lxu.attributes
 
+
 
class Plane_Tool(lxifc.Tool, lxifc.ToolModel, lxu.attributes.DynamicAttributes):
 
class Plane_Tool(lxifc.Tool, lxifc.ToolModel, lxu.attributes.DynamicAttributes):
   
+
 
     def __init__(self):
 
     def __init__(self):
 
         lxu.attributes.DynamicAttributes.__init__(self)
 
         lxu.attributes.DynamicAttributes.__init__(self)
       
+
 
         '''
 
         '''
 
             Our tool is going to have a single value that controls how it
 
             Our tool is going to have a single value that controls how it
Line 42: Line 42:
 
         self.dyna_Add('size', lx.symbol.sTYPE_FLOAT)
 
         self.dyna_Add('size', lx.symbol.sTYPE_FLOAT)
 
         self.attr_SetFlt(0,1.0)
 
         self.attr_SetFlt(0,1.0)
       
+
 
         '''
 
         '''
 
             Allocate a vector type. We're setting the mimimum required here,
 
             Allocate a vector type. We're setting the mimimum required here,
Line 50: Line 50:
 
         pkt_svc = lx.service.Packet()
 
         pkt_svc = lx.service.Packet()
 
         self.vec_type = pkt_svc.CreateVectorType(lx.symbol.sCATEGORY_TOOL)
 
         self.vec_type = pkt_svc.CreateVectorType(lx.symbol.sCATEGORY_TOOL)
       
+
 
     def tool_Reset(self):
 
     def tool_Reset(self):
 
         '''
 
         '''
Line 58: Line 58:
 
         '''
 
         '''
 
         self.attr_SetFlt(0,1.0)
 
         self.attr_SetFlt(0,1.0)
   
+
 
     def tool_Evaluate(self,vts):
 
     def tool_Evaluate(self,vts):
 
         '''
 
         '''
Line 65: Line 65:
 
             and polygons, defined by the size attribute.
 
             and polygons, defined by the size attribute.
 
         '''
 
         '''
       
+
 
         '''
 
         '''
 
             We'll be using the LayerService to interact with meshes in the
 
             We'll be using the LayerService to interact with meshes in the
Line 75: Line 75:
 
         layer_svc = lx.service.Layer()
 
         layer_svc = lx.service.Layer()
 
         layer_scan = lx.object.LayerScan(layer_svc.ScanAllocate(lx.symbol.f_LAYERSCAN_EDIT))
 
         layer_scan = lx.object.LayerScan(layer_svc.ScanAllocate(lx.symbol.f_LAYERSCAN_EDIT))
       
+
 
         if layer_scan.test() == False:
 
         if layer_scan.test() == False:
 
             return
 
             return
       
+
 
         '''
 
         '''
 
             We have a single attribute that defines the size of the plane. We
 
             We have a single attribute that defines the size of the plane. We
Line 86: Line 86:
 
         '''
 
         '''
 
         size_attr = self.attr_GetFlt(0)
 
         size_attr = self.attr_GetFlt(0)
       
+
 
         '''
 
         '''
 
             We're going to operate on all active layers, so we simply want
 
             We're going to operate on all active layers, so we simply want
Line 97: Line 97:
 
             '''
 
             '''
 
             mesh_loc = lx.object.Mesh(layer_scan.MeshEdit(n))
 
             mesh_loc = lx.object.Mesh(layer_scan.MeshEdit(n))
           
+
 
             if mesh_loc.test() == False:
 
             if mesh_loc.test() == False:
 
                 continue
 
                 continue
           
+
 
             '''
 
             '''
 
                 As we are editing both Points and Polygons, we need to get
 
                 As we are editing both Points and Polygons, we need to get
Line 107: Line 107:
 
             point_loc = lx.object.Point(mesh_loc.PointAccessor())
 
             point_loc = lx.object.Point(mesh_loc.PointAccessor())
 
             poly_loc = lx.object.Polygon(mesh_loc.PolygonAccessor())
 
             poly_loc = lx.object.Polygon(mesh_loc.PolygonAccessor())
           
+
 
             if poly_loc.test() == False or point_loc.test() == False:
 
             if poly_loc.test() == False or point_loc.test() == False:
 
                 continue
 
                 continue
           
+
 
             '''
 
             '''
 
                 Now we are going to construct the plane geometry. To keep things
 
                 Now we are going to construct the plane geometry. To keep things
Line 121: Line 121:
 
             for point in range(0,4):
 
             for point in range(0,4):
 
                 '''
 
                 '''
                     Create a new point. This *should* take a position and
+
                     First we want to calculate a position for the new point.
                    return a PointID, but it seems to be working incorrectly at
+
                    the moment, so it assigns a random position and returns both
+
                    the position and point ID.
+
                '''
+
                current_point = point_loc.New()
+
               
+
                '''
+
                    Modo *should* make the new point the active point, so we can
+
                    work on it straight away, but it doesn't. Thankfully, one of
+
                    the returned values from the New() function is the PointID.
+
                    So we can simply select the newly created point by it's ID
+
                    and work on it.
+
                '''
+
                point_loc.Select(current_point[1])
+
               
+
                '''
+
                    Now we have the new point selected, we need to create a
+
                    position vector and set the new points position.
+
 
                 '''
 
                 '''
 
                 point_pos = list()
 
                 point_pos = list()
Line 151: Line 133:
 
                     point_pos = ((size_attr)/2,0.0,(-size_attr)/2)
 
                     point_pos = ((size_attr)/2,0.0,(-size_attr)/2)
 
                  
 
                  
                 point_loc.SetPos(point_pos)
+
                 '''
                  
+
                    Create a new point. This method takes a position and
 +
                    return a PointID, so we pass it the position vector we
 +
                    created.
 +
                '''
 +
                current_point = point_loc.New(point_pos)
 +
 +
                 '''
 +
                    Modo *should* make the new point the active point, so we can
 +
                    work on it straight away, but it doesn't. Thankfully, one of
 +
                    the returned values from the New() function is the PointID.
 +
                    So we can simply select the newly created point by it's ID
 +
                    and work on it.
 +
                '''
 +
                point_loc.Select(current_point)
 +
 
                 '''
 
                 '''
 
                     We have to pass a list of points to the Polygon New()
 
                     We have to pass a list of points to the Polygon New()
 
                     method. So we add the current PointID to the Points tuple.
 
                     method. So we add the current PointID to the Points tuple.
 
                 '''
 
                 '''
                 points = points + (current_point[1],)
+
                 points = points + (current_point,)
           
+
 
             '''
 
             '''
 
                 The Polygon New() method requires the list of points to be
 
                 The Polygon New() method requires the list of points to be
Line 170: Line 166:
 
             points_storage.setSize(4)
 
             points_storage.setSize(4)
 
             points_storage.set(points)
 
             points_storage.set(points)
           
+
 
             '''
 
             '''
 
                 Now we have our point positions, we create a new polygon.
 
                 Now we have our point positions, we create a new polygon.
 
             '''
 
             '''
 
             poly_loc.New(lx.symbol.iPTYP_FACE,points_storage,4,0)
 
             poly_loc.New(lx.symbol.iPTYP_FACE,points_storage,4,0)
           
+
 
             '''
 
             '''
 
                 Before we move on to the next layer, we tell modo that have
 
                 Before we move on to the next layer, we tell modo that have
Line 181: Line 177:
 
             '''
 
             '''
 
             layer_scan.SetMeshChange(n, lx.symbol.f_MESHEDIT_GEOMETRY)
 
             layer_scan.SetMeshChange(n, lx.symbol.f_MESHEDIT_GEOMETRY)
       
+
 
         '''
 
         '''
 
             Finally, we call the Apply function, which closes our LayerScan
 
             Finally, we call the Apply function, which closes our LayerScan
Line 187: Line 183:
 
         '''
 
         '''
 
         layer_scan.Apply()
 
         layer_scan.Apply()
   
+
 
     def tool_VectorType(self):
 
     def tool_VectorType(self):
 
         '''
 
         '''
Line 194: Line 190:
 
         '''
 
         '''
 
         return self.vec_type
 
         return self.vec_type
   
+
 
     def tool_Order(self):
 
     def tool_Order(self):
 
         '''
 
         '''
Line 200: Line 196:
 
         '''
 
         '''
 
         return lx.symbol.s_ORD_ACTR
 
         return lx.symbol.s_ORD_ACTR
   
+
 
     def tool_Task(self):
 
     def tool_Task(self):
 
         '''
 
         '''
Line 208: Line 204:
 
         '''
 
         '''
 
         return lx.symbol.i_TASK_ACTR
 
         return lx.symbol.i_TASK_ACTR
   
+
 
     def tmod_Flags(self):
 
     def tmod_Flags(self):
 
         '''
 
         '''
Line 216: Line 212:
 
         '''
 
         '''
 
         return lx.symbol.fTMOD_I0_ATTRHAUL
 
         return lx.symbol.fTMOD_I0_ATTRHAUL
   
+
 
     def tmod_Initialize(self,vts,adjust,flags):
 
     def tmod_Initialize(self,vts,adjust,flags):
 
         '''
 
         '''
Line 224: Line 220:
 
         adj_tool = lx.object.AdjustTool(adjust)
 
         adj_tool = lx.object.AdjustTool(adjust)
 
         adj_tool.SetFlt(0, 1.0)
 
         adj_tool.SetFlt(0, 1.0)
 
+
 
     def tmod_Haul(self,index):
 
     def tmod_Haul(self,index):
 
         '''
 
         '''
Line 239: Line 235:
 
         else:
 
         else:
 
             return 0
 
             return 0
 
+
 
'''
 
'''
 
     "Blessing" the class promotes it to a fist class server. This basically
 
     "Blessing" the class promotes it to a fist class server. This basically

Revision as of 11:58, 4 May 2013

The code below implements a simple tool plugin that demonstrates how to create geometry using Python. It essentially creates a plane on the active layer. Hauling in the viewport changes its size.

There's no support for specifying the origin of the tool, there's no support for snapping, workplane, falloffs, tool handles...etc. It's really really simple. It doesn't even have a form for displaying the properties that drive the tool. It's just a very basic example of a tool.

To activate the tool, type "tool.set prim.plane on" into the command history.

Note: As a plugin, the python source file needs to be placed in an 'lxserv' folder.


#!python
 
'''
 
    A simple tool plugin to demonstrate geometry creation. A plane is
    created on the active mesh layer, with the size controlled by hauling
    in the viewport.
 
    Tools are rather advanced and there is a lot of potential for taking
    this example further. For example, drawing tool handles, defining the
    origin of the geometry creation, support for falloffs, generating UVs...etc.
 
    Author: Matt Cox
 
'''
 
import lx
import lxifc
import lxu.attributes
 
class Plane_Tool(lxifc.Tool, lxifc.ToolModel, lxu.attributes.DynamicAttributes):
 
    def __init__(self):
        lxu.attributes.DynamicAttributes.__init__(self)
 
        '''
            Our tool is going to have a single value that controls how it
            operates. This value will control the size of the plane geometry
            that we create. So we create a new Dynamic Attribute, who's type
            is float and it's initial value is 1.0.
        '''        
        self.dyna_Add('size', lx.symbol.sTYPE_FLOAT)
        self.attr_SetFlt(0,1.0)
 
        '''
            Allocate a vector type. We're setting the mimimum required here,
            but we could add some packets to the vector stack, that would
            allow us to do things like work with symmetry or falloffs.
        '''
        pkt_svc = lx.service.Packet()
        self.vec_type = pkt_svc.CreateVectorType(lx.symbol.sCATEGORY_TOOL)
 
    def tool_Reset(self):
        '''
            This function is called to reset the tool back to it's default.
            In our case, we are simply setting the plane size attribute
            back to the default of 1m.
        '''
        self.attr_SetFlt(0,1.0)
 
    def tool_Evaluate(self,vts):
        '''
            This is where our tool is actually evaluated. We're going to get
            the current layers and loop through them, generating new vertices
            and polygons, defined by the size attribute.
        '''
 
        '''
            We'll be using the LayerService to interact with meshes in the
            scene. We also want to localize a LayerScan object using the
            ScanAllocate method. The symbol "f_LAYERSCAN_EDIT", tells modo
            that we want to scan active layers and edit the mesh. See the SDK
            wiki for the declaration of this symbol.
        '''
        layer_svc = lx.service.Layer()
        layer_scan = lx.object.LayerScan(layer_svc.ScanAllocate(lx.symbol.f_LAYERSCAN_EDIT))
 
        if layer_scan.test() == False:
            return
 
        '''
            We have a single attribute that defines the size of the plane. We
            read it's value using the index or the order the attributes were
            added in the constructor. As we only have a single attribute on
            this tool, it's clearly going to be 0.
        '''
        size_attr = self.attr_GetFlt(0)
 
        '''
            We're going to operate on all active layers, so we simply want
            to loop through the active layers and perform some operation.
        '''        
        for n in range(0,layer_scan.Count()):
            '''
                We want to grab the Edit Mesh on the current layer and
                localize it into a Mesh object, so that we can edit it.
            '''
            mesh_loc = lx.object.Mesh(layer_scan.MeshEdit(n))
 
            if mesh_loc.test() == False:
                continue
 
            '''
                As we are editing both Points and Polygons, we need to get
                some Point and Polygon interfaces from the mesh.
            '''
            point_loc = lx.object.Point(mesh_loc.PointAccessor())
            poly_loc = lx.object.Polygon(mesh_loc.PolygonAccessor())
 
            if poly_loc.test() == False or point_loc.test() == False:
                continue
 
            '''
                Now we are going to construct the plane geometry. To keep things
                simple, we're going to loop through a statement that creates 4
                new points. For each of the points, we'll store their ID in a
                list. Finally, we'll iterrate over the list and create the
                new polygon from the four points.
            '''
            points = ()            
            for point in range(0,4):
                '''
                    First we want to calculate a position for the new point.
                '''
                point_pos = list()
                if point == 0:
                    point_pos = ((-size_attr)/2,0.0,(-size_attr)/2)
                elif point == 1:
                    point_pos = ((-size_attr)/2,0.0,(size_attr)/2)
                elif point == 2:
                    point_pos = ((size_attr)/2,0.0,(size_attr)/2)
                elif point == 3:
                    point_pos = ((size_attr)/2,0.0,(-size_attr)/2)
 
                '''
                    Create a new point. This method takes a position and
                    return a PointID, so we pass it the position vector we
                    created.
                '''
                current_point = point_loc.New(point_pos)
 
                '''
                    Modo *should* make the new point the active point, so we can
                    work on it straight away, but it doesn't. Thankfully, one of
                    the returned values from the New() function is the PointID.
                    So we can simply select the newly created point by it's ID
                    and work on it.
                '''
                point_loc.Select(current_point)
 
                '''
                    We have to pass a list of points to the Polygon New()
                    method. So we add the current PointID to the Points tuple.
                '''
                points = points + (current_point,)
 
            '''
                The Polygon New() method requires the list of points to be
                passed as a Storage Buffer. So we create a storage buffer, set
                it's type to store pointers, set the size to be number of points
                we intend to store in it and then finally, write out Points
                tuple to it.
            '''
            points_storage = lx.object.storage()
            points_storage.setType('p')
            points_storage.setSize(4)
            points_storage.set(points)
 
            '''
                Now we have our point positions, we create a new polygon.
            '''
            poly_loc.New(lx.symbol.iPTYP_FACE,points_storage,4,0)
 
            '''
                Before we move on to the next layer, we tell modo that have
                edited this mesh layer.
            '''
            layer_scan.SetMeshChange(n, lx.symbol.f_MESHEDIT_GEOMETRY)
 
        '''
            Finally, we call the Apply function, which closes our LayerScan
            interface and applies all our changes to the mesh.
        '''
        layer_scan.Apply()
 
    def tool_VectorType(self):
        '''
            This function simply returns the tool VectorType that we created
            in the constructor.
        '''
        return self.vec_type
 
    def tool_Order(self):
        '''
            This sets the position of the tool in the toolpipe.
        '''
        return lx.symbol.s_ORD_ACTR
 
    def tool_Task(self):
        '''
            Simply defines the type of task performed by this tool. We set
            this to an Action tool, which basically means it will alter the
            state of modo.
        '''
        return lx.symbol.i_TASK_ACTR
 
    def tmod_Flags(self):
        '''
            This sets how we intend to interact with the tool. The symbol
            "fTMOD_I0_ATTRHAUL" basically says that we expect to haul an
            attribute when clicking and dragging with the left mouse button.
        '''
        return lx.symbol.fTMOD_I0_ATTRHAUL
 
    def tmod_Initialize(self,vts,adjust,flags):
        '''
            This is called when the tool is activated. We use it to simply
            set the attribute that we hauling back to the default.
        '''
        adj_tool = lx.object.AdjustTool(adjust)
        adj_tool.SetFlt(0, 1.0)
 
    def tmod_Haul(self,index):
        '''
            Hauling is dependent on the direction of the haul. So a vertical
            haul can drive a different parameter to a horizontal haul. The
            direction of the haul is represented by an index, with 0
            representing horizontal and 1 representing vertical. The function
            simply returns the name of the attribute to drive, given it's index.
            As we only have one attribute, we'll set horizontal hauling to
            control it and vertical hauling to do nothing.
        '''
        if index == 0:
            return "size"
        else:
            return 0
 
'''
    "Blessing" the class promotes it to a fist class server. This basically
    means that modo will now recognize this plugin script as a tool plugin.
'''
lx.bless(Plane_Tool, "prim.plane")