Difference between revisions of "Item gasket"

From The Foundry MODO SDK wiki
Jump to: navigation, search
Line 164: Line 164:
  
 
===[[Server_Tags|Server Tags]]===
 
===[[Server_Tags|Server Tags]]===
 +
 +
Servers tags are examined when the server is initialized, and give information about the server. We set the tags in this case by taking descinfo[] arrays and associating the relevant data with the corresponding flags.
  
 
These tags indicate that the CGasketPackage is of the super type Locator.
 
These tags indicate that the CGasketPackage is of the super type Locator.
Line 173: Line 175:
  
 
===[[Initialize_(index)|Initialize]]===
 
===[[Initialize_(index)|Initialize]]===
 +
 +
Intialize is called when we add the plugin to modo, and is the utility that exports the server.
  
 
As the initialize methods have been written inside the class declarations, we call those functions for our global initialize functions.
 
As the initialize methods have been written inside the class declarations, we call those functions for our global initialize functions.
Line 246: Line 250:
 
         ILxUnknownID addChan)
 
         ILxUnknownID addChan)
 
  {
 
  {
         CLxUser_AddChannel ac (addChan);
+
         ...
+
        ac.NewChannel  (Cs_TYPE, LXsTYPE_INTEGER);
+
        ac.SetDefault  (0.0, TYPEi_SERPINSKI);
+
        ac.SetHint    (hint_typechan);
+
+
        ac.NewChannel  (Cs_LEVEL, LXsTYPE_FLOAT);
+
        ac.SetDefault  (3.0, 3);
+
+
        return LXe_OK;
+
 
  }
 
  }
  
Line 264: Line 259:
 
         const LXtGUID *guid)
 
         const LXtGUID *guid)
 
  {
 
  {
         return inst_spawn.TestInterfaceRC (guid);
+
         ...
 
  }
 
  }
  
Line 273: Line 268:
 
         void       **ppvObj)
 
         void       **ppvObj)
 
  {
 
  {
         inst_spawn.Alloc (ppvObj);
+
         ...
        return LXe_OK;
+
 
  }
 
  }
  
Line 284: Line 278:
 
         ILxUnknownID super)
 
         ILxUnknownID super)
 
  {
 
  {
         m_item.set (item);
+
         ...
        return LXe_OK;
+
 
  }
 
  }
  
Line 295: Line 288:
 
         unsigned *index)
 
         unsigned *index)
 
  {
 
  {
         CLxUser_Evaluation eval (evalObj);
+
         ...
+
        index[0] =
+
            eval.AddChan (m_item, Cs_TYPE);
+
            eval.AddChan (m_item, Cs_LEVEL);
+
            eval.AddChan (m_item, LXsICHAN_XFRMCORE_WORLDMATRIX);
+
+
        return LXe_OK;
+
 
  }
 
  }
  
Line 313: Line 299:
 
         void       **ppvObj)
 
         void       **ppvObj)
 
  {
 
  {
         CLxUser_Attributes ai (attr);
+
         ...
        CGasketGenerator *gen = gen_spawn.Alloc (ppvObj);
+
+
        gen->i_type = ai.Int  (index + 0);
+
        gen->f_iter = ai.Float (index + 1);
+
        ai.ObjectRO            (index + 2, gen->w_matrix);
+
+
        return LXe_OK;
+
 
  }
 
  }
 
   
 
   
Line 328: Line 307:
 
         int *update)
 
         int *update)
 
  {  
 
  {  
         *update = LXfTBLX_PREVIEW_UPDATE_GEOMETRY;
+
         ...
+
        return LXe_OK;
+
 
  }
 
  }
  
Line 337: Line 314:
 
  CGasketGenerator::CGasketGenerator ()
 
  CGasketGenerator::CGasketGenerator ()
 
  {
 
  {
         dyna_Add (LXsPARTICLEATTR_SEED, "integer");
+
         ...
        attr_SetInt (0, 137);
+
 
  }
 
  }
  
Line 347: Line 323:
 
         LXtID4 type)
 
         LXtID4 type)
 
  {
 
  {
         return (type == LXiTBLX_PARTICLES ? 3 : 0);
+
         ...
 
  }
 
  }
 
   
 
   
Line 356: Line 332:
 
         const char       **name)
 
         const char       **name)
 
  {
 
  {
         if (type != LXiTBLX_PARTICLES || index > 2)
+
         ...
                return LXe_OUTOFBOUNDS;
+
+
        switch (index) {
+
            case 0: name[0] = LXsTBLX_PARTICLE_POS; break;
+
            case 1: name[0] = LXsTBLX_PARTICLE_XFRM; break;
+
            case 2: name[0] = LXsTBLX_PARTICLE_ID; break;
+
        }
+
        return LXe_OK;
+
 
  }
 
  }
  
Line 374: Line 342:
 
         ILxUnknownID vdesc)
 
         ILxUnknownID vdesc)
 
  {
 
  {
         CLxUser_TableauVertex vrx (vdesc);
+
         ...
        unsigned offset;
+
+
        i_pos  = -1;
+
        i_xfrm = -1;
+
        i_id  = -1;
+
+
        vrt_size = vrx.Size ();
+
        if (vrt_size == 0)
+
                return LXe_OK;
+
+
        if (LXx_OK (vrx.Lookup (LXiTBLX_PARTICLES, LXsTBLX_PARTICLE_POS,  &offset)))
+
                i_pos  = offset;
+
+
        if (LXx_OK (vrx.Lookup (LXiTBLX_PARTICLES, LXsTBLX_PARTICLE_XFRM, &offset)))
+
                i_xfrm = offset;
+
+
        if (LXx_OK (vrx.Lookup (LXiTBLX_PARTICLES, LXsTBLX_PARTICLE_ID,  &offset)))
+
                i_id  = offset;
+
+
        return LXe_OK;
+
 
  }
 
  }
  
Line 405: Line 353:
 
         ILxUnknownID trisoup)
 
         ILxUnknownID trisoup)
 
  {
 
  {
         double s;
+
         ...
        int i;
+
        LxResult result;
+
+
        /*
+
        * Allocate the vertex vector and init to zeros. We only need one
+
        * since points are sampled sequentially.
+
        */
+
        vrt_vec = new float[vrt_size];
+
        if (!vrt_vec)
+
                return LXe_OUTOFMEMORY;
+
+
        for (i = 0; i < vrt_size; i++)
+
                vrt_vec[i] = 0.0;
+
+
        /*
+
        * Set the transform scale based on the number of iterations. The
+
        * rigid transform comes from the middle 3x3 matrix of the world transform.
+
        */
+
        switch (i_type) {
+
            case TYPEi_SQUAREHOLE:
+
            case TYPEi_SQUAREFLAKE:
+
                s = 3.0;
+
                break;
+
+
            case TYPEi_SERPINSKI:
+
            case TYPEi_TETRAHEDRON:
+
                s = 2.0;
+
                break;
+
        }
+
+
        s = pow (1.0 / s, (double) f_iter);
+
+
        if (i_xfrm >= 0) {
+
                if (0) { // world space
+
                        LXtMatrix4 m;
+
                        int i;
+
+
                        w_matrix.Get4 (m);
+
+
                        for (i = 0; i < 9; i++)
+
                                vrt_vec[i_xfrm + i] = s * m[i % 3][i / 3];
+
                } else {
+
                        vrt_vec[i_xfrm + 0] = s;
+
                        vrt_vec[i_xfrm + 4] = s;
+
                        vrt_vec[i_xfrm + 8] = s;
+
                }
+
        }
+
+
        /*
+
        * Init random seed from the attribute the client may have set.
+
        */
+
        int seed;
+
        attr_GetInt (0, &seed);
+
        rand_seq.seed (seed);
+
+
        result = LXe_OK;
+
        try {
+
                /*
+
                * Init the triangle soup.
+
                */
+
                tri_soup.set (trisoup);
+
                tri_soup.Segment (1, LXiTBLX_SEG_POINT);
+
+
                /*
+
                * Set initial square to a fixed shape transformed by the
+
                * gasket item's world transform and then recurse. Any errors
+
                * lower down will be caught by the try block.
+
                */
+
                switch (i_type) {
+
                    case TYPEi_SQUAREHOLE:
+
                    case TYPEi_SQUAREFLAKE:
+
                        Square9 ();
+
                        break;
+
+
                    case TYPEi_SERPINSKI:
+
                        Serpinski ();
+
                        break;
+
+
                    case TYPEi_TETRAHEDRON:
+
                        Tetrahedron ();
+
                        break;
+
                }
+
+
        } catch (LxResult rc) {
+
                result = rc;
+
        }
+
+
        delete [] vrt_vec;
+
        return result;
+
 
  }
 
  }
  
Line 502: Line 361:
 
  CGasketGenerator::Square9 ()
 
  CGasketGenerator::Square9 ()
 
  {
 
  {
         CCorner cc, c0, c1, c2, c3;
+
         ...
+
        cc.Set ( 0.0, 0.0,  0.0);
+
        c0.Set (-1.0, 0.0, -1.0);
+
        c1.Set (+1.0, 0.0, -1.0);
+
        c2.Set (+1.0, 0.0, +1.0);
+
        c3.Set (-1.0, 0.0, +1.0);
+
+
        if (0) { // world space
+
                cc.Xfrm (w_matrix);
+
                c0.Xfrm (w_matrix);
+
                c1.Xfrm (w_matrix);
+
                c2.Xfrm (w_matrix);
+
                c3.Xfrm (w_matrix);
+
        }
+
+
        Square9Gen (cc, c0, c1, c2, c3, f_iter);
+
 
  }
 
  }
  
Line 532: Line 375:
 
         float level)
 
         float level)
 
  {
 
  {
         CCorner a03, a12, cen;
+
         ...
+
        a03.Ave (c0, c3);
+
        a12.Ave (c1, c2);
+
        cen.Ave (a03, a12);
+
+
        if (level <= 1.0) {
+
                Point (cc, cen, level);
+
                return;
+
        }
+
+
        CCorner c01, c10, c12, c21, c23, c32, c30, c03;
+
        CCorner c00, c11, c22, c33;
+
+
        c01.Third (c0, c1);
+
        c10.Third (c1, c0);
+
        c12.Third (c1, c2);
+
        c21.Third (c2, c1);
+
        c23.Third (c2, c3);
+
        c32.Third (c3, c2);
+
        c30.Third (c3, c0);
+
        c03.Third (c0, c3);
+
+
        c00.Third (c03, c12);
+
        c11.Third (c12, c03);
+
        c22.Third (c21, c30);
+
        c33.Third (c30, c21);
+
+
        level -= 1.0;
+
        Square9Gen (cen, c0,  c01, c00, c03, level);
+
        Square9Gen (cen, c10, c1,  c12, c11, level);
+
        Square9Gen (cen, c21, c2,  c23, c22, level);
+
        Square9Gen (cen, c30, c33, c32, c3,  level);
+
        if (i_type == TYPEi_SQUAREHOLE) {
+
                Square9Gen (cen, c01, c10, c11, c00, level);
+
                Square9Gen (cen, c11, c12, c21, c22, level);
+
                Square9Gen (cen, c33, c22, c23, c32, level);
+
                Square9Gen (cen, c03, c00, c33, c30, level);
+
        } else {
+
                Square9Gen (cen, c00, c11, c22, c33, level);
+
        }
+
 
  }
 
  }
  
Line 580: Line 383:
 
  CGasketGenerator::Serpinski ()
 
  CGasketGenerator::Serpinski ()
 
  {
 
  {
         CCorner cc, c0, c1, c2;
+
         ...
        float a;
+
+
        a = sqrt (3.0f) / 2.0f;
+
        cc.Set ( 0.0, 0.0,  0.0);
+
        c0.Set ( 1.0, 0.0,  0.0);
+
        c1.Set (-0.5, 0.0,  a);
+
        c2.Set (-0.5, 0.0, -a);
+
+
        if (0) { // world space
+
                cc.Xfrm (w_matrix);
+
                c0.Xfrm (w_matrix);
+
                c1.Xfrm (w_matrix);
+
                c2.Xfrm (w_matrix);
+
        }
+
+
        SerpinskiGen (cc, c0, c1, c2, f_iter);
+
 
  }
 
  }
 
   
 
   
Line 607: Line 394:
 
         float level)
 
         float level)
 
  {
 
  {
         CCorner c01, cen;
+
         ...
 
+
        c01.Ave (c0, c1);
+
        cen.Ave (c01, c2, 2 / 3.0f);
+
 
+
        if (level <= 1.0) {
+
                Point (cc, cen, level);
+
                return;
+
        }
+
 
+
        CCorner c12, c20;
+
 
+
        c12.Ave (c1, c2);
+
        c20.Ave (c2, c0);
+
 
+
        level -= 1.0;
+
        SerpinskiGen (cen, c0, c01, c20, level);
+
        SerpinskiGen (cen, c1, c12, c01, level);
+
        SerpinskiGen (cen, c2, c20, c12, level);
+
 
  }
 
  }
  
Line 671: Line 440:
 
         float level)
 
         float level)
 
  {
 
  {
         CCorner c01, c23, cen;
+
         ...
+
        c01.Ave (c0, c1);
+
        c23.Ave (c2, c3);
+
        cen.Ave (c01, c23);
+
+
        if (level <= 1.0) {
+
                Point (cc, cen, level);
+
                return;
+
        }
+
+
        CCorner c02, c03, c12, c13;
+
+
        c02.Ave (c0, c2);
+
        c03.Ave (c0, c3);
+
        c12.Ave (c1, c2);
+
        c13.Ave (c1, c3);
+
+
        level -= 1.0;
+
        TetrahedronGen (cen, c0, c01, c02, c03, level);
+
        TetrahedronGen (cen, c1, c01, c12, c13, level);
+
        TetrahedronGen (cen, c2, c02, c12, c23, level);
+
        TetrahedronGen (cen, c3, c03, c13, c23, level);
+
 
  }
 
  }
  
Line 704: Line 451:
 
         float level)
 
         float level)
 
  {
 
  {
         CCorner cc;
+
         ...
        unsigned int index;
+
        LxResult rc;
+
+
        cc.Ave (center, parent, level);
+
+
        if (i_pos >= 0) {
+
                vrt_vec[i_pos + 0] = cc.pos[0];
+
                vrt_vec[i_pos + 1] = cc.pos[1];
+
                vrt_vec[i_pos + 2] = cc.pos[2];
+
        }
+
+
        if (i_id >= 0)
+
                vrt_vec[i_id] = rand_seq.uniform ();
+
+
        rc = tri_soup.Vertex  (vrt_vec, &index);
+
        if (LXx_FAIL (rc))
+
                throw (rc);
+
+
        rc = tri_soup.Polygon (index, 0, 0);
+
        if (LXx_FAIL (rc))
+
                throw (rc);
+
 
  }
 
  }

Revision as of 23:49, 11 September 2013

Item_gasket.cpp 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 gasket item to the modo item list.

Gasket shot1.png

The gasket item.

Code Walkthrough

Class Declarations

This class generates the gasket, so we inherit from CLxTableauSurface. The TableauSurface Method is responsible for generating all the triangles that intersect the bounding box. Additionally, we inherit from CLxDynamicAttributes, which provides the attributes interface for our gasket.

class CGasketGenerator :
               public CLxImpl_TableauSurface,
               public CLxDynamicAttributes
{
   public:
               static void
       initialize ()
       {
               CLxGenericPolymorph	*srv;

               srv = new CLxPolymorph<CGasketGenerator>;
               srv->AddInterface (new CLxIfc_TableauSurface<CGasketGenerator>);
               srv->AddInterface (new CLxIfc_Attributes    <CGasketGenerator>);
               lx::AddSpawner (SPNNAME_GENERATOR, srv);
       }

       /*
        * The 'corner' is a simple 3D vector class used to compute the corners of
        * the various primitives.
        */
       class CCorner {
           public:
               LXtFVector	 pos;

               void		 Set (float x, float y, float z)
               {
                       LXx_VSET3 (pos, x, y, z);
               }

               void		 Ave (const CCorner &c1, const CCorner &c2, float w1 = 0.5)
               {
                       float		 w2 = 1.0f - w1;

                       for (int i = 0; i < 3; i++)
                               pos[i] = c1.pos[i] * w1 + c2.pos[i] * w2;
               }

               void		 Third (const CCorner &c1, const CCorner &c2)
               {
                       Ave (c1, c2, 2 / 3.0f);
               }

               void		 Xfrm (const CLxUser_Matrix &m)
               {
                       LXtVector	 in, out;

                       LXx_VCPY (in, pos);
                       m.MultiplyVector (in, out);
                       LXx_VCPY (pos, out);
               }
       };

       CLxUser_Matrix		 w_matrix;
       float			 f_iter;
       int			 i_type;

       CLxUser_TriangleSoup	 tri_soup;
       CLxPseudoRandom		 rand_seq;
       int			 vrt_size;
       float			*vrt_vec;
       int			 i_pos, i_xfrm, i_id;

       CGasketGenerator ();

       unsigned int	 tsrf_FeatureCount (LXtID4 type) LXx_OVERRIDE;
       LxResult	 tsrf_FeatureByIndex (LXtID4 type, unsigned int index, const char **name) LXx_OVERRIDE;
       LxResult	 tsrf_SetVertex (ILxUnknownID vdesc) LXx_OVERRIDE;
       LxResult	 tsrf_Sample (const LXtTableauBox bbox, float scale, ILxUnknownID trisoup) LXx_OVERRIDE;

       void		 Square9 ();
       void		 Square9Gen (const CCorner &cc, const CCorner &c0, const CCorner &c1, const CCorner &c2, const CCorner &c3, float level);

       void		 Serpinski ();
       void		 SerpinskiGen (const CCorner &cc, const CCorner &c0, const CCorner &c1, const CCorner &c2, float level);

       void		 Tetrahedron ();
       void		 TetrahedronGen (const CCorner &cc, const CCorner &c0, const CCorner &c1, const CCorner &c2, const CCorner &c3, float level);

       void		 Point (const CCorner &parent, const CCorner &center, float level);
};

We want this class to create a gasket instance, so we first inherit the CLxImpl_PackageInstance class. What this does is implement the item states and channels. Next, we inherit from CLxImpl_ParticleItem, which lets us allocate a particle object. Finally, we inherit from CLxImpl_TableauSource, which allows us to set how certain channels of the instance will update the preview.

class CGasketInstance :
       public CLxImpl_PackageInstance, 
       public CLxImpl_ParticleItem,
       public  
{
   public:
               static void
       initialize ()
       {
               CLxGenericPolymorph	*srv;

               srv = new CLxPolymorph<CGasketInstance>;
               srv->AddInterface (new CLxIfc_PackageInstance<CGasketInstance>);
               srv->AddInterface (new CLxIfc_ParticleItem   <CGasketInstance>);
               srv->AddInterface (new CLxIfc_TableauSource  <CGasketInstance>);
               lx::AddSpawner (SPNNAME_INSTANCE, srv);
       }

       CLxSpawner<CGasketGenerator>	 gen_spawn;
       CLxUser_Item			 m_item;

       CGasketInstance ()
               : gen_spawn (SPNNAME_GENERATOR)
       {}

       LxResult	 pins_Initialize (ILxUnknownID item, ILxUnknownID super) LXx_OVERRIDE;
       void		 pins_Cleanup (void) LXx_OVERRIDE;

       LxResult	 prti_Prepare  (ILxUnknownID eval, unsigned *index) LXx_OVERRIDE;
       LxResult	 prti_Evaluate (ILxUnknownID attr, unsigned index, void **ppvObj) LXx_OVERRIDE;

       /*
        * TableauSource interface.
        */
       LxResult	 tsrc_PreviewUpdate (int chanIndex, int *update) LXx_OVERRIDE;
};

We want this class to create the package object for the gasket, so we inherit from CLxImpl_Package.

class CGasketPackage :
               public CLxImpl_Package
{
   public:
       static LXtTagInfoDesc		 descInfo[];

               static void
       initialize ()
       {
               CLxGenericPolymorph	*srv;

               srv = new CLxPolymorph<CGasketPackage>;
               srv->AddInterface (new CLxIfc_Package   <CGasketPackage>);
               srv->AddInterface (new CLxIfc_StaticDesc<CGasketPackage>);
               lx::AddServer (SRVNAME_PACKAGE, srv);
       }

       CLxSpawner<CGasketInstance>	 inst_spawn;

       CGasketPackage ()
               : inst_spawn (SPNNAME_INSTANCE)
       {}

       LxResult		pkg_SetupChannels (ILxUnknownID addChan) LXx_OVERRIDE;
       LxResult		pkg_TestInterface (const LXtGUID *guid) LXx_OVERRIDE;
       LxResult		pkg_Attach (void **ppvObj) LXx_OVERRIDE;
};

Server Tags

Servers tags are examined when the server is initialized, and give information about the server. We set the tags in this case by taking descinfo[] arrays and associating the relevant data with the corresponding flags.

These tags indicate that the CGasketPackage is of the super type Locator.

LXtTagInfoDesc	 CGasketPackage::descInfo[] = {
       { LXsPKG_SUPERTYPE,	LXsITYPE_LOCATOR	},
       { 0 }
};

Initialize

Intialize is called when we add the plugin to modo, and is the utility that exports the server.

As the initialize methods have been written inside the class declarations, we call those functions for our global initialize functions.

       void
initialize ()
{
       CGasketPackage   :: initialize ();
       CGasketGenerator :: initialize ();
       CGasketInstance  :: initialize ();
}

This method adds a new server dependent on the CGasketGenerator class that uses the TableauSource and Attributes interfaces.

CGasketGenerator::initialize ()
       {
               CLxGenericPolymorph	*srv;

               srv = new CLxPolymorph<CGasketGenerator>;
               srv->AddInterface (new CLxIfc_TableauSurface<CGasketGenerator>);
               srv->AddInterface (new CLxIfc_Attributes    <CGasketGenerator>);
               lx::AddSpawner (SPNNAME_GENERATOR, srv);
       }

This method adds a new server dependent on the CGasketInstance class that uses the PackageInstance, ParticleItem, and TableauSource interfaces.

CGasketInstance::initialize ()
       {
               CLxGenericPolymorph	*srv;

               srv = new CLxPolymorph<CGasketInstance>;
               srv->AddInterface (new CLxIfc_PackageInstance<CGasketInstance>);
               srv->AddInterface (new CLxIfc_ParticleItem   <CGasketInstance>);
               srv->AddInterface (new CLxIfc_TableauSource  <CGasketInstance>);
               lx::AddSpawner (SPNNAME_INSTANCE, srv);
       }

This method adds a new server dependent on the CGasketPackage class that uses the Package and StaticDesc interfaces.

       CGasketPackage::initialize ()
       {
               CLxGenericPolymorph	*srv;

               srv = new CLxPolymorph<CGasketPackage>;
               srv->AddInterface (new CLxIfc_Package   <CGasketPackage>);
               srv->AddInterface (new CLxIfc_StaticDesc<CGasketPackage>);
               lx::AddServer (SRVNAME_PACKAGE, srv);
       }

Implementations

#define Cs_TYPE			"type"
#define Cs_LEVEL		"level"
 
#define TYPEi_SQUAREHOLE	 0
#define TYPEi_SQUAREFLAKE	 1
#define TYPEi_SERPINSKI		 2
#define TYPEi_TETRAHEDRON	 3

static LXtTextValueHint hint_typechan[] = {
       TYPEi_SQUAREHOLE,	"squareHole",
       TYPEi_SQUAREFLAKE,	"squareFlake",
       TYPEi_SERPINSKI,	"serpinski",
       TYPEi_TETRAHEDRON,	"tetrahedron",
       -1,			"=gasket_toy-type",
       0,			NULL
};

The package has a set of standard channels with default values. These are setup at the start using the AddChannel interface. The gasket has a type and an iteration count (which needs a low default, since these can get huge really quickly).

       LxResult
CGasketPackage::pkg_SetupChannels (
       ILxUnknownID		 addChan)
{
       ...
}

TestInterface() is required so that nexus knows what interfaces instance of this package support. Necessary to prevent query loops.

       LxResult
CGasketPackage::pkg_TestInterface (
       const LXtGUID		*guid)
{
       ...
}

Attach() is called to create a new instance of this item. The returned object implements a specific item of this type in the scene.

LxResult
CGasketPackage::pkg_Attach (
       void		       **ppvObj)
{
       ...
}

Gasket Item Instance-The instance is the implementation of the item, and there will be one allocated for each item in the scene. It can respond to a set of events. Initialization typically stores the item it's attached to.

LxResult
CGasketInstance::pins_Initialize (
       ILxUnknownID		 item,
       ILxUnknownID		 super)
{
       ...
}

The particle interface initializes and returns a particle object, which is then used to access particle data. This first method is passed an evaluation object which selects the channels it wants and returns a key index.

LxResult
CGasketInstance::prti_Prepare (
       ILxUnknownID		 evalObj,
       unsigned		*index)
{
       ...
}

Once prepared, the second method is called to actually create the object and initialize its state from the channel values.

LxResult
CGasketInstance::prti_Evaluate (
       ILxUnknownID		 attr,
       unsigned		 index,
       void		       **ppvObj)
{
       ...
}

       LxResult
CGasketInstance::tsrc_PreviewUpdate (
       int			 chanIndex,
       int			*update)
{ 
       ...
}

Gasket Particle Object-The particle object can be enumerated by a client to get the data for all particles. It also can preset an attributes interface to allow the client to set hints, in this case the random seed.

CGasketGenerator::CGasketGenerator ()
{
       ...
}

Like tableau surfaces, particle sources have features. These are the properties of each particle as a vector of floats. We provide the standard 3 particle features: position, transform, and ID.

unsigned int
CGasketGenerator::tsrf_FeatureCount (
       LXtID4			 type)
{
       ...
}

       LxResult
CGasketGenerator::tsrf_FeatureByIndex (
       LXtID4			 type,
       unsigned int		 index,
       const char	       **name)
{
       ...
}

Given a tableau vertex allocated by the client, we need to determine the features they want and their offsets into the vertex vector.

LxResult
CGasketGenerator::tsrf_SetVertex (
       ILxUnknownID		 vdesc)
{
       ...
}

Sampling walks the particles. This will be done by traversing the fractal geometry recursively.

LxResult
CGasketGenerator::tsrf_Sample (
       const LXtTableauBox	 bbox,
       float			 scale,
       ILxUnknownID		 trisoup)
{
       ...
}

Square gasket root: makes a flat square.

       void
CGasketGenerator::Square9 ()
{
       ...
}

Square gasket recursion will split the square into 9 sub-squares. The hole mode is an 8-fold multiplier that leaves out the middle, and the flake mode is a 5-fold multiplier that leaves out the corners.

       void
CGasketGenerator::Square9Gen (
       const CCorner		&cc,
       const CCorner		&c0,
       const CCorner		&c1,
       const CCorner		&c2,
       const CCorner		&c3,
       float			 level)
{
       ...
}

Serpinski starts with an equilateral triangle. Each level splits the edges evenly and recurses on the three corner triangles.

void
CGasketGenerator::Serpinski ()
{
       ...
}

       void
CGasketGenerator::SerpinskiGen (
       const CCorner		&cc,
       const CCorner		&c0,
       const CCorner		&c1,
       const CCorner		&c2,
       float			 level)
{
       ...
}

Tetrahedron is four points evenly spaced. Each edge is split and the four corners become half-sized tetrahedrons.

       void
CGasketGenerator::Tetrahedron ()
{
// corners of a tetrahedron
#if 0
   ( 0, Sqr(3) - 1/Sqr(3),            0)
   (-1,        - 1/Sqr(3),            0)
   ( 1,        - 1/Sqr(3),            0)
   ( 0,                 0, 2 * Sqr(2/3))
#endif
       CCorner		 cc, c0, c1, c2, c3;
       float		 s3;

       s3  = sqrt (3.0f);

       cc.Set ( 0.0, 0.0,  0.0);
       c0.Set ( 0.0, s3 - 1 / s3, 0.0);
       c1.Set (-1.0,    - 1 / s3, 0.0);
       c2.Set ( 1.0,    - 1 / s3, 0.0);
       c3.Set ( 0.0,         0.0, 2 * sqrt (2 / 3.0f));

       if (0) {	// world space
               cc.Xfrm (w_matrix);
               c0.Xfrm (w_matrix);
               c1.Xfrm (w_matrix);
               c2.Xfrm (w_matrix);
               c3.Xfrm (w_matrix);
       }

       TetrahedronGen (cc, c0, c1, c2, c3, f_iter);
}

       void
CGasketGenerator::TetrahedronGen (
       const CCorner		&cc,
       const CCorner		&c0,
       const CCorner		&c1,
       const CCorner		&c2,
       const CCorner		&c3,
       float			 level)
{
       ...
}

To send a particle to the client we set the position and ID, if requested (the transform is fixed in this example). Then we generate a single point. This will interpolate between the parent center position and the real center position for intermediate levels.

void
CGasketGenerator::Point (
       const CCorner		&parent,
       const CCorner		&center,
       float			 level)
{
       ...
}