/*
Wireframe_Rif.cpp
Refer to,
    http://fundza.com/devkit/rifplugins/mesh2wireframe/index.html
for information about this rif.
  
Malcolm Kesson
Sept 8 2016
  
Sept 16 2016: Corrected a memory copy error
Nov  9 2017:  Added support for subdiv surfaces, but only as a "cage".
  
In the "Pre Shape MEL" text field enter one of the following MEL commands,
    RiAttribute("user", "int wireframe", 0);  // mesh only
    RiAttribute("user", "int wireframe", 1);  // wireframe only
    RiAttribute("user", "int wireframe", 2);  // mesh and wireframe
*/
#include <RifPlugin.h>
#include <RixInterfaces.h>
#include <RifFilter.h>
#include <ri.h>
#include <rx.h>
#include <string>
#include <sstream>
#include <stdlib.h> 
  
#define MESH 0
#define WIRE 1
#define WIRE_AND_MESH 2
#define DEFAULT_MODE MESH
  
class Wireframe_Rif : public RifPlugin {
    public:
            Wireframe_Rif(RtFloat width);
    virtual ~Wireframe_Rif() { }
    virtual RifFilter &GetFilter();
    
    private:
        RixMessages       *m_msg;
        RixRenderState *m_rstate;
        RifFilter       m_filters;
        RtFloat           m_width;
  
        // Callbacks_____________________________________________________________
        static RtVoid   pointsGeneralPolygonsV(RtInt npolys, RtInt *nloops, RtInt *nverts, 
                                RtInt* verts, RtInt n, RtToken nms[], RtPointer vals[]);
        static RtVoid   hierarchicalSubdivisionMeshV(RtToken mask, RtInt nf,
                                RtInt nverts[], RtInt verts[], RtInt nt, RtToken tags[],
                                RtInt nargs[], RtInt intargs[], RtFloat floatargs[],
                                RtToken stringargs[], RtInt, RtToken[], RtPointer[]);                
        // Utilities_____________________________________________________________
        virtual RtPoint *getPtrToVertexData(RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]);
        virtual int         getRenderMode(std::string name);
        virtual RtVoid  addRoundCurveAttr();
        virtual RtVoid  convertPolygonsToCurves(RtInt npolys, RtInt nloops[], RtInt vertsPerLoop[], RtInt vertsIndices[],
                                                RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]);
        virtual RtVoid  convertSubdivToCurves(RtInt nfaces, RtInt vertsPerLoop[], RtInt vertsIndices[], 
                                                RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]);
    };
  
//-------------------------------------------------------------
// Entry point called by PRMan.
RifPlugin *RifPluginManufacture(int argc, char **argv) {
    RtFloat defaultwidth = 0.025;
    if(argc == 1 && *argv[0] != '\0')
        defaultwidth = atof(argv[0]);
    return new Wireframe_Rif(defaultwidth);
    }
//-------------------------------------------------------------
// Constructor
Wireframe_Rif::Wireframe_Rif(RtFloat scale) {
    m_width = scale;
    m_filters.PointsGeneralPolygonsV = pointsGeneralPolygonsV;
    m_filters.HierarchicalSubdivisionMeshV = hierarchicalSubdivisionMeshV;
    m_filters.Filtering = RifFilter::k_Continue;
    
    RixContext  *rixContext = RxGetRixContext();
    m_msg = (RixMessages*) rixContext->GetRixInterface(k_RixMessages);
    m_rstate = (RixRenderState*) rixContext->GetRixInterface(k_RixRenderState);
    }
//-------------------------------------------------------------
RifFilter& Wireframe_Rif::GetFilter() { 
    return m_filters; 
    }
//-------------------------------------------------------------
// Callback:
RtVoid Wireframe_Rif::pointsGeneralPolygonsV(RtInt npolys, RtInt nloops[], RtInt vertsPerLoop[], RtInt vertsIndices[], 
                                  RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
    Wireframe_Rif *self = static_cast<Wireframe_Rif*> (RifGetCurrentPlugin());
    int mode = self->getRenderMode("user:wireframe");
    /*
    std::stringstream   msg;
    msg << " mode = ";
    msg << mode;
    self->m_msg->InfoAlways(msg.str().c_str());
    */
    if(mode == 0 || mode == 2)
        RiPointsGeneralPolygonsV(npolys, nloops, vertsPerLoop, vertsIndices, paramNum, paramNames, paramVals);
    if(mode == 1 || mode == 2)
        self->convertPolygonsToCurves(npolys, nloops, vertsPerLoop, vertsIndices, paramNum, paramNames, paramVals);
    }
//-------------------------------------------------------------
// Callback:
RtVoid Wireframe_Rif::hierarchicalSubdivisionMeshV(RtToken mask, 
                    RtInt nfaces, RtInt vertsPerLoop[], RtInt vertsIndices[], 
                    RtInt nt, RtToken tags[], RtInt nargs[], RtInt intargs[], RtFloat floatargs[],
                    RtToken stringargs[], 
                    RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
    Wireframe_Rif *self = static_cast<Wireframe_Rif*> (RifGetCurrentPlugin());
    int mode = self->getRenderMode("user:wireframe");
  
    if(mode == 0 || mode == 2) 
        RiHierarchicalSubdivisionMeshV( mask, nfaces,  vertsPerLoop, vertsIndices, 
                     nt,  tags,  nargs,  intargs, floatargs, stringargs, 
                     paramNum,  paramNames,  paramVals);
    else if(mode == 1)
        self->convertSubdivToCurves(nfaces, vertsPerLoop, vertsIndices, paramNum, paramNames, paramVals);
    }
  
//_______________________________________________________________________
// Utility:
// Almost identical to convertSubdivToCurves().
RtVoid Wireframe_Rif::convertPolygonsToCurves(RtInt npolys, RtInt nloops[], RtInt vertsPerLoop[], RtInt vertsIndices[], 
                                  RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
    Wireframe_Rif *self = static_cast<Wireframe_Rif*> (RifGetCurrentPlugin());
    
    // Step 1: Count the number of source polygon "P" vertices.
    int numVerts;
    int numLoops;
    int vertCount = 0;
    int loopCounter = 0;
    for(int n = 0; n < npolys; n++) {
        numLoops = nloops[n];
        for(int j = 0; j < numLoops; j++) {
            numVerts = vertsPerLoop[loopCounter++];
            vertCount += numVerts;
            }
        }
    // Step 2: Get a pointer to the source "P" vertex data.
    RtPoint *polyVertexPtr = getPtrToVertexData(paramNum, paramNames, paramVals);
    RtPoint *polyVertexCopy = (RtPoint*) calloc(1, sizeof(RtPoint) * vertCount);
    // Step 3: Copy the data.
    memcpy(polyVertexCopy, polyVertexPtr, sizeof(RtPoint) * vertCount);
    
    // Step 4: Assign "P" vertex data so that each curve defines the perimeter of each face.
    int     vertIndex;
    int     vertCounter = 0;
    RtFloat *fPtr;
    loopCounter = 0;
    for(int n = 0; n < npolys; n++) {
        for(int j = 0; j < nloops[n]; j++) {
            numVerts = vertsPerLoop[loopCounter++]; 
            for(int k = 0; k < numVerts; k++) {
                vertIndex = vertsIndices[vertCounter];
                fPtr = (RtFloat*) &polyVertexCopy[vertIndex];
                polyVertexPtr[vertCounter][0] = *fPtr++;
                polyVertexPtr[vertCounter][1] = *fPtr++;
                polyVertexPtr[vertCounter][2] = *fPtr++;
                vertCounter++;
                }
            }
        }
    // Step 5: Output the RiCurves and free the temporaty copy of the original "P" data.
    int         numCurves = loopCounter;
    RtToken     pNames[2] = { RI_P, RI_CONSTANTWIDTH };
    RtFloat     width = self->m_width;
    RtPointer     outVals[2] = { polyVertexPtr, &width };
    self->addRoundCurveAttr();
    RiCurvesV(RI_LINEAR, numCurves, vertsPerLoop, RI_PERIODIC, 2, pNames, outVals);
    free(polyVertexCopy);
    }
    
//_______________________________________________________________________
// Utility:
// Almost identical to convertPolygonsToCurves().
RtVoid Wireframe_Rif::convertSubdivToCurves(RtInt nfaces, RtInt vertsPerLoop[], RtInt vertsIndices[], 
                                  RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
    Wireframe_Rif *self = static_cast<Wireframe_Rif*> (RifGetCurrentPlugin());
    
    // Step 1: Count the number of source subdiv "P" vertices.
    int numVerts;
    int vertCount = 0;
    int loopCounter = 0;
    for(int n = 0; n < nfaces; n++) {
        numVerts = vertsPerLoop[loopCounter++];
        vertCount += numVerts;
        }
    // Step 2: Get a pointer to the source "P" vertex data.
    RtPoint *subdivVertexPtr = getPtrToVertexData(paramNum, paramNames, paramVals);
    RtPoint *subdivVertexCopy = (RtPoint*) calloc(1, sizeof(RtPoint) * vertCount);
    // Step 3: Copy the data.
    memcpy(subdivVertexCopy, subdivVertexPtr, sizeof(RtPoint) * vertCount);
  
    // Step 4: Assign "P" vertex data so that each curve defines the perimeter of each face.
    int     vertIndex;
    int     vertCounter = 0;
    RtFloat *fPtr;
    loopCounter = 0;
  
    for(int n = 0; n < nfaces; n++) {
         numVerts = vertsPerLoop[loopCounter++]; 
        for(int k = 0; k < numVerts; k++) {
            vertIndex = vertsIndices[vertCounter];
            fPtr = (RtFloat*) &subdivVertexCopy[vertIndex];
            subdivVertexPtr[vertCounter][0] = *fPtr++;
            subdivVertexPtr[vertCounter][1] = *fPtr++;
            subdivVertexPtr[vertCounter][2] = *fPtr++;
            vertCounter++;
            }
        }
    // Step 5: Output the RiCurves and free the temporaty copy of the original "P" data.
    int         numCurves = loopCounter;
    RtToken     pNames[2] = { RI_P, RI_CONSTANTWIDTH };
    RtFloat     width = self->m_width;
    RtPointer     outVals[2] = { subdivVertexPtr, &width };
    self->addRoundCurveAttr();
    RiCurvesV(RI_LINEAR, numCurves, vertsPerLoop, RI_PERIODIC, 2, pNames, outVals);
    free(subdivVertexCopy);
    }
//_______________________________________________________________________
// Utility:
// Returns a pointer to the first point of the "P" array.
RtPoint* Wireframe_Rif::getPtrToVertexData(RtInt paramNum, RtToken paramNames[], RtPointer paramVals[]) {
    for(int n = 0; n < paramNum; n++) {
        if(paramNames[n] == RI_P || strcmp(paramNames[n], RI_P) == 0)
            return (RtPoint*)paramVals[n];
        }
    return NULL;
    }
//_______________________________________________________________________
// Utility:
// Returns the int value of the "user:wireframe" attribute.
// 0 - use original polymesh
// 1 - use curves for wireframe
// 2 - use both polymesh and curves.
int Wireframe_Rif::getRenderMode(std::string name) {
    int    result = -1;
    RtInt     resultCount, resultError, resultLen = sizeof(int*);
    RixRenderState::Type resultType;
    resultError = m_rstate->GetAttribute(name.c_str(), &result, resultLen,
                        &resultType, &resultCount);
    if(resultError == 0)
        return result;
    return DEFAULT_MODE;
    }
//_______________________________________________________________________
// Utility:
RtVoid Wireframe_Rif::addRoundCurveAttr( ) {
    RixContext      *rix = RxGetRixContext();
    RixTokenStorage *tok_store = static_cast<RixTokenStorage*>(rix->GetRixInterface(k_RixGlobalTokenData));
    RtToken   tok = (char*)tok_store->GetToken("int roundcurve");
    RtToken   tokens[] = { tok };
    int       value = 1;
    RtPointer values[] = { &value };
    RiAttributeV(RI_DICE, 1, tokens, values);
    }