/* 
 CutrVelvet.cpp
 RfM22.0
*/
#include <RixPattern.h> 
#include <RixShadingUtils.h>
#include <RixPredefinedStrings.hpp>
  
class CutrVelvet : public RixPattern {
public:
    CutrVelvet();
    virtual ~CutrVelvet() { }
    virtual int Init(RixContext &ctx, RtUString pluginpath);
    virtual RixSCParamInfo const *GetParamTable();
    virtual void Synchronize(RixContext &ctx, RixSCSyncMsg sync_msg,
                            RixParameterList const *plist) { }
    virtual int CreateInstanceData(RixContext &ctx, RtUString const str,
                            RixParameterList const *plist, InstanceData *data)
            {
            return -1;
            }
    virtual void Finalize(RixContext &) { }
    virtual int ComputeOutputParams(RixShadingContext const *ctx,
                                    RtInt *noutputs, 
                                    OutputSpec **outputs,
                                    RtPointer instanceData,
                                    RixSCParamInfo const *ignored);
    virtual bool Bake2dOutput(RixBakeContext const *ctx, Bake2dSpec &spec, RtPointer ptr) { return false; }
    virtual bool Bake3dOutput(RixBakeContext const *ctx, Bake3dSpec &spec, RtPointer ptr) { return false; }
    private:
        RixMessages *m_msg;
        RixShadeFunctions *m_shd;  // Shading functions in RixInterfaces.h
        RtFloat        m_edge_width;
        RtColorRGB     m_edge_color;
        RtFloat        m_edge_brightness;
        RtColorRGB     m_base_color;
        RtFloat        m_base_brightness;
    };
  
CutrVelvet::CutrVelvet():
    m_msg(NULL),
    m_shd(NULL),
    m_edge_width(0),
    m_edge_color(1,1,1),
    m_edge_brightness(1),
    m_base_color(1,1,1),
    m_base_brightness(1)
    { }
  
int CutrVelvet::Init(RixContext &ctx, RtUString const pluginpath) {
    m_msg = (RixMessages*)ctx.GetRixInterface(k_RixMessages);
    m_shd = (RixShadeFunctions*)ctx.GetRixInterface(k_RixShadeFunctions);
  
    // Uncomment the next three lines if a rib Option will be queried.
    //RixRenderState *rstate = (RixRenderState*)ctx.GetRixInterface(k_RixRenderState);
    //RixRenderState::Type optType;
    //RtInt optNumValues, err;
    // Example of using messaging,
    //    m_msg->Info("%f\n", a_float_value);
    return (!m_msg) ? 1 : 0;
    }
  
RixSCParamInfo const *CutrVelvet::GetParamTable() {
    static RixSCParamInfo s_ptable[] = {
        // Output
        RixSCParamInfo(RtUString("resultRGB"), k_RixSCColor, k_RixSCOutput),
        // Inputs
        RixSCParamInfo(RtUString("edge_width"), k_RixSCFloat),
        RixSCParamInfo(RtUString("edge_color"), k_RixSCColor),
        RixSCParamInfo(RtUString("edge_brightness"), k_RixSCFloat),
        RixSCParamInfo(RtUString("base_color"), k_RixSCColor),
        RixSCParamInfo(RtUString("base_brightness"), k_RixSCFloat),
        RixSCParamInfo() // end of table
        };
    return &s_ptable[0];
    }
  
enum paramIndex {
    k_resultRGB = 0,
    k_edge_width,
    k_edge_color,
    k_edge_brightness,
    k_base_color,
    k_base_brightness
    };
    
int CutrVelvet::ComputeOutputParams(RixShadingContext const *ctx,
                                RtInt *noutputs, 
                                OutputSpec **outputs,
                                RtPointer instanceData,
                                RixSCParamInfo const *ignored) {
    // OUTPUTS BEGIN____________________________________
    // Allocate memory for the OutputSpec data structure.
    RixShadingContext::Allocator pool(ctx);
    OutputSpec *outSpec = pool.AllocForPattern<OutputSpec>(1);
    *outputs = outSpec;
  
    // Allocate memory for each output.
    RtColorRGB    *resultRGB = pool.AllocForPattern<RtColorRGB>(ctx->numPts);
  
    // Connect the output(s) to the OutputSpec.
    *noutputs = 1;
    outSpec[0].paramId = k_resultRGB;
    outSpec[0].detail = k_RixSCVarying;
    outSpec[0].value = resultRGB;
  
    // INPUTS BEGIN____________________________________
    bool varying = true;
    // Declare a pointer for each input then obtain their valuesusing EvalParam().
    RtFloat        const *edge_width;
    RtColorRGB    const *edge_color;
    RtFloat        const *edge_brightness;
    RtColorRGB    const *base_color;
    RtFloat        const *base_brightness;
    ctx->EvalParam(k_edge_width, -1, &edge_width, &m_edge_width, varying);
    ctx->EvalParam(k_edge_color, -1, &edge_color, &m_edge_color, varying);
    ctx->EvalParam(k_edge_brightness, -1, &edge_brightness, &m_edge_brightness, varying);
    ctx->EvalParam(k_base_color, -1, &base_color, &m_base_color, varying);
    ctx->EvalParam(k_base_brightness, -1, &base_brightness, &m_base_brightness, varying);
  
    // Assign an array of normalized shading normals to Nn.
    RtNormal3 const  *Nn;
    ctx->GetBuiltinVar(RixShadingContext::k_Nn, &Nn);    
  
    // Assign an array of normalized view vectors to Vn.
    RtVector3 const  *Vn;
    ctx->GetBuiltinVar(RixShadingContext::k_Vn, &Vn);
        
    // Assign values to the outputs.
    RtFloat dot;
    for(int i = 0; i < ctx->numPts; i++) {
        dot = 1.0 - Vn[i].Dot(Nn[i]);
        dot = RixSmoothStep(1.0 - edge_width[i], 1.0, dot);
        resultRGB[i] = RixMix(base_color[i] * base_brightness[i], edge_color[i] * edge_brightness[i], dot);
        }
    return 0;
    }
RIX_PATTERNCREATE {
    return new CutrVelvet();
    }
RIX_PATTERNDESTROY {
    delete((CutrVelvet*)pattern);
    }