Mesh Operation - Manual implementation

__TOC__

Mesh Operations can be integrated directly into the procedural modelling system, allowing them to be evaluated as part of the procedural stack. A modifier should spawn the ILxMeshOperation server and store it in the MeshOpObj channel on the Mesh Operation Item. This channel is then evaluated by the procedural modelling system, and it’s functions are called to perform the mesh edit.

Whilst we provide a method for Mesh Operation - Automatic implementation a mesh operation item type and modifier, in some cases, a mesh operation has to be implemented manually, for example, if the mesh operation depends on other items in the scene or time for it’s evaluation, there’s no way this can be provided by the automatically generated mesh operation item. In these cases, the auto conversion cannot be used to convert the ILxMeshOperation interface into a Mesh Operation item. Instead, a Package and Modifier need to be created that can provide the mesh operation to the procedural system.

The package/item should be of the type LXsITYPE_MESHOP, and the modifier should read the inputs it requires, and output the ILxMeshOperation COM object to the LXsICHAN_MESHOP_OBJ channel. The LXsMESHOP_PMODEL server tag should not be present on the ILxMeshOperation spawned by this modifier.

Sample Code

The following sample code implements a very basic mesh operation, item type and modifier that creates a grid at the centre of the world. The size of the grid is determined by scene time.

C++

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
/*
 *      The Package and PackageInstance define the item that is added to the
 *      procedural mesh operation stack.
 */

class Instance : public CLxImpl_PackageInstance
{
        public:
                static void initialize ()
                {
                        CLxGenericPolymorph     *srv = NULL;

                        srv = new CLxPolymorph                                          <Instance>;
                        srv->AddInterface               (new CLxIfc_PackageInstance     <Instance>);

                        lx::AddSpawner ("prim.plane.inst", srv);
                }
};

class Package : public CLxImpl_Package
{
        public:
                static void initialize ()
                {
                        CLxGenericPolymorph     *srv = NULL;

                        srv = new CLxPolymorph                                          <Package>;
                        srv->AddInterface               (new CLxIfc_Package             <Package>);
                        srv->AddInterface               (new CLxIfc_StaticDesc          <Package>);

                        lx::AddServer ("prim.plane", srv);
                }

                Package () : _inst_spawn ("prim.plane.inst") {}

                        LxResult
                pkg_TestInterface (
                        const LXtGUID            guid)                  LXx_OVERRIDE
                {
                        _inst_spawn.TestInterfaceRC (guid);
                }

                        LxResult
                pkg_Attach (
                        void                    **ppvObj)               LXx_OVERRIDE
                {
                        _inst_spawn.Alloc (ppvObj);

                        return ppvObj[0] ? LXe_OK : LXe_FAILED;
                }

                static LXtTagInfoDesc    descInfo[];

        private:
                CLxSpawner <Instance>    _inst_spawn;
};

LXtTagInfoDesc Package::descInfo[] =
{
        { LXsPKG_SUPERTYPE,     LXsITYPE_MESHOP },
        { 0 }
};

/*
 *      The Mesh Operation is spawned by the modifier and generates a plane in
 *      the procedural mesh.
 */

class MeshOp : public CLxImpl_MeshOperation
{
        public:
                static void initialize ()
                {
                        CLxGenericPolymorph     *srv = NULL;

                        srv = new CLxPolymorph                                          <MeshOp>;
                        srv->AddInterface               (new CLxIfc_MeshOperation       <MeshOp>);

                        lx::AddSpawner ("prim.plane.op", srv);
                }

                        LxResult
                mop_Evaluate (
                        ILxUnknownID             mesh_obj,
                        LXtID4                   type,
                        LXtMarkMode              mode)                  LXx_OVERRIDE
                {
                        CLxUser_Mesh             mesh (mesh_obj);
                        CLxUser_Polygon          polygon;
                        CLxUser_Point            point;
                        LXtPolygonID             poly_id = NULL;
                        LXtPointID               point_id[4];
                        LxResult                 result = LXe_FAILED;
                        LXtVector                pos;
                        double                   size = 0.5;
                        static double            positions[4][3] = {{-0.5, 0.0, -0.5},
                                                                    { 0.5, 0.0, -0.5},
                                                                    { 0.5, 0.0,  0.5},
                                                                    {-0.5, 0.0,  0.5}};

                        size = _size * 2.0;

                        if (mesh.test ())
                        {
                                polygon.fromMesh (mesh);
                                point.fromMesh (mesh);

                                if (polygon.test () && point.test ())
                                {
                                        for (unsigned i = 0; i < 4; i++)
                                        {
                                                LXx_VSCL3 (pos, positions[i], size);
                                                point.New (pos, &point_id[i]);
                                        }

                                        polygon.New (LXiPTYP_FACE, point_id, 4, 0, &poly_id);
                                        mesh.SetMeshEdits (LXf_MESHEDIT_GEOMETRY);

                                        result = LXe_OK;
                                }
                        }

                        return result;
                }

                double                   _size;
};

/*
 *      The Modifier is associated with the all items of our type, and it reads
 *      time as an input, spawns the mesh operation and writes it to the mesh
 *      operation object channel.
 */

class ModifierElement : public CLxItemModifierElement
{
        public:
                ModifierElement (
                        CLxUser_Evaluation      &eval,
                        ILxUnknownID             item_obj)
                {
                        CLxUser_Item             item (item_obj);

                        _time_index = -1;
                        _output_index = -1;

                        if (item.test ())
                        {
                                /*
                                 *      Allocate time as an input and the mesh
                                 *      operation object as an output.
                                 */

                                _time_index = eval.AddTime ();
                                _output_index = eval.AddChan (item, LXsICHAN_MESHOP_OBJ, LXfECHAN_WRITE);
                        }
                }

                        void
                Eval (
                        CLxUser_Evaluation      &eval,
                        CLxUser_Attributes      &attr)                  LXx_OVERRIDE
                {
                        CLxUser_ValueReference   val_ref;
                        MeshOp                  *meshOp = NULL;
                        ILxUnknownID             meshOp_obj = NULL;
                        double                   time = 0.0;

                        CLxSpawner <MeshOp>      spawner ("prim.plane.op");

                        if (_output_index < 0)
                                return;

                        if (_time_index >= 0)
                                time = attr.Float (_time_index);

                        /*
                         *      Spawn the mesh operation and copy the time channel to
                         *      it for the size.
                         */

                        meshOp = spawner.Alloc (meshOp_obj);
                        if (meshOp)
                                meshOp->size = time;

                        /*
                         *      Write the mesh operation into the mesh operation item
                         *      output channel.
                         */

                        if (attr.ObjectRW (_output_index, val_ref))
                                val_ref.SetObject (meshOp_obj);

                        /*
                         *      Release the mesh operation COM object, as we spawned
                         *      the item directly.
                         */

                        lx::UnkRelease (meshOp_obj);
                }

        private:
                int                      _output_index, _time_index;
};

class ModifierServer : public CLxItemModifierServer
{
        public:
                static void initialize ()
                {
                        CLxExport_ItemModifierServer <ModifierServer>   ("prim.plane.mod");
                }

                        const char *
                ItemType ()                                             LXx_OVERRIDE
                {
                        /*
                         *      Associate the modifier with all items of our type.
                         */

                        return "prim.plane";
                }

                        CLxItemModifierElement *
                Alloc (
                        CLxUser_Evaluation      &eval,
                        ILxUnknownID             item_obj)              LXx_OVERRIDE
                {
                        return new ModifierElement (eval, item_obj);
                }
};

void initialize ()
{
        Package::initialize ();
        Instance::initialize ();
        MeshOp::initialize ();
        ModifierServer::initialize ();
}

Python

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python

import lx
import lxifc
import lxu.attributes
import lxu.package
import lxu.vector

'''
    The Package and PackageInstance define the item that is added to the
    procedural mesh operation stack. As there are no channels on this item,
    we can just use the BasicPackage class, which comes with it's own
    PackageInstance implementation.
'''

class Package (lxu.package.BasicPackage):
    pass

'''
    The Mesh Operation is spawned by the modifier and generates a plane in
    the procedural mesh.
'''

class MeshOperation (lxifc.MeshOperation):

    def __init__ (self):

        self._size = 0.5

    def mop_Evaluate (self, mesh_obj, type, mode):

        mesh = lx.object.Mesh (mesh_obj)
        positions = ((-0.5, 0.0, -0.5),
                     ( 0.5, 0.0, -0.5),
                     ( 0.5, 0.0,  0.5),
                     (-0.5, 0.0,  0.5))

        size = self._size
        if size < 0.0:
            size = 0.0;
        size = size * 2.0

        polygon = lx.object.Polygon (mesh.PolygonAccessor ())
        point = lx.object.Point (mesh.PointAccessor ())

        point_ids = []
        for pos in positions:
            point_ids.append (point.New (lxu.vector.scale (pos, size)))

        points_storage = lx.object.storage ()
        points_storage.setType ('p')
        points_storage.setSize (4)
        points_storage.set (point_ids)

        polygon.New (lx.symbol.iPTYP_FACE, points_storage, 4, 1)

        mesh.SetMeshEdits (lx.symbol.f_MESHEDIT_GEOMETRY)

'''
    The Modifier is associated with the all items of our type, and it reads
    time as an input, spawns the mesh operation and writes it to the mesh
    operation object channel.
'''

class ModifierElement (lxifc.Modifier):

    def __init__ (self, item, eval):

        self._attr = lx.object.Attributes (eval)
        self._eval = lx.object.Evaluation (eval)

        '''
            Allocate time as an input and the mesh operation object as an
            output.
        '''

        self._time_index = self._eval.ReadTime ()
        self._output_index = self._eval.AddChannelName (item, lx.symbol.sICHAN_MESHOP_OBJ, lx.symbol.fECHAN_WRITE)

    def mod_Evaluate (self):

        '''
            Spawn the mesh operation and copy the time channel to it for
            the size.
        '''

        meshOp = MeshOperation ()
        meshOp._size = self._attr.GetFlt (self._time_index)

        '''
            Write the mesh operation into the mesh operation item output
            channel.
        '''

        val_ref = lx.object.ValueReference (self._attr.Value (self._output_index, 1))
        val_ref.SetObject (meshOp)

class ModifierServer (lxifc.EvalModifier):

    def __init__ (self):

        self._itemType = lx.service.Scene ().ItemTypeLookup ("prim.plane")

    def eval_Reset (self, scene):

        self._scene = lx.object.Scene (scene)
        self._index = 0
        self._count = self._scene.ItemCount (self._itemType)

    def eval_Next (self):

        '''
            This function is called repeatedly. We return the item and
            key (0) for every item that we want to assign a modifier for.
            When there are no items left, the function returns 0 to stop
            enumeration.
        '''

        if self._index >= self._count:
            return (0, 0)

        item = self._scene.ItemByIndex (self._itemType, self._index)
        self._index += 1

        return (item, 0)

    def eval_Alloc (self, item, index, eval):

        '''
            Allocate a modifier for this item. It's constructor will
            handle any setup and channel allocation.
        '''

        return ModifierElement (item, eval)

'''
    Bless the eval modifier and package to register them as servers.
    The mesh operation and modifier element will be spawned on demand.
'''

tags = {lx.symbol.sMOD_TYPELIST: "prim.plane"}
lx.bless (ModifierServer, "prim.plane.mod", tags)

tags = {lx.symbol.sPKG_SUPERTYPE: lx.symbol.sITYPE_MESHOP}
lx.bless (Package, "prim.plane", tags)