CustomMaterial: Server basics

A CustomMaterial is plug-in server that implements a material that adds new material attributes and can contribute to shading.

Overview

Materials are used when you need to extend modo’s advanced materials with custom attributes. These attributes can then be textured and finally converted into shading components at the end of the shading pipe. A custom material is always evaluated in 2 stages: first as a material and second as a shader to produce the shading components. Headers =======

shade (lx-shade.hpp) * CLxImpl_CustomMaterial action (lx-action.hpp) * CLxUser_Evaluation package (lx-package.hpp) * CLxUser_AddChannel value (lx-value.hpp) * CLxUser_Attributes vector (lx-vector.hpp) * CLxUser_PacketService

The plug-in server class derives from CLxImpl_CustomMaterial, and exports the ILxCustomMaterial interface. For speed purposes the class will also want to keep the packet service ready to hand.

CLxUser_PacketService pkt_service;

CustomMaterial::SetupChannels()

Like a package, a value texture server must define its channels. The SetupChannels() method is identical to the one used by Package: Server basics.

1
2
3
4
5
         LxResult
 cmt_SetupChannels (
         ILxUnknownID            addChan)
 {
         CLxUser_AddChannel      ac (addChan);
1
2
3
4
         ac.NewChannel ("myInput", "percent");
         ac.SetDefault (1.0, 1);
         return LXe_OK;
 }

CustomMaterial::LinkChannels()

The value texture reads its channel values in a modifier context. That means that it has to declare the channels it wants to read using the Evaluation Interface. This is also a good place to cache the offsets for any vector packets that it wants to read.

1
2
3
4
5
6
         LxResult
 cmt_LinkChannels (
         ILxUnknownID            eval,
         ILxUnknownID            item)
 {
         CLxUser_Evaluation      ev (eval);
1
2
3
4
         idx_value = ev.AddChan (item, "myInput");
         tin_offset = pkt_service.GetOffset (LXsCATEGORY_SAMPLE, LXsP_TEXTURE_INPUT);
         return LXe_OK;
 }

CustomMaterial::ReadChannels()

Linking channels occurs once as items are added or their relationships are changed. Reading channels then happens any time channel values change. For example changing time may cause channels to be read again if they are animated. Values are read from an Attributes Interface using the index cached when linking channels. The values are then stored in an allocated data structure which is returned from the method.

1
2
3
4
5
6
7
         LxResult
 cmt_ReadChannels (
         ILxUnknownID            attr,
         void                  **ppvData)
 {
         CLxUser_Attributes      at (attr);
         RendData               *rd = new RendData;
1
2
3
4
         rd->value  = at.Float (idx_value);
         ppvData[0] = rd;
         return LXe_OK;
 }

CustomMaterial::MaterialEvaluate()

Material evaluation is the first step of the evaluation, it is intended to set the material attributes into of the shading packets. This may happen thousands or even millions of times per frame, so it’s intended to be as fast as possible. The private data struct containing channel values is passed as data. In general we do this to extend the material attributes, it is therefore necessary to define a custom packet using the VectorPacket Interface]] and its effects using the [[PacketEffect Interface. In the following code snippet our custom packet is LXpMyPacket.

Note also that this is a void function. Anything that can fail, such as allocations, should already have been done as part of reading channels. The evaluation is assumed to succeed and there is no allowance for failure.

1
2
3
4
5
6
7
         void
 cmt_MaterialEvaluate (
         ILxUnknownID            vector,
          void                  *data)
 {
          LXpMyPacket           *pack = (LXpMyPacket*) pkt_service.FastPacket (vector, pkt_offset);
         RendData               *rd = (RendData *) data;
1
2
3
4
         pack->color[0] = 1.0;
         pack->color[1] = 0.0;
         pack->color[2] = 0.0;
 }

CustomMaterial::ShaderEvaluate()

Shader evaluation is the second and final step of the evaluation, it is intended to read the material attributes from a sample vector packet and produce shading components. In this silly example we just replace the diffuse shading component with our custom color (red in this case)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
        void
 cmt_ShaderEvaluate (
        ILxUnknownID            vector,
        ILxUnknownID            rayObj,
        LXpShadeComponents     *sCmp,
        LXpShadeOutput         *sOut,
        void                    *data)
 {
         LXpMyPacket            *pack = (LXpMyPacket*) pkt_service.FastPacket (vector, pkt_offset);
         RendData               *rd = (RendData * ) data;
1
2
3
4
5
6
        raycast.set (rayObj);
        for (i=0;i<3;i++) {
               sCmp->diff[i]  = pack->color[i];
               sOut->color[i] = sCmp->diff[i] + sCmp->spec[i] + sCmp->refl[i] + sCmp->tran[i] + sCmp->subs[i] + sCmp->lumi[i];
        }
  }