Difference between revisions of "Item gasket"
Line 11: | Line 11: | ||
===Class Declarations=== | ===Class Declarations=== | ||
− | This class generates the gasket, so we inherit from [[Tableau_(lx-tableau.hpp)#.2816.29_User_Class:_TableauSurface_method|CLxTableauSurface]]. The TableauSurface Method is responsible for generating all the triangles that intersect the bounding box. Additionally, we inherit from [[Attributes_(lxu-attributes.hpp)#CLxDynamicAttributes|CLxDynamicAttributes]], which provides the attributes interface for our gasket. | + | This class generates the gasket, so we inherit from [[Tableau_(lx-tableau.hpp)#.2816.29_User_Class:_TableauSurface_method|CLxTableauSurface]]. The TableauSurface Method is responsible for generating all the triangles that intersect the bounding box. |
+ | |||
+ | Additionally, we inherit from [[Attributes_(lxu-attributes.hpp)#CLxDynamicAttributes|CLxDynamicAttributes]], which provides the attributes interface for our gasket. | ||
class CGasketGenerator : | class CGasketGenerator : |
Revision as of 17:21, 12 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.
The gasket item.
Contents
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 ¢er, 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 () { ... } 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 ¢er, float level) { ... }